Commit inicial - WordPress Análisis de Precios Unitarios

- 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>
This commit is contained in:
root
2025-11-03 21:04:30 -06:00
commit a22573bf0b
24068 changed files with 4993111 additions and 0 deletions

View File

@@ -0,0 +1,10 @@
module.exports = Backbone.Collection.extend( {
/**
* helper function to get the last item of a collection
*
* @return Backbone.Model
*/
last: function () {
return this.at( this.size() - 1 );
}
} );

View File

@@ -0,0 +1,6 @@
var NotificationModel = require( './../models/Notification' );
module.exports = Backbone.Collection.extend({
model: NotificationModel,
});

View File

@@ -0,0 +1,46 @@
module.exports = Backbone.Model.extend({
idAttribute: 'id',
defaults: {
active: {},
dismissed: {},
},
getDate: function () {
if (this.has('start') && this.get('start')) {
let startDate = new Date(this.get('start'));
let currentDate = new Date(TD_Notification?.date_time_now);
let timeDiff = currentDate.getTime() - startDate.getTime();
// Convert the difference to months, days, hours, or minutes
let yearsDiff = Math.floor(timeDiff / (1000 * 3600 * 24 * 365));
if (yearsDiff > 0) {
return yearsDiff + (yearsDiff === 1 ? " year ago" : " years ago");
}
let monthsDiff = Math.floor(timeDiff / (1000 * 3600 * 24 * 30));
if (monthsDiff > 0) {
return monthsDiff + (monthsDiff === 1 ? " month ago" : " months ago");
}
let daysDiff = Math.floor(timeDiff / (1000 * 3600 * 24));
if (daysDiff > 0) {
return daysDiff + (daysDiff === 1 ? " day ago" : " days ago");
}
let hoursDiff = Math.floor(timeDiff / (1000 * 3600));
if (hoursDiff > 0) {
return hoursDiff + (hoursDiff === 1 ? " hour ago" : " hours ago");
}
let minsDiff = Math.floor(timeDiff / (1000 * 60));
if (minsDiff > 0) {
return minsDiff + (minsDiff === 1 ? " minute ago" : " minutes ago");
}
return "just now";
} else {
return "";
}
}
});

View File

@@ -0,0 +1,131 @@
TD_Notification = TD_Notification || {};
// Import necessary modules
const NotificationDrawer = require( './views/NotificationDrawer' );
const NotificationsList = require( './views/NotificationsList' );
const NotificationCollection = require( './collections/Notifications' );
const FooterView = require( './views/NotificationFooter' );
( function ( $ ) {
$(document).ready(function () {
// Initialize variables
let notificationType = 'Active';
let notificationData = [];
let notificationListView = null;
let footerView = null;
// Function to render the UI
const renderUI = function () {
// Filter data based on notification type
const filteredData = notificationType === 'Active' ? TD_Notification?.data?.active : TD_Notification?.data?.dismissed;
// Count dismissed and active notifications
const dismissedCount = TD_Notification?.data?.dismissed?.length;
const activeCount = TD_Notification?.data?.active?.length;
if ( activeCount <= 0 ) {
$('.notification-indicator').parent().remove();
}
// Create a new collection with filtered data
const collection = new NotificationCollection( filteredData );
// Render NotificationDrawer
const notificationDrawer = new NotificationDrawer( {
el: $( '.td-app-notification-counter' ),
collection: collection,
activeCount,
dismissedCount
} );
notificationDrawer.render();
// Render NotificationsList
notificationListView = new NotificationsList( {
el: $('.td-app-notification-holder'),
collection,
notificationType,
dismissedCount,
activeCount,
} );
notificationListView.render();
footerView = new FooterView({
el: $('.notification-footer'),
collection: collection,
notificationType: notificationType,
hideDismissAll: notificationType === 'Dismissed' || activeCount < 2,
});
footerView.render();
// Event handler for collection change
notificationListView.on( 'collectionChanged', function ( data ) {
notificationData = data.collection.toJSON();
renderUI(); // Re-render UI with updated data
} );
footerView.on( 'collectionChanged', function ( data ) {
notificationData = data.collection.toJSON();
renderUI(); // Re-render UI with updated data
} );
// Event handler for notification type change
notificationListView.on( 'notificationTypeChanged', function ( data ) {
notificationType = data?.notification_type ? data?.notification_type : 'Active';
renderUI(); // Re-render UI with updated notification type
} );
$('.tve-notification').last().css('border-bottom', 'none')
};
window.render_ui = renderUI;
// Check visibility every 100 milliseconds
const interval = setInterval(function () {
if ($('.td-app-notification-counter').is(':visible')) {
// Render the UI for the first time
renderUI();
clearInterval(interval);
}
}, 100);
// Initial check in case the element is already visible when the script runs
if ($('.td-app-notification-counter').is(':visible')) {
renderUI();
clearInterval(interval);
}
const checkEmptyOrNot = setInterval(function () {
if ($('.td-app-notification-wrapper').is(':empty')) {
renderUI();
} else {
clearInterval(checkEmptyOrNot);
}
}, 100);
// Function to close the notification drawer
const closeNotificationDrawer = function () {
$( '.td-app-notification-overlay' ).addClass( 'close' );
$( '.td-app-notification-drawer' ).removeClass( 'open' );
};
// Event listener for Esc key press
$(document).on('keydown', function (e) {
if (e.key === 'Escape') {
closeNotificationDrawer();
}
});
const getUrlParameter = function(name) {
name = name.replace(/[\[\]]/g, '\\$&'); // Escape special characters for regex
const regex = new RegExp('[?&]' + name + '(=([^&#]*)|&|#|$)');
const results = regex.exec(window.location.href);
if (!results) return null; // If no match is found
if (!results[2]) return ''; // If the parameter exists but no value is set
return decodeURIComponent(results[2].replace(/\+/g, ' '));
}
if ( getUrlParameter('notify') == 1 ) {
$( '.td-app-notification-overlay' ).removeClass( 'close' );
$( '.td-app-notification-drawer' ).addClass( 'open' );
}
});
} )( jQuery );

View File

@@ -0,0 +1,12 @@
/**
* Created by Russel Hussain on 4/29/2024.
*/
( function ( $ ) {
module.exports = Backbone.View.extend( {
render: function () {
return this;
}
} )
} )( jQuery );

View File

@@ -0,0 +1,47 @@
/**
* Created by Russel Hussain on 05/05/2024.
*/
( function ( $ ) {
const BaseView = require( './Base' );
module.exports = BaseView.extend( {
notificationType: 'Active',
translate: TD_Notification.t,
initialize: function ( options ) {
if (options && options.notificationType) {
this.notificationType = options.notificationType;
}
},
render: function () {
this.$el.html( this.getHtml() );
return this;
},
getHtml: function () {
let theme_class = TD_Notification.notify_class ? TD_Notification.notify_class : '';
let html = '<div class="no-notifications no-notifications-'+ theme_class +'">';
let notificationType = this.notificationType;
if ( notificationType === 'Active' ) {
html += '<img alt="Dannie the Detective" src="'+ TD_Notification?.image_url +'">' +
'<div class="great-scott great-scott-'+ theme_class +'">' + this.translate.no_new_title + '</div>' +
'<div class="no-new-notifications no-new-notification-'+ theme_class +'">' + this.translate.no_new + '</div>' +
'<span class="switch-notification-type toggle-to-dismissed switch-notification-type-'+ theme_class + '">' + this.translate.see_dismissed +'</span>';
}
if (notificationType === 'Dismissed') {
html += '<img alt="Dannie the Detective" src="'+ TD_Notification?.image_url +'">' +
'<div class="great-scott great-scott-'+ theme_class +'">' + this.translate.no_new_title + '</div>' +
'<div class="no-new-notifications no-new-notification-'+ theme_class +'">' + this.translate.no_dismissed + '</div>' +
'<span class="switch-notification-type toggle-to-active switch-notification-type-'+ theme_class +'">' + this.translate.see_new + '</span>';
}
html += '</div>';
return html;
}
} );
} )( jQuery );

View File

@@ -0,0 +1,111 @@
( function ( $ ) {
const BaseView = require( './Base' );
module.exports = BaseView.extend( {
render: function () {
this.$el.append( this.getHtml() );
return this;
},
getHtml: function () {
let theme_class = TD_Notification.notify_class ? TD_Notification.notify_class : '';
let html = '<div class="tve-notification notification-'+ theme_class +'">' +
'<div class="icon icon-'+ theme_class +'">' +
'<svg class="tve-circle-close type-' + this.model.get('type') + '" xmlns="http://www.w3.org/2000/svg">><use xlink:href="#icon-notification-type-' + this.model.get('type') + '"></use></svg>' +
'</div>' +
'<div class="body">' +
'<div class="title title-'+ theme_class +'">' +
'<div>' + this.model.get('title') + '</div>';
if (this.model.getDate()) {
html += '<div class="date date-'+ theme_class +'">' + this.model.getDate() + '</div>';
}
html += '</div>' +
'<div class="notification-content notification-content-'+ theme_class +'">' + this.addMediaPlayer(this.model.get('content')) + '</div>' +
'<div class="tvd-notification-actions">';
if ( this.model.get( 'button1_action' )) {
html += '<a type="button" class="tve-dash-button notify-primary notify-primary-'+ theme_class +'" href="' + this.model.get( 'button1_action' ) + '" target="_blank" rel="noopener noreferrer">' + this.model.get( 'button1_label' ) + '</a>';
}
if ( this.model.get( 'button2_action' ) ) {
html += '<a type="button" class="tve-dash-button notify-secondary notify-secondary-'+ theme_class +'" href="' + this.model.get( 'button2_action' ) + '" target="_blank" rel="noopener noreferrer">' + this.model.get( 'button2_label' ) + '</a>';
}
if ( this.model.get('dismissed') != 1 ) {
html += '<span class="dismiss-notification dismiss-notification-'+ theme_class +'" data-id="' + this.model.get('notification_id') + '">Dismiss</span>';
}
html += '</div>' +
'</div>' +
'</div>';
return html;
},
addMediaPlayer: function (content) {
content = content.replace(/&nbsp;/g, '<br>');
// Check for direct video links (mp4, webm, ogg)
const videoRegex = /(https?:\/\/\S+\.(mp4|webm|ogg))/gi;
if (videoRegex.test(content)) {
content = content.replace(videoRegex, (match, videoUrl) => {
return `<div class="notification-content-video-container"><video class="custom-video-player" controls>
<source src="${videoUrl}" type="video/mp4">
Your browser does not support the video tag.
</video></div>`;
});
}
// Check for YouTube links
const youtubeRegex = /(https?:\/\/(www\.)?youtube\.com\/watch\?v=[\w-]+|https?:\/\/youtu\.be\/[\w-]+)/gi;
if (youtubeRegex.test(content)) {
content = content.replace(youtubeRegex, (match, youtubeUrl) => {
const urlParams = new URLSearchParams(new URL(youtubeUrl).search);
const videoId = urlParams.get('v') || youtubeUrl.split('/').pop();
const embedUrl = `https://www.youtube.com/embed/${videoId}`;
return `<div class="notification-content-video-container"><iframe class="custom-iframe" src="${embedUrl}" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe></div>`;
});
}
const youtubeFigureRegex = /<figure[^>]*>\s*<div[^>]*>\s*(https?:\/\/(www\.)?youtube\.com\/watch\?v=[\w-]+|https?:\/\/youtu\.be\/[\w-]+)\s*<\/div>\s*<\/figure>/gi;
content = content.replace(youtubeFigureRegex, (match, youtubeUrl) => {
const urlParams = new URLSearchParams(new URL(youtubeUrl).search);
const videoId = urlParams.get('v') || youtubeUrl.split('/').pop();
const embedUrl = `https://www.youtube.com/embed/${videoId}`;
return `<div class="notification-content-video-container"><iframe class="custom-iframe" src="${embedUrl}" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe></div>`;
});
// Check for GIF images inside <a> tags
const gifInAnchorRegex = /<a[^>]*>\s*(https?:\/\/\S+\.(gif))\s*<\/a>/gi;
if (gifInAnchorRegex.test(content)) {
content = content.replace(gifInAnchorRegex, (match, gifUrl) => {
return `<div class="notification-content-image-container"><img class="custom-image-player" src="${gifUrl}" alt="GIF"></div>`;
});
}
// Check for image links (jpg, jpeg, png, gif)
const imageRegex = /(https?:\/\/\S+\.(jpg|jpeg|png|gif))/gi;
if (content.includes('<img')) {
return content;
}
if (imageRegex.test(content)) {
content = content.replace(imageRegex, (match, imageUrl) => {
return `<div class="notification-content-image-container"><img class="custom-image-player" src="${imageUrl}" alt="Image"></div>`;
});
}
const htmlTagRegex = /<\/?[a-z][\s\S]*>/i;
if (!htmlTagRegex.test(content)) {
content = content.replace(/\n/g, '<br>');
}
return content;
}
} );
} )( jQuery );

View File

@@ -0,0 +1,48 @@
(function ( $ ) {
const BaseView = require( './Base' );
module.exports = BaseView.extend( {
dismissedCount: 0,
activeCount: 0,
events: {
'click .tvd-notifications-btn': 'open',
'click .td-app-notification-counter-holder': 'open',
'click .text-notify-t-automator': 'open',
},
initialize: function ( options ) {
$( window ).on('scroll', this.adjustDrawerPosition.bind(this));
$( document ).on('click', this.handleDocumentClick.bind(this));
if ( options ) {
this.activeCount = options.activeCount;
this.dismissedCount = options.dismissedCount;
}
},
adjustDrawerPosition: function () {
const scrollTop = $( window ).scrollTop();
const newTop = 124 + scrollTop + 'px';
this.$( '.td-app-notification-drawer' ).css( 'top', newTop );
},
render: function () {
this.$( '.td-app-notification-counter-holder' ).text( this.activeCount );
this.activeCount >= 1 ? this.$( '.td-app-notification-counter-holder' ).show() : this.$( '.td-app-notification-counter-holder' ).hide();
return this;
},
open: function () {
$( '.td-app-notification-overlay' ).removeClass( 'close' );
$( '.td-app-notification-drawer' ).addClass( 'open' );
},
handleDocumentClick: function (event) {
if ( $(event.target).closest('.td-app-notification-overlay.overlay').length) {
$( '.td-app-notification-overlay' ).addClass( 'close' );
$( '.td-app-notification-drawer' ).removeClass( 'open' );
}
}
} );
} )( jQuery );

View File

@@ -0,0 +1,73 @@
( function ( $ ) {
const BaseView = require( './Base' );
module.exports = BaseView.extend( {
hideDismissAll: false,
events: {
'click .dismiss-all': 'dismissAll',
},
initialize: function ( options ) {
if ( options && options.hideDismissAll ) {
this.hideDismissAll = options.hideDismissAll;
}
this.listenTo( this.collection, 'change:dismiss-all', this.collectionChanged );
},
render: function () {
this.$el.html( this.getHtml() );
return this;
},
getHtml: function () {
let theme_class = TD_Notification.notify_class ? TD_Notification.notify_class : '';
let html = '<div class="pagination pagination-'+ theme_class +'"></div>' +
'<div class="dismiss-all dismiss-all-'+ theme_class +'">';
if ( !this.hideDismissAll ) {
html += '<span class="dismiss-all dismiss-all-'+ theme_class +'">Dismiss All</span>';
}
html += '</div>';
return html;
},
dismissAll: function () {
this.collection.each(function (item) {
const index = TD_Notification.data.active.findIndex(notification => notification.notification_id == item.get('notification_id'));
if (index !== -1) {
TD_Notification.data.active.splice(index, 1);
}
TD_Notification.data.dismissed.unshift(item.toJSON());
});
this.collectionChanged();
this.dismissAllNotification();
},
dismissAllNotification: function () {
$.ajax( {
type: 'POST',
url: TD_Notification.baseUrl + '/dismiss-all',
headers: {
'X-WP-Nonce': TD_Notification?.dismiss_nonce // Pass the nonce in the headers
},
success: function (response) {
// Handle success response
},
error: function (xhr, status, error) {
console.error('Error marking notification as read:', error);
}
} );
},
collectionChanged: function () {
this.trigger( 'collectionChanged', { notification_type: 'Active', collection: this.collection } );
}
});
} )( jQuery );

View File

@@ -0,0 +1,75 @@
( function ( $ ) {
let BaseView = require( './Base' );
module.exports = BaseView.extend( {
data: {
notification_type: 'Active',
notification_number: 0,
active_count: 0,
dismissed_count: 0
},
events: {
'click .toggle-to-dismissed': 'toggleNotification',
'click .toggle-to-active': 'toggleNotification'
},
initialize: function ( options ) {
if ( options ) {
this.data.active_count = options.activeCount;
this.data.dismissed_count = options.dismissedCount;
this.data.notification_type = options.notificationType;
}
if ( this.data.notification_type === 'Dismissed' ) {
this.collection.each( function ( item ) {
if ( item.get( 'dismissed' ) == 0 ) {
item.set( 'dismissed', 1 );
}
});
}
this.data.notification_number = this.collection.length;
},
render: function () {
this.$el.html( this.getHtml() );
return this;
},
getHtml: function () {
let html = '';
let theme_class = TD_Notification.notify_class ? TD_Notification.notify_class : '';
if ( this.data.notification_type === 'Active' ) {
html += '<span class="tvd-notification-number notification-number-'+ theme_class +'">(' + this.data.notification_number + ') New Notifications</span>';
} else if ( this.data.notification_type === 'Dismissed' ) {
html += '<span class="tvd-notification-number notification-number-'+ theme_class +'"> (' + this.data.notification_number + ') Notifications</span>';
}
html += '<div class="toggle-notifications">';
//If current tab is active and no of dismissed notification is zero, no need to show
if ( this.data.notification_type === 'Active' && this.data.dismissed_count > 0 ) {
html += '<span class="toggle-to-dismissed toggle-notifications toggle-'+ theme_class +'">Dismissed Notifications</span>';
}
//If Current tab is Dismissed should have option to go to active notification
if ( this.data.notification_type === 'Dismissed' ) {
html += '<span class="toggle-to-active toggle-notifications toggle-'+ theme_class +'">Active Notifications</span>';
}
html += '</div>';
html += '<span class="tvd-close-notification-drawer close-notification-'+ theme_class +'">';
html += '<svg class="icon-tve-dash-notification-cross"><use xlink:href="#icon-tvd-notification-cross"></use></svg>';
html += '</span>';
return html;
},
toggleNotification: function ( event ) {
const notificationType = $( event.currentTarget ).hasClass( 'toggle-to-dismissed' ) ? 'Dismissed' : 'Active';
if (this.data.notification_type !== notificationType) {
this.data.notification_type = notificationType;
this.trigger('notificationTypeChanged', { notification_type: notificationType });
}
event.stopPropagation();
}
} );
} )( jQuery );

View File

@@ -0,0 +1,149 @@
(function ( $ ) {
let BaseView = require('./Base');
let HeaderView = require('./NotificationHeader');
let FooterView = require('./NotificationFooter');
let NotificationView = require('./NotificationController');
let NoNotificationView = require('./NoNotification');
module.exports = BaseView.extend( {
headerView: null,
hideDismissAll: false,
notificationType: 'Active',
dismissedCount: 0,
activeCount: 0,
events: {
'click .tvd-close-notification-drawer': 'close',
'click .dismiss-notification': 'dismiss',
'click .switch-notification-type': 'toggleNotification',
},
initialize: function ( options ) {
if ( options ) {
this.activeCount = options.activeCount;
this.dismissedCount = options.dismissedCount;
this.hideDismissAll = options.notificationType === 'Dismissed' || this.activeCount < 2;
this.notificationType = options.notificationType;
}
this.listenTo( this.collection, 'change:dismiss', this.collectionChanged );
},
render: function () {
this.renderHeader();
const $wrapperContainer = this.$('.td-app-notification-wrapper');
if (this.collection.length > 0) {
$wrapperContainer.html('<div class="tvd-notifications-list"></div>');
this.renderList();
} else {
$wrapperContainer.empty();
new NoNotificationView( {
el: $wrapperContainer,
notificationType: this.notificationType
} ).render();
}
if ($wrapperContainer.find('.notification-footer').length === 0) {
this.renderFooter();
}
return this;
},
renderHeader: function () {
this.headerView = new HeaderView( {
el: this.$( '.td-app-notification-header' ),
collection: this.collection,
activeCount: this.activeCount,
dismissedCount: this.dismissedCount,
notificationType: this.notificationType
} ).render();
this.listenTo( this.headerView, 'notificationTypeChanged', this.notificationTypeChanged );
},
renderList: function () {
const $listContainer = this.$( '.tvd-notifications-list' );
$listContainer.empty();
// Instantiate NotificationView outside the loop
const notificationViews = this.collection.map(function(model) {
return new NotificationView({
model: model
});
});
// Render each NotificationView instance within the loop
notificationViews.forEach(function(notificationView) {
$listContainer.append(notificationView.render().el);
});
},
renderFooter: function () {
const $notificationFooter = $( '.notification-footer' );
$notificationFooter.empty();
new FooterView({
el: $notificationFooter,
collection: this.collection,
notificationType: this.notificationType,
hideDismissAll: this.hideDismissAll,
}).render();
},
toggleNotification: function ( event ) {
const notificationType = $( event.currentTarget ).hasClass( 'toggle-to-dismissed' ) ? 'Dismissed' : 'Active';
this.trigger('notificationTypeChanged', { notification_type: notificationType });
},
dismiss: function ( event ) {
event.preventDefault();
let notification_id = $( event.currentTarget ).data( 'id' );
let item = this.collection.findWhere( { notification_id: String(notification_id) } );
if ( item ) {
this.collection.remove(item);
const index = TD_Notification.data.active.findIndex(notification => notification.notification_id== notification_id);
if (index !== -1) {
TD_Notification.data.active.splice(index, 1);
}
TD_Notification.data.dismissed.unshift(item.toJSON());
this.dismissNotification( notification_id );
this.collectionChanged();
}
},
dismissNotification: function ( remoteId ) {
$.ajax( {
type: 'POST',
url: TD_Notification.baseUrl + '/dismiss',
headers: {
'X-WP-Nonce': TD_Notification?.dismiss_nonce // Pass the nonce in the headers
},
data: {
notification_id: remoteId,
},
success: function ( response ) {
// Handle success response
},
error: function (xhr, status, error) {
console.error('Error marking notification as read:', error);
}
} );
},
close: function () {
const notificationType = 'Active';
this.trigger('notificationTypeChanged', { notification_type: notificationType });
$( '.td-app-notification-overlay' ).addClass( 'close' );
$( '.td-app-notification-drawer' ).removeClass( 'open' );
},
collectionChanged: function () {
this.trigger( 'collectionChanged', { notification_type: 'Active', collection: this.collection } );
},
notificationTypeChanged: function ( data ) {
this.trigger( 'notificationTypeChanged', data );
}
} );
} )( jQuery );