Snippets Collections
<!DOCTYPE html>
<html>
<head>
    <title>Hello World Program</title>
</head>
<body>

<script>
    // Using console.log()
    console.log("Hello World");

    // Using document.write()
    document.write("Hello World");

    // Using alert()
    alert("Hello World");
</script>

</body>
</html>
<!DOCTYPE html>
<html lang="en" ng-app="myApp">
<head>
    <meta charset="UTF-8">
    <title>AngularJS Form Validation</title>
    <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.8.2/angular.min.js"></script>
    <style>
        .error { color: red; }
        input.ng-invalid.ng-touched { border-color: red; }
        input.ng-valid.ng-touched { border-color: green; }
    </style>
</head>
<body ng-controller="FormCtrl">

<h2>Registration Form</h2>

<form name="userForm" ng-submit="submitForm()" novalidate>

    <!-- Name -->
    <label>Name:</label><br>
    <input type="text" name="name"
           ng-model="user.name"
           required
           ng-minlength="3">
    <div class="error" ng-show="userForm.name.$touched && userForm.name.$invalid">
        <span ng-show="userForm.name.$error.required">Name is required.</span>
        <span ng-show="userForm.name.$error.minlength">Minimum 3 characters.</span>
    </div>
    <br><br>

    <!-- Email -->
    <label>Email:</label><br>
    <input type="email" name="email"
           ng-model="user.email"
           required>
    <div class="error" ng-show="userForm.email.$touched && userForm.email.$invalid">
        <span ng-show="userForm.email.$error.required">Email is required.</span>
        <span ng-show="userForm.email.$error.email">Invalid email format.</span>
    </div>
    <br><br>

    <!-- Password -->
    <label>Password:</label><br>
    <input type="password" name="password"
           ng-model="user.password"
           ng-minlength="6"
           required>
    <div class="error" ng-show="userForm.password.$touched && userForm.password.$invalid">
        <span ng-show="userForm.password.$error.required">Password is required.</span>
        <span ng-show="userForm.password.$error.minlength">Minimum 6 cha
add_action( 'wp_head', 'add_recaptcha_site_key' );

function add_recaptcha_site_key() {
    ?>
    <script src="https://www.google.com/recaptcha/api.js?render=YOUR_SITE_KEY_HERE"></script>
    <?php
}
function wc_varb_price_range( $wcv_price, $product ) {
 
    $prefix = sprintf('%s: ', __('From', 'wcvp_range'));
 
    $wcv_reg_min_price = $product->get_variation_regular_price( 'min', true );
    $wcv_min_sale_price    = $product->get_variation_sale_price( 'min', true );
    $wcv_max_price = $product->get_variation_price( 'max', true );
    $wcv_min_price = $product->get_variation_price( 'min', true );
 
    $wcv_price = ( $wcv_min_sale_price == $wcv_reg_min_price ) ?
        wc_price( $wcv_reg_min_price ) :
        '<del>' . wc_price( $wcv_reg_min_price ) . '</del>' . '<ins>' . wc_price( $wcv_min_sale_price ) . '</ins>';
 
    return ( $wcv_min_price == $wcv_max_price ) ?
        $wcv_price :
        sprintf('%s%s', $prefix, $wcv_price);
}
 
add_filter( 'woocommerce_variable_sale_price_html', 'wc_varb_price_range', 10, 2 );
add_filter( 'woocommerce_variable_price_html', 'wc_varb_price_range', 10, 2 );
export const parseCsvText = (text: string, delimiter: string) => {
  const normalized = text.replace(/^\uFEFF/, "");
  const rows: string[][] = [];
  let row: string[] = [];
  let field = "";
  let inQuotes = false;

  for (let i = 0; i < normalized.length; i += 1) {
    const char = normalized[i];

    if (char === '"') {
      if (inQuotes && normalized[i + 1] === '"') {
        field += '"';
        i += 1;
        continue;
      }
      inQuotes = !inQuotes;
      continue;
    }

    if (!inQuotes && char === delimiter) {
      row.push(field);
      field = "";
      continue;
    }

    if (!inQuotes && (char === "\n" || char === "\r")) {
      if (char === "\r" && normalized[i + 1] === "\n") {
        i += 1;
      }
      row.push(field);
      rows.push(row);
      row = [];
      field = "";
      continue;
    }

    field += char;
  }

  if (field.length > 0 || row.length > 0) {
    row.push(field);
    rows.push(row);
  }

  return rows;
};
<!DOCTYPE html>
<html lang="en" ng-app="myApp">
<head>
  <meta charset="UTF-8">
  <title>AngularJS Form Validation</title>

  <!-- AngularJS CDN -->
  <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.8.2/angular.min.js"></script>

  <style>
    body {
      font-family: Arial;
      margin: 40px;
    }
    input.ng-invalid.ng-touched, select.ng-invalid.ng-touched {
      border: 2px solid red;
    }
    input.ng-valid.ng-touched, select.ng-valid.ng-touched {
      border: 2px solid green;
    }
    .error {
      color: red;
      font-size: 14px;
    }
  </style>
</head>

<body ng-controller="FormController">

  <h2>Registration Form</h2>

  <form name="regForm" novalidate>

    <!-- Name -->
    <label>Name:</label><br>
    <input type="text" name="name" ng-model="user.name" required ng-minlength="3">
    <div class="error" ng-show="regForm.name.$touched && regForm.name.$invalid">
      Name is required (min 3 characters)
    </div>
    <br><br>

    <!-- Email -->
    <label>Email:</label><br>
    <input type="email" name="email" ng-model="user.email" required>
    <div class="error" ng-show="regForm.email.$touched && regForm.email.$invalid">
      Enter a valid email address
    </div>
    <br><br>

    <!-- Password -->
    <label>Password:</label><br>
    <input type="password" name="password" ng-model="user.password" required ng-minlength="6">
    <div class="error" ng-show="regForm.password.$touched && regForm.password.$invalid">
      Password must be at least 6 characters
    </div>
    <br><br>

    <!-- Age -->
    <label>Age:</label><br>
    <input type="number" name="age" ng-model="user.age" min="18" max="60" required>
    <div class="error" ng-show="regForm.age.$touched && regForm.age.$invalid">
      Age must be between 18 and 60
    </div>
    <br><br>

    <!-- Gender -->
    <label>Gender:</label><br>
    <input type="radio" ng-model="user.gender" value="Male" required> Male
    <input type="radio" ng-model="user.gender" value="Female"> Female
    <br><br>

    <!-- Country -->
    <label>Country:</label><br>
    <select name="country" ng-model="user.country" required>
      <option value="">--Select--</option>
      <option>India</option>
      <option>USA</option>
      <option>UK</option>
    </select>
    <div class="error" ng-show="regForm.country.$touched && regForm.country.$invalid">
      Please select a country
    </div>
    <br><br>

    <!-- Terms -->
    <label>
      <input type="checkbox" ng-model="user.terms" required> I accept terms and conditions
    </label>
    <br><br>

    <!-- Submit -->
    <button type="submit" ng-disabled="regForm.$invalid">
      Submit
    </button>

  </form>

  <br>

  <!-- Display form data -->
  <h3>Entered Data:</h3>
  <pre>{{ user | json }}</pre>

</body>

<script>
  var app = angular.module("myApp", []);

  app.controller("FormController", function ($scope) {
    $scope.user = {};
  });
</script>

</html>
def get_folder_in_s3_bucket(bucket, prefix):
    try:
        s3_bucket_data = client.list_objects(Bucket= bucket, Prefix= prefix, Delimiter="/")
        s3_folder_list =  [(prefix_dict['Prefix']).removeprefix(prefix) for prefix_dict in s3_bucket_data.get("CommonPrefixes", None)]
        return s3_folder_list
    except Exception as e:
        print(f"There is an error as {e}")
        return None
{
	"blocks": [
		{
			"type": "header",
			"text": {
				"type": "plain_text",
				"text": ":sunshine: :x-connect: Boost Days: What's on this week :x-connect: :sunshine:"
			}
		},
		{
			"type": "section",
			"text": {
				"type": "mrkdwn",
				"text": "Good morning Melbourne and hope you all had a fab weekend! :sunshine: \n\n Please see below for what's on this week! :yay: BRINGING some summer vibes to the office! "
			}
		},
		{
			"type": "divider"
		},
		{
			"type": "header",
			"text": {
				"type": "plain_text",
				"text": ":calendar-date-28: Wednesday, 28th January",
				"emoji": true
			}
		},
		{
			"type": "section",
			"text": {
				"type": "mrkdwn",
				"text": "\n:coffee: :muffin: *Xero Café* – Cookies and Tim Tams \n :coffee: *Barista Special* – Golden Latte \n :flag-fr: Join us at *12.00pm* for some French Lunch in the Wominjeka Breakout Space on Level 3."
			}
		},
		{
			"type": "header",
			"text": {
				"type": "plain_text",
				"text": ":calendar-date-29: Thursday, 29th January",
				"emoji": true
			}
		},
		{
			"type": "section",
			"text": {
				"type": "mrkdwn",
				"text": ":coffee: *Xero Cafe*: Cookies and Tim tams.\n :coffee: *Barista Special* – Golden Latte \n :Breakfast: :strawberry: Join us at *8.30am -10.30am* for a *Tennis Inspired Breaky* in the Wominjeka Breakout Space on Level 3 . "
			}
		},
		{
			"type": "header",
			"text": {
				"type": "plain_text",
				"text": ":calendar-date-30: Friday, 30th January ",
				"emoji": true
			}
		},
		{
			"type": "section",
			"text": {
				"type": "mrkdwn",
				"text": ":cheers-9743: :aperol-spritz: *Happy Hour:* from 4pm - 5.30pm in the Wominjeka Breakout space. \n\n :yay: *Xero Melbourne Open*: Who is ready to become Melbourne Xero's PING PONG champion? There are prizes to be won, AO merchandise! Sign your team up in the :thread: \n\n :frozen-yoghurt: *Summer Special*: We also have a frozen yoghurt station, YES the best next thing to *YO-CHI*. We are bringing summer vibes to the office and would love to see our Xeros joining us."
			}
		},
		{
			"type": "divider"
		}
	]
}
# Intenta reparar el archivo primero
mv ~/.zsh_history ~/.zsh_history_bad
strings ~/.zsh_history_bad > ~/.zsh_history
fc -R ~/.zsh_history  # Recargar historial
/**
 if rerendering occurs or to stop duplicate events

if (button.dataset.recShareTracked) return;
button.dataset.recShareTracked = "true";

*/



// example

 function recShareTracking() {
        const recUserActions = document.querySelector(".rec-user-actions");

        if (!recUserActions) return;

        const shareButtons = recUserActions.querySelectorAll(
            ".rec-user-actions__cta-button",
        );

        if (!shareButtons.length) return;

        console.log({shareButtons})

        shareButtons.forEach((button) => {
            // Prevent duplicate listeners
            if (button.dataset.recShareTracked) return;
            button.dataset.recShareTracked = "true";

            button.addEventListener("click", (e) => {
                const clickedElement = e.target;
                const isButtonClick =
                    clickedElement === button ||
                    clickedElement.closest(".rec-user-actions__cta-button") ===
                        button;

                if (!isButtonClick) return;

                const buttonTextRaw = button.textContent?.trim() || "";
                const buttonText = buttonTextRaw.replace(/\s+/g, " ").trim() || "";

                if (buttonText) {
                    gtmPush({
                        event: "interaction_click",
                        component_name: "button",
                        click_text: buttonText,
                        click_url: null,
                    });
                }
            });
        });
    }
#include <stdio.h>
#include <omp.h>

int main(int argc, char** argv){
    int partial_Sum, total_Sum;

    #pragma omp parallel private(partial_Sum) shared(total_Sum)
    {
        partial_Sum = 0;
        total_Sum = 0;

        #pragma omp for
        for(int i = 1; i <= 1000; i++){
            partial_Sum += i;
        }

        //Create thread safe region.
        #pragma omp critical
        {
            //add each threads partial sum to the total sum
            total_Sum += partial_Sum;
        }
    }
    printf("Total Sum: %d\n", total_Sum);
    return 0;
}

#include <stdio.h>
#include <omp.h>

int main(int argc, char** argv){
    #pragma omp parallel
    {
        printf("Hello from process: %d\n", omp_get_thread_num());
    }
    return 0;
}
concat('https://crm.zoho.com/crm/org834552803/tab/Potentials/', "Reservations"."Id") as "View Record",
  
    const listItem = target.closest('li.facet-group__list-item');
    if (!listItem) return;

    // Toggle the data-checked attribute
    const wasChecked = listItem.getAttribute('data-checked') === 'true';
    listItem.setAttribute('data-checked', wasChecked ? 'false' : 'true');
Bring your crypto exchange idea to life with confidence. Block Intelligence offers expert Centralized Crypto Exchange Development, combining fast trading engines, strong security, seamless KYC/AML compliance, and intuitive interfaces. Perfect for startups and enterprises, our solutions help you launch smoothly, attract users, and scale in a competitive crypto market. From concept to a fully functional platform, we help you build an exchange that works, grows, and earns trust.

Know more: https://www.blockintelligence.io/centralized-crypto-exchange-development-company
 
WhatsApp: +91 77384 79381

 Email: connect@blockchain.ai.in
{
	"blocks": [
		{
			"type": "header",
			"text": {
				"type": "plain_text",
				"text": ":sunshine: :x-connect: Boost Days: What's on this week :x-connect: :sunshine:"
			}
		},
		{
			"type": "section",
			"text": {
				"type": "mrkdwn",
				"text": "Good morning Brisbane and hope you all had a fab bank holiday weekend! :sunshine: \n\n Please see below for what's on this week! :yay:  "
			}
		},
		{
			"type": "divider"
		},
		{
			"type": "header",
			"text": {
				"type": "plain_text",
				"text": ":calendar-date-28: Wednesday, 29th January",
				"emoji": true
			}
		},
		{
			"type": "section",
			"text": {
				"type": "mrkdwn",
				"text": ":coffee: *Café Partnership*: Enjoy free coffee and café-style beverages from our Cafe partner *Industry Beans*. \n\n:lunch: *Morning Tea*:from *9am* in the kitchen! \n:lunch: Enjoy a Thai Lunch at 12pm in the kitchen."
			}
		},
		{
			"type": "header",
			"text": {
				"type": "plain_text",
				"text": ":calendar-date-30:Friday, 30th January ",
				"emoji": true
			}
		},
		{
			"type": "section",
			"text": {
				"type": "mrkdwn",
				"text": ":cheers-9743: :cheese: *Happy Hour & Happy Friday:* from 3pm - 4pm in the kitchen! Wind down for the week over some drinks and nibbles."
			}
		},
		{
			"type": "divider"
		},
		{
			"type": "section",
			"text": {
				"type": "mrkdwn",
				"text": "Stay tuned to this channel for more details, check out the <https://calendar.google.com/calendar/u/0?cid=Y19uY2M4cDN1NDRsdTdhczE0MDhvYjZhNnRjb0Bncm91cC5jYWxlbmRhci5nb29nbGUuY29t|*Brisbane Social Calendar*>, and get ready to Boost your workdays!\n\nLove,\nWX Team :party-wx:"
			}
		}
	]
}
export const LOCAL_STORAGE_KEYS = {
  CONVERSATION_SELECTED_TAB: "conversation-selected-tab",
  CONVERSATION_RIGHT_PANEL_SHOWN: "conversation-right-panel-shown",
  CONVERSATION_UNPINNED_TABS: "conversation-unpinned-tabs",
};
onSuccess: (_, variables) => {
  clearConversationLocalStorage(variables.conversationId);
},
export const clearConversationLocalStorage = (conversationId: string) => {
  const keys = [
    "conversation-selected-tab",
    "conversation-right-panel-shown",
    "conversation-unpinned-tabs",
  ];

  keys.forEach((prefix) => {
    localStorage.removeItem(`${prefix}-${conversationId}`);
  });
}
Blitzvorschau (Schnellste Methode)

Angenommen, Ihre Daten stehen in Spalte A1 ("Test1234").
Schreiben Sie in Zelle B1 das gewünschte Ergebnis ohne Zahlen ("Test").
Schreiben Sie in Zelle B2 das Ergebnis für die nächste Zeile.
Drücken Sie Strg + E (oder gehen Sie auf Daten -> Blitzvorschau). Excel erkennt das Muster und entfernt alle Zahlen. 
 function footerNavTracking() {
        const footerWrapper = document.querySelector(".footer__wrapper");
        if (!footerWrapper) return;

        footerWrapper.addEventListener("click", (e) => {
            // Find the closest relevant link
            const link = e.target.closest(
                ".footer__col-second-button_wrapper a, .footer__logo-link",
            );
            if (!link) return;

            // Get the URL from the <a> tag
            const url = link.getAttribute("href") || "";

            // Social links
            if (link.matches(".footer__col-second-button_wrapper a")) {
                gtmPush({
                    event: "navigation_click",
                    navigation_type: "footer_nav",
                    click_text: "Social",
                    click_url: url,
                    nav_level: 1,
                });
            }

            // Logo links
            if (link.matches(".footer__logo-link")) {
                gtmPush({
                    event: "navigation_click",
                    navigation_type: "footer_nav",
                    click_text: "logo",
                    click_url: url,
                    nav_level: 1,
                });
            }
        });
    }
 function navigationTracking() {
        const mainNav = document.querySelector(".main-navigation__main");
        if (!mainNav) return;

        const navItems = [
            {
                selector: ".main-navigation__control-link",
                type: "nav-links",
                handler: (e, el) => {
                    const btnTextRaw = el.textContent || "";
                    const btnText = btnTextRaw.replace(/\s+/g, " ").trim();
                    const isPressed =
                        el.getAttribute("aria-pressed") === "true";
                    const navState = isPressed ? "closed" : "open";

                    navigationClickTracking(btnText, null, 1, navState);
                },
            },
            {
                selector: "#main-nav-bookmarks",
                type: "nav-bookmark",
                handler: (e, el) => {
                    const btnText = el.innerText.trim();
                    navigationClickTracking(btnText, "/sruh/bookmarks", 1);
                },
            },
            {
                selector: "#main-nav-close",
                type: "nav-search",
                handler: (e, el) => {
                    const btnText = el.outerText.trim();
                    navigationClickTracking(btnText, null, 1);
                },
            },
            {
                selector: "#main-nav-quiz",
                type: "nav-quiz",
                handler: () => {
                    navigationClickTracking(
                        "Senior Road User Quiz",
                        "/sruh/quiz",
                        1,
                    );
                },
            },
        ];

        navItems.forEach((item) => {
            const elements = mainNav.querySelectorAll(item.selector);

            elements.forEach((el) => {
                if (el.dataset.navTracked) return;
                el.dataset.navTracked = "true";

                el.addEventListener("click", (e) => {
                    item.handler(e, el);
                });
            });
        });

        /*  SUB MENU LINK TRACKING */

        const subNavWrapper = document.querySelector(".main-navigation__aside");
        if (!subNavWrapper) return;

        // Use event delegation to catch all subnav link clicks
        // This works regardless of when the subnav is opened
        if (subNavWrapper.dataset.subNavTrackingSetup) return;
        subNavWrapper.dataset.subNavTrackingSetup = "true";

        subNavWrapper.addEventListener("click", (e) => {
            // Find the clicked link
            const link = e.target.closest(
                ".main-navigation__content-list li a",
            );
            if (!link) return;

            // Only track if the link is within an active menu
            const activeMenu = link.closest(".main-navigation__content.active");
            if (!activeMenu) return;

            const linkText = link.textContent.replace(/\s+/g, " ").trim();
            const linkUrl = link.getAttribute("href") || "";

            navigationClickTracking(linkText, linkUrl, 2);
        });
    }
const TEST_MODE = true; // true = preventDefault for testing, false = production
    function maybePreventDefault(e) {
        if (TEST_MODE) e.preventDefault();
    }
     // usage: maybePreventDefault(e);


//example

   function footerNavTracking() {
        const footerWrapper = document.querySelector(".footer");
        if (!footerWrapper) return;

        footerWrapper.addEventListener("click", (e) => {
            maybePreventDefault(e);
            const { target } = e;

            const link = target.closest(`
                .footer-content__partners-link,
                .footer-content__navigation-link,
                .footer__copyright-section a,
                .footer-site-logo__link
            `);
            if (!link) return;

            const linkurl = link.getAttribute('href') || "";
            const linkTextRaw = link.textContent.trim();
            const linkText = linkTextRaw.replace(/\s+/g, " ").trim() || "";

            if(link.matches('.footer-content__partners-link')){
                gtmPush({
                    event: "navigation_click",
                    navigation_type: "footer_nav",
                    click_text: "our partners",
                    click_url: linkurl,
                    nav_level: 1,
                });
            }

            if(link.matches('.footer-content__navigation-link, .footer__copyright-section a, .footer-site-logo__link')){
                gtmPush({
                    event: "navigation_click",
                    navigation_type: "footer_nav",
                    click_text: linkText,
                    click_url: linkurl,
                    nav_level: 1,
                });
            }
        });
    }
<!DOCTYPE html>
<html lang="es">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Creador de Currículum Vitae</title>
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
    <style>
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
            font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
        }
        
        body {
            background-color: #f5f7fa;
            color: #333;
            line-height: 1.6;
            padding-bottom: 40px;
        }
        
        .container {
            max-width: 1200px;
            margin: 0 auto;
            padding: 20px;
        }
        
        header {
            text-align: center;
            padding: 30px 0;
            background: linear-gradient(135deg, #2c3e50, #4a6491);
            color: white;
            border-radius: 10px;
            margin-bottom: 30px;
            box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
        }
        
        h1 {
            font-size: 2.5rem;
            margin-bottom: 10px;
        }
        
        .subtitle {
            font-size: 1.1rem;
            opacity: 0.9;
        }
        
        .app-container {
            display: flex;
            flex-wrap: wrap;
            gap: 30px;
        }
        
        .form-section {
            flex: 1;
            min-width: 300px;
            background-color: white;
            border-radius: 10px;
            padding: 25px;
            box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);
        }
        
        .preview-section {
            flex: 1;
            min-width: 300px;
            background-color: white;
            border-radius: 10px;
            padding: 25px;
            box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);
        }
        
        h2 {
            color: #2c3e50;
            margin-bottom: 20px;
            padding-bottom: 10px;
            border-bottom: 2px solid #4a6491;
            display: flex;
            align-items: center;
            gap: 10px;
        }
        
        h2 i {
            color: #4a6491;
        }
        
        .form-group {
            margin-bottom: 20px;
        }
        
        label {
            display: block;
            margin-bottom: 8px;
            font-weight: 600;
            color: #2c3e50;
        }
        
        input, textarea {
            width: 100%;
            padding: 12px 15px;
            border: 1px solid #ddd;
            border-radius: 6px;
            font-size: 1rem;
            transition: border-color 0.3s;
        }
        
        input:focus, textarea:focus {
            outline: none;
            border-color: #4a6491;
        }
        
        textarea {
            min-height: 100px;
            resize: vertical;
        }
        
        .skills-container {
            display: flex;
            flex-wrap: wrap;
            gap: 10px;
            margin-top: 10px;
        }
        
        .skill-tag {
            background-color: #eef2f7;
            padding: 6px 12px;
            border-radius: 20px;
            display: flex;
            align-items: center;
            gap: 5px;
            font-size: 0.9rem;
        }
        
        .skill-tag .remove-skill {
            cursor: pointer;
            color: #e74c3c;
            font-weight: bold;
        }
        
        .add-skill {
            display: flex;
            gap: 10px;
        }
        
        .add-skill input {
            flex: 1;
        }
        
        .btn {
            background-color: #4a6491;
            color: white;
            border: none;
            padding: 10px 20px;
            border-radius: 6px;
            cursor: pointer;
            font-size: 1rem;
            font-weight: 600;
            transition: background-color 0.3s;
            display: inline-flex;
            align-items: center;
            justify-content: center;
            gap: 8px;
        }
        
        .btn:hover {
            background-color: #3a5379;
        }
        
        .btn-primary {
            background-color: #2c3e50;
        }
        
        .btn-primary:hover {
            background-color: #1a252f;
        }
        
        .btn-secondary {
            background-color: #3498db;
        }
        
        .btn-secondary:hover {
            background-color: #2980b9;
        }
        
        .btn-danger {
            background-color: #e74c3c;
        }
        
        .btn-danger:hover {
            background-color: #c0392b;
        }
        
        .btn-success {
            background-color: #27ae60;
        }
        
        .btn-success:hover {
            background-color: #219653;
        }
        
        .btn-block {
            display: block;
            width: 100%;
            margin-top: 20px;
            padding: 12px;
        }
        
        .btn-sm {
            padding: 6px 12px;
            font-size: 0.9rem;
        }
        
        /* Estilos para la foto */
        .photo-upload-container {
            display: flex;
            flex-direction: column;
            align-items: center;
            gap: 15px;
            margin-bottom: 20px;
        }
        
        .photo-preview {
            width: 150px;
            height: 180px;
            border-radius: 8px;
            border: 3px solid #4a6491;
            overflow: hidden;
            background-color: #f8f9fa;
            display: flex;
            align-items: center;
            justify-content: center;
            position: relative;
        }
        
        .photo-preview img {
            width: 100%;
            height: 100%;
            object-fit: cover;
        }
        
        .photo-placeholder {
            display: flex;
            flex-direction: column;
            align-items: center;
            justify-content: center;
            color: #7f8c8d;
            text-align: center;
            padding: 20px;
        }
        
        .photo-placeholder i {
            font-size: 48px;
            margin-bottom: 10px;
            color: #bdc3c7;
        }
        
        .photo-upload-btn {
            position: relative;
            overflow: hidden;
        }
        
        .photo-upload-btn input[type="file"] {
            position: absolute;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
            opacity: 0;
            cursor: pointer;
        }
        
        .photo-actions {
            display: flex;
            gap: 10px;
            margin-top: 10px;
        }
        
        .photo-instructions {
            font-size: 0.85rem;
            color: #666;
            text-align: center;
            margin-top: 5px;
        }
        
        .cv-preview {
            background-color: white;
            min-height: 800px;
            padding: 30px;
            border: 1px solid #eee;
            border-radius: 8px;
            font-size: 0.95rem;
            line-height: 1.5;
        }
        
        /* Encabezado SIN foto (versión anterior) */
        .cv-header-no-photo {
            background-color: #2c3e50;
            color: white;
            padding: 30px;
            border-radius: 8px;
            margin-bottom: 30px;
        }
        
        /* Encabezado CON foto (nueva versión) */
        .cv-header-with-photo {
            background-color: #2c3e50;
            color: white;
            padding: 30px;
            border-radius: 8px;
            margin-bottom: 30px;
            display: flex;
            justify-content: space-between;
            align-items: flex-start;
            position: relative;
            min-height: 180px;
        }
        
        .cv-header-content {
            flex: 1;
        }
        
        .cv-photo-container {
            width: 140px;
            height: 160px;
            border-radius: 8px;
            overflow: hidden;
            border: 3px solid rgba(255, 255, 255, 0.3);
            background-color: rgba(255, 255, 255, 0.1);
            display: flex;
            align-items: center;
            justify-content: center;
            margin-left: 20px;
            flex-shrink: 0;
        }
        
        .cv-photo {
            width: 100%;
            height: 100%;
            object-fit: cover;
        }
        
        .cv-photo-placeholder {
            display: flex;
            flex-direction: column;
            align-items: center;
            justify-content: center;
            color: rgba(255, 255, 255, 0.7);
            text-align: center;
            padding: 20px;
            font-size: 0.9rem;
        }
        
        .cv-photo-placeholder i {
            font-size: 36px;
            margin-bottom: 8px;
        }
        
        .cv-name {
            font-size: 2.2rem;
            margin-bottom: 5px;
        }
        
        .cv-title {
            font-size: 1.4rem;
            opacity: 0.9;
            margin-bottom: 15px;
        }
        
        .cv-contact {
            display: flex;
            flex-wrap: wrap;
            gap: 20px;
            margin-top: 15px;
        }
        
        .cv-contact-item {
            display: flex;
            align-items: center;
            gap: 8px;
            font-size: 0.95rem;
        }
        
        .cv-section {
            margin-bottom: 25px;
        }
        
        .cv-section-title {
            color: #2c3e50;
            border-bottom: 2px solid #4a6491;
            padding-bottom: 5px;
            margin-bottom: 15px;
            font-size: 1.3rem;
        }
        
        .cv-item {
            margin-bottom: 20px;
            padding-bottom: 15px;
            border-bottom: 1px solid #eee;
        }
        
        .cv-item:last-child {
            border-bottom: none;
        }
        
        .cv-item-title {
            font-weight: 600;
            color: #333;
            margin-bottom: 5px;
            font-size: 1.1rem;
        }
        
        .cv-item-subtitle {
            color: #4a6491;
            font-style: italic;
            margin-bottom: 5px;
        }
        
        .cv-item-dates {
            color: #666;
            font-size: 0.9rem;
            margin-bottom: 8px;
            display: flex;
            align-items: center;
            gap: 5px;
        }
        
        .current-badge {
            background-color: #27ae60;
            color: white;
            padding: 2px 8px;
            border-radius: 12px;
            font-size: 0.8rem;
            font-weight: 600;
        }
        
        .cv-skills {
            display: flex;
            flex-wrap: wrap;
            gap: 10px;
        }
        
        .cv-skill {
            background-color: #eef2f7;
            padding: 6px 12px;
            border-radius: 4px;
            font-size: 0.9rem;
        }
        
        .actions {
            display: flex;
            justify-content: center;
            gap: 20px;
            margin-top: 30px;
            flex-wrap: wrap;
        }
        
        .instructions {
            background-color: #eef2f7;
            padding: 20px;
            border-radius: 8px;
            margin-top: 30px;
            font-size: 0.95rem;
        }
        
        .instructions h3 {
            margin-bottom: 10px;
            color: #2c3e50;
        }
        
        .instructions ul {
            padding-left: 20px;
        }
        
        .instructions li {
            margin-bottom: 8px;
        }
        
        /* Estilos para la experiencia laboral dinámica */
        .job-entry {
            background-color: #f8f9fa;
            padding: 20px;
            border-radius: 8px;
            margin-bottom: 20px;
            border-left: 4px solid #4a6491;
        }
        
        .job-header {
            display: flex;
            justify-content: space-between;
            align-items: center;
            margin-bottom: 15px;
        }
        
        .job-title {
            font-weight: 600;
            color: #2c3e50;
        }
        
        .current-job-toggle {
            display: flex;
            align-items: center;
            gap: 8px;
            margin-bottom: 15px;
        }
        
        .toggle-label {
            font-size: 0.9rem;
            color: #666;
        }
        
        .toggle-switch {
            position: relative;
            display: inline-block;
            width: 50px;
            height: 24px;
        }
        
        .toggle-switch input {
            opacity: 0;
            width: 0;
            height: 0;
        }
        
        .toggle-slider {
            position: absolute;
            cursor: pointer;
            top: 0;
            left: 0;
            right: 0;
            bottom: 0;
            background-color: #ccc;
            transition: .4s;
            border-radius: 24px;
        }
        
        .toggle-slider:before {
            position: absolute;
            content: "";
            height: 16px;
            width: 16px;
            left: 4px;
            bottom: 4px;
            background-color: white;
            transition: .4s;
            border-radius: 50%;
        }
        
        input:checked + .toggle-slider {
            background-color: #27ae60;
        }
        
        input:checked + .toggle-slider:before {
            transform: translateX(26px);
        }
        
        .remove-job {
            background-color: transparent;
            border: none;
            color: #e74c3c;
            cursor: pointer;
            font-size: 1.2rem;
            padding: 5px;
        }
        
        .add-job-btn {
            margin-top: 10px;
        }
        
        /* Estilos para el pie de página del PDF */
        .pdf-footer {
            position: fixed;
            bottom: 0;
            left: 0;
            right: 0;
            text-align: center;
            font-size: 10pt;
            color: #666;
            padding: 10px;
            background-color: white;
            border-top: 1px solid #eee;
        }
        
        .page-number {
            font-family: Arial, sans-serif;
        }
        
        @media (max-width: 768px) {
            .app-container {
                flex-direction: column;
            }
            
            .cv-preview {
                min-height: auto;
            }
            
            .actions {
                flex-direction: column;
            }
            
            .job-header {
                flex-direction: column;
                align-items: flex-start;
                gap: 10px;
            }
            
            .cv-header-with-photo {
                flex-direction: column;
                align-items: center;
                text-align: center;
                min-height: auto;
            }
            
            .cv-photo-container {
                margin-left: 0;
                margin-bottom: 20px;
                order: -1;
            }
            
            .cv-header-content {
                width: 100%;
            }
        }
    </style>
</head>
<body>
    <div class="container">
        <header>
            <h1><i class="fas fa-file-alt"></i> Creador de Currículum Vitae</h1>
            <p class="subtitle">Completa el formulario y genera tu CV profesional en minutos</p>
        </header>
        
        <div class="app-container">
            <!-- Sección del formulario -->
            <section class="form-section">
                <h2><i class="fas fa-edit"></i> Información Personal</h2>
                
                <!-- Subida de foto -->
                <div class="form-group">
                    <label>Foto de Perfil (Opcional)</label>
                    <div class="photo-upload-container">
                        <div class="photo-preview" id="photo-preview">
                            <div class="photo-placeholder" id="photo-placeholder">
                                <i class="fas fa-user-circle"></i>
                                <span>Sin foto</span>
                            </div>
                            <img id="photo-preview-img" style="display: none;">
                        </div>
                        
                        <div class="photo-actions">
                            <button class="btn btn-secondary photo-upload-btn" id="upload-photo-btn">
                                <i class="fas fa-upload"></i> Subir Foto
                                <input type="file" id="photo-input" accept="image/*" style="display: none;">
                            </button>
                            <button class="btn btn-danger" id="remove-photo-btn" style="display: none;">
                                <i class="fas fa-trash"></i> Eliminar
                            </button>
                        </div>
                        
                        <div class="photo-instructions">
                            <p><strong>Opcional:</strong> JPG o PNG, máximo 2MB</p>
                            <p>Con foto: CV moderno | Sin foto: CV clásico</p>
                        </div>
                    </div>
                </div>
                
                <div class="form-group">
                    <label for="name">Nombre Completo</label>
                    <input type="text" id="name" placeholder="Ej: Juan Pérez González">
                </div>
                
                <div class="form-group">
                    <label for="title">Título Profesional</label>
                    <input type="text" id="title" placeholder="Ej: Desarrollador Web Frontend">
                </div>
                
                <div class="form-group">
                    <label for="email">Correo Electrónico</label>
                    <input type="email" id="email" placeholder="Ej: juan.perez@email.com">
                </div>
                
                <div class="form-group">
                    <label for="phone">Teléfono</label>
                    <input type="text" id="phone" placeholder="Ej: +34 612 345 678">
                </div>
                
                <div class="form-group">
                    <label for="location">Ubicación</label>
                    <input type="text" id="location" placeholder="Ej: Madrid, España">
                </div>
                
                <div class="form-group">
                    <label for="summary">Resumen Profesional</label>
                    <textarea id="summary" placeholder="Breve descripción de tu experiencia, habilidades y objetivos profesionales..."></textarea>
                </div>
                
                <h2><i class="fas fa-briefcase"></i> Experiencia Laboral</h2>
                <div id="jobs-container">
                    <!-- Las experiencias laborales se agregarán aquí dinámicamente -->
                </div>
                
                <div class="form-group">
                    <button class="btn btn-secondary add-job-btn" id="add-job-btn">
                        <i class="fas fa-plus"></i> Agregar Experiencia Laboral
                    </button>
                </div>
                
                <h2><i class="fas fa-graduation-cap"></i> Educación</h2>
                
                <div class="form-group">
                    <label for="education1-degree">Título Académico</label>
                    <input type="text" id="education1-degree" placeholder="Ej: Grado en Ingeniería Informática">
                </div>
                
                <div class="form-group">
                    <label for="education1-school">Institución</label>
                    <input type="text" id="education1-school" placeholder="Ej: Universidad Complutense de Madrid">
                </div>
                
                <div class="form-group">
                    <label for="education1-dates">Fechas</label>
                    <input type="text" id="education1-dates" placeholder="Ej: 2016 - 2020">
                </div>
                
                <h2><i class="fas fa-star"></i> Habilidades</h2>
                
                <div class="form-group">
                    <div class="skills-container" id="skills-container">
                        <!-- Las habilidades se agregarán aquí dinámicamente -->
                    </div>
                    <div class="add-skill">
                        <input type="text" id="new-skill" placeholder="Ej: JavaScript, React, HTML/CSS">
                        <button class="btn btn-sm" id="add-skill-btn">Agregar</button>
                    </div>
                </div>
                
                <button class="btn btn-primary btn-block" id="generate-pdf">
                    <i class="fas fa-download"></i> Descargar CV en PDF
                </button>
            </section>
            
            <!-- Sección de vista previa -->
            <section class="preview-section">
                <h2><i class="fas fa-eye"></i> Vista Previa del CV</h2>
                
                <div class="cv-preview" id="cv-preview">
                    <!-- El encabezado se actualizará dinámicamente según si hay foto o no -->
                    <div id="cv-header-container">
                        <!-- Se llenará dinámicamente con JavaScript -->
                    </div>
                    
                    <div class="cv-section">
                        <div class="cv-section-title">Resumen Profesional</div>
                        <div id="cv-summary">Breve descripción de tu experiencia, habilidades y objetivos profesionales.</div>
                    </div>
                    
                    <div class="cv-section">
                        <div class="cv-section-title">Experiencia Laboral</div>
                        <div id="cv-jobs-container">
                            <!-- Las experiencias laborales se mostrarán aquí -->
                            <div class="cv-item">
                                <div class="cv-item-title">Puesto de Trabajo</div>
                                <div class="cv-item-subtitle">Empresa</div>
                                <div class="cv-item-dates">
                                    Fechas
                                    <span class="current-badge">Actual</span>
                                </div>
                                <div>Descripción de responsabilidades y logros en este puesto.</div>
                            </div>
                        </div>
                    </div>
                    
                    <div class="cv-section">
                        <div class="cv-section-title">Educación</div>
                        <div class="cv-item">
                            <div class="cv-item-title" id="cv-education1-degree">Título Académico</div>
                            <div class="cv-item-subtitle" id="cv-education1-school">Institución</div>
                            <div class="cv-item-dates" id="cv-education1-dates">Fechas</div>
                        </div>
                    </div>
                    
                    <div class="cv-section">
                        <div class="cv-section-title">Habilidades</div>
                        <div class="cv-skills" id="cv-skills">
                            <div class="cv-skill">Ejemplo de habilidad</div>
                            <div class="cv-skill">Otra habilidad</div>
                            <div class="cv-skill">Habilidad técnica</div>
                        </div>
                    </div>
                </div>
                
                <div class="actions">
                    <button class="btn" id="reset-form">
                        <i class="fas fa-redo"></i> Reiniciar Formulario
                    </button>
                    <button class="btn" id="print-cv">
                        <i class="fas fa-print"></i> Imprimir CV
                    </button>
                </div>
                
                <div class="instructions">
                    <h3>Instrucciones:</h3>
                    <ul>
                        <li><strong>Foto opcional:</strong> Con foto tendrás un CV moderno, sin foto un CV clásico.</li>
                        <li>Completa todos los campos del formulario.</li>
                        <li>Agrega tus experiencias laborales con el botón "Agregar Experiencia Laboral".</li>
                        <li>Marca como "Trabajo Actual" si es tu empleo actual.</li>
                        <li>La vista previa se actualizará automáticamente.</li>
                        <li>Agrega tus habilidades una por una.</li>
                        <li>Haz clic en "Descargar CV en PDF" para guardar tu currículum.</li>
                        <li>Usa "Imprimir CV" si prefieres imprimirlo directamente.</li>
                    </ul>
                </div>
            </section>
        </div>
    </div>

    <!-- Incluir html2pdf -->
    <script src="https://cdnjs.cloudflare.com/ajax/libs/html2pdf.js/0.10.1/html2pdf.bundle.min.js"></script>
    
    <script>
        // Elementos del formulario
        const formInputs = {
            name: document.getElementById('name'),
            title: document.getElementById('title'),
            email: document.getElementById('email'),
            phone: document.getElementById('phone'),
            location: document.getElementById('location'),
            summary: document.getElementById('summary'),
            education1Degree: document.getElementById('education1-degree'),
            education1School: document.getElementById('education1-school'),
            education1Dates: document.getElementById('education1-dates'),
            newSkill: document.getElementById('new-skill')
        };
        
        // Elementos de vista previa
        const previewElements = {
            name: document.getElementById('cv-name'),
            title: document.getElementById('cv-title'),
            email: document.getElementById('cv-email'),
            phone: document.getElementById('cv-phone'),
            location: document.getElementById('cv-location'),
            summary: document.getElementById('cv-summary'),
            education1Degree: document.getElementById('cv-education1-degree'),
            education1School: document.getElementById('cv-education1-school'),
            education1Dates: document.getElementById('cv-education1-dates'),
            skills: document.getElementById('cv-skills'),
            jobsContainer: document.getElementById('cv-jobs-container'),
            headerContainer: document.getElementById('cv-header-container')
        };
        
        // Elementos de foto
        const photoInput = document.getElementById('photo-input');
        const photoPreview = document.getElementById('photo-preview-img');
        const photoPlaceholder = document.getElementById('photo-placeholder');
        const uploadPhotoBtn = document.getElementById('upload-photo-btn');
        const removePhotoBtn = document.getElementById('remove-photo-btn');
        
        // Elementos de habilidades
        const skillsContainer = document.getElementById('skills-container');
        const addSkillBtn = document.getElementById('add-skill-btn');
        const cvSkillsContainer = document.getElementById('cv-skills');
        
        // Elementos de experiencia laboral
        const jobsContainer = document.getElementById('jobs-container');
        const addJobBtn = document.getElementById('add-job-btn');
        
        // Botones de acción
        const generatePdfBtn = document.getElementById('generate-pdf');
        const resetFormBtn = document.getElementById('reset-form');
        const printCvBtn = document.getElementById('print-cv');
        
        // Almacenar habilidades
        let skills = ['JavaScript', 'HTML/CSS', 'React', 'Git'];
        
        // Almacenar experiencias laborales
        let jobs = [
            {
                id: 1,
                title: 'Desarrollador Frontend',
                company: 'Tech Solutions S.A.',
                dates: 'Enero 2020 - Presente',
                description: 'Desarrollo de aplicaciones web utilizando React, HTML5 y CSS3. Colaboración en equipos ágiles.',
                current: true
            },
            {
                id: 2,
                title: 'Desarrollador Web Junior',
                company: 'Digital Agency',
                dates: 'Junio 2018 - Diciembre 2019',
                description: 'Creación de sitios web responsivos y mantenimiento de sitios existentes.',
                current: false
            }
        ];
        
        // Almacenar foto (como Data URL)
        let userPhoto = null;
        
        // Función para actualizar el encabezado según si hay foto o no
        function updateCVHeader() {
            if (userPhoto) {
                // Con foto: usar diseño con foto
                previewElements.headerContainer.innerHTML = `
                    <div class="cv-header-with-photo">
                        <div class="cv-header-content">
                            <div class="cv-name">${formInputs.name.value || 'Nombre Completo'}</div>
                            <div class="cv-title">${formInputs.title.value || 'Título Profesional'}</div>
                            <div class="cv-contact">
                                <div class="cv-contact-item">
                                    <i class="fas fa-envelope"></i> ${formInputs.email.value || 'correo@ejemplo.com'}
                                </div>
                                <div class="cv-contact-item">
                                    <i class="fas fa-phone"></i> ${formInputs.phone.value || '+34 612 345 678'}
                                </div>
                                <div class="cv-contact-item">
                                    <i class="fas fa-map-marker-alt"></i> ${formInputs.location.value || 'Ciudad, País'}
                                </div>
                            </div>
                        </div>
                        
                        <div class="cv-photo-container">
                            <img src="${userPhoto}" class="cv-photo">
                        </div>
                    </div>
                `;
            } else {
                // Sin foto: usar diseño clásico (como la versión anterior)
                previewElements.headerContainer.innerHTML = `
                    <div class="cv-header-no-photo">
                        <div class="cv-name">${formInputs.name.value || 'Nombre Completo'}</div>
                        <div class="cv-title">${formInputs.title.value || 'Título Profesional'}</div>
                        <div class="cv-contact">
                            <div class="cv-contact-item">
                                <i class="fas fa-envelope"></i> ${formInputs.email.value || 'correo@ejemplo.com'}
                            </div>
                            <div class="cv-contact-item">
                                <i class="fas fa-phone"></i> ${formInputs.phone.value || '+34 612 345 678'}
                            </div>
                            <div class="cv-contact-item">
                                <i class="fas fa-map-marker-alt"></i> ${formInputs.location.value || 'Ciudad, País'}
                            </div>
                        </div>
                    </div>
                `;
            }
        }
        
        // Inicializar foto
        function initializePhoto() {
            // Agregar evento al botón de subida personalizado
            uploadPhotoBtn.addEventListener('click', function() {
                photoInput.click();
            });
            
            // Manejar selección de archivo
            photoInput.addEventListener('change', handlePhotoUpload);
            
            // Manejar eliminación de foto
            removePhotoBtn.addEventListener('click', removePhoto);
        }
        
        // Manejar subida de foto
        function handlePhotoUpload(event) {
            const file = event.target.files[0];
            if (!file) return;
            
            // Validar tipo de archivo
            if (!file.type.match('image.*')) {
                alert('Por favor, selecciona un archivo de imagen (JPG, PNG, etc.)');
                return;
            }
            
            // Validar tamaño (máximo 2MB)
            if (file.size > 2 * 1024 * 1024) {
                alert('La imagen es demasiado grande. El tamaño máximo es 2MB.');
                return;
            }
            
            const reader = new FileReader();
            
            reader.onload = function(e) {
                userPhoto = e.target.result;
                
                // Mostrar vista previa en el formulario
                photoPreview.src = userPhoto;
                photoPreview.style.display = 'block';
                photoPlaceholder.style.display = 'none';
                
                // Mostrar botón de eliminar
                removePhotoBtn.style.display = 'block';
                
                // Actualizar encabezado del CV
                updateCVHeader();
            };
            
            reader.readAsDataURL(file);
        }
        
        // Eliminar foto
        function removePhoto() {
            userPhoto = null;
            
            // Ocultar vista previa en el formulario
            photoPreview.style.display = 'none';
            photoPlaceholder.style.display = 'flex';
            
            // Ocultar botón de eliminar
            removePhotoBtn.style.display = 'none';
            
            // Limpiar input de archivo
            photoInput.value = '';
            
            // Actualizar encabezado del CV
            updateCVHeader();
        }
        
        // Inicializar experiencias laborales
        function initializeJobs() {
            jobsContainer.innerHTML = '';
            jobs.forEach(job => {
                addJobToForm(job);
            });
            updateJobsPreview();
        }
        
        // Agregar experiencia laboral al formulario
        function addJobToForm(job = null) {
            const jobId = job ? job.id : Date.now();
            const jobTitle = job ? job.title : '';
            const jobCompany = job ? job.company : '';
            const jobDates = job ? job.dates : '';
            const jobDescription = job ? job.description : '';
            const isCurrent = job ? job.current : false;
            
            const jobEntry = document.createElement('div');
            jobEntry.className = 'job-entry';
            jobEntry.id = `job-${jobId}`;
            
            jobEntry.innerHTML = `
                <div class="job-header">
                    <div class="job-title">Experiencia Laboral</div>
                    <button type="button" class="remove-job" data-job-id="${jobId}">
                        <i class="fas fa-times"></i>
                    </button>
                </div>
                
                <div class="current-job-toggle">
                    <span class="toggle-label">¿Es tu trabajo actual?</span>
                    <label class="toggle-switch">
                        <input type="checkbox" class="current-job-checkbox" ${isCurrent ? 'checked' : ''} data-job-id="${jobId}">
                        <span class="toggle-slider"></span>
                    </label>
                </div>
                
                <div class="form-group">
                    <label for="job-title-${jobId}">Puesto de Trabajo</label>
                    <input type="text" id="job-title-${jobId}" class="job-title-input" 
                           placeholder="Ej: Desarrollador Frontend" value="${jobTitle}">
                </div>
                
                <div class="form-group">
                    <label for="job-company-${jobId}">Empresa</label>
                    <input type="text" id="job-company-${jobId}" class="job-company-input" 
                           placeholder="Ej: Tech Solutions S.A." value="${jobCompany}">
                </div>
                
                <div class="form-group">
                    <label for="job-dates-${jobId}">Fechas</label>
                    <input type="text" id="job-dates-${jobId}" class="job-dates-input" 
                           placeholder="Ej: Enero 2020 - Presente" value="${jobDates}">
                </div>
                
                <div class="form-group">
                    <label for="job-description-${jobId}">Descripción</label>
                    <textarea id="job-description-${jobId}" class="job-description-input" 
                              placeholder="Describe tus responsabilidades y logros en este puesto...">${jobDescription}</textarea>
                </div>
            `;
            
            jobsContainer.appendChild(jobEntry);
            
            // Añadir eventos a los campos de este trabajo
            const titleInput = jobEntry.querySelector('.job-title-input');
            const companyInput = jobEntry.querySelector('.job-company-input');
            const datesInput = jobEntry.querySelector('.job-dates-input');
            const descriptionInput = jobEntry.querySelector('.job-description-input');
            const currentCheckbox = jobEntry.querySelector('.current-job-checkbox');
            const removeBtn = jobEntry.querySelector('.remove-job');
            
            // Actualizar el array de trabajos cuando cambien los inputs
            const updateJobInArray = () => {
                const jobIndex = jobs.findIndex(j => j.id === jobId);
                
                if (jobIndex === -1) {
                    // Nuevo trabajo
                    jobs.push({
                        id: jobId,
                        title: titleInput.value,
                        company: companyInput.value,
                        dates: datesInput.value,
                        description: descriptionInput.value,
                        current: currentCheckbox.checked
                    });
                } else {
                    // Actualizar trabajo existente
                    jobs[jobIndex] = {
                        ...jobs[jobIndex],
                        title: titleInput.value,
                        company: companyInput.value,
                        dates: datesInput.value,
                        description: descriptionInput.value,
                        current: currentCheckbox.checked
                    };
                }
                
                updateJobsPreview();
            };
            
            titleInput.addEventListener('input', updateJobInArray);
            companyInput.addEventListener('input', updateJobInArray);
            datesInput.addEventListener('input', updateJobInArray);
            descriptionInput.addEventListener('input', updateJobInArray);
            currentCheckbox.addEventListener('change', updateJobInArray);
            
            // Eliminar trabajo
            removeBtn.addEventListener('click', function() {
                if (jobs.length <= 1) {
                    alert('Debes tener al menos una experiencia laboral.');
                    return;
                }
                
                if (confirm('¿Estás seguro de que quieres eliminar esta experiencia laboral?')) {
                    jobs = jobs.filter(j => j.id !== jobId);
                    jobEntry.remove();
                    updateJobsPreview();
                }
            });
            
            // Si es un trabajo nuevo, agregarlo al array
            if (!job) {
                jobs.push({
                    id: jobId,
                    title: '',
                    company: '',
                    dates: '',
                    description: '',
                    current: false
                });
            }
        }
        
        // Actualizar vista previa de experiencias laborales
        function updateJobsPreview() {
            previewElements.jobsContainer.innerHTML = '';
            
            // Ordenar trabajos: primero los actuales, luego los anteriores
            const sortedJobs = [...jobs].sort((a, b) => {
                if (a.current && !b.current) return -1;
                if (!a.current && b.current) return 1;
                return 0;
            });
            
            sortedJobs.forEach(job => {
                if (job.title || job.company || job.dates || job.description) {
                    const jobElement = document.createElement('div');
                    jobElement.className = 'cv-item';
                    
                    jobElement.innerHTML = `
                        <div class="cv-item-title">${job.title || 'Puesto de Trabajo'}</div>
                        <div class="cv-item-subtitle">${job.company || 'Empresa'}</div>
                        <div class="cv-item-dates">
                            ${job.dates || 'Fechas'}
                            ${job.current ? '<span class="current-badge">Actual</span>' : ''}
                        </div>
                        <div>${job.description || 'Descripción de responsabilidades y logros en este puesto.'}</div>
                    `;
                    
                    previewElements.jobsContainer.appendChild(jobElement);
                }
            });
            
            // Si no hay trabajos, mostrar un placeholder
            if (previewElements.jobsContainer.children.length === 0) {
                const placeholder = document.createElement('div');
                placeholder.className = 'cv-item';
                placeholder.innerHTML = `
                    <div class="cv-item-title">Puesto de Trabajo</div>
                    <div class="cv-item-subtitle">Empresa</div>
                    <div class="cv-item-dates">
                        Fechas
                        <span class="current-badge">Actual</span>
                    </div>
                    <div>Descripción de responsabilidades y logros en este puesto.</div>
                `;
                previewElements.jobsContainer.appendChild(placeholder);
            }
        }
        
        // Inicializar habilidades
        function initializeSkills() {
            skills.forEach(skill => addSkillToContainers(skill));
        }
        
        // Agregar habilidad a ambos contenedores
        function addSkillToContainers(skill) {
            // Agregar al formulario
            const skillTag = document.createElement('div');
            skillTag.className = 'skill-tag';
            skillTag.innerHTML = `
                ${skill}
                <span class="remove-skill" data-skill="${skill}">×</span>
            `;
            skillsContainer.appendChild(skillTag);
            
            // Agregar a la vista previa
            const cvSkill = document.createElement('div');
            cvSkill.className = 'cv-skill';
            cvSkill.textContent = skill;
            cvSkillsContainer.appendChild(cvSkill);
            
            // Añadir evento para eliminar habilidad
            skillTag.querySelector('.remove-skill').addEventListener('click', function() {
                const skillToRemove = this.getAttribute('data-skill');
                skills = skills.filter(s => s !== skillToRemove);
                updateSkillsDisplay();
            });
        }
        
        // Actualizar visualización de habilidades
        function updateSkillsDisplay() {
            // Limpiar contenedores
            skillsContainer.innerHTML = '';
            cvSkillsContainer.innerHTML = '';
            
            // Volver a agregar todas las habilidades
            skills.forEach(skill => addSkillToContainers(skill));
        }
        
        // Actualizar vista previa en tiempo real
        function updatePreview() {
            // Información personal
            updateCVHeader(); // Actualizar encabezado según si hay foto o no
            
            previewElements.summary.textContent = formInputs.summary.value || 'Breve descripción de tu experiencia, habilidades y objetivos profesionales.';
            
            // Educación
            previewElements.education1Degree.textContent = formInputs.education1Degree.value || 'Título Académico';
            previewElements.education1School.textContent = formInputs.education1School.value || 'Institución';
            previewElements.education1Dates.textContent = formInputs.education1Dates.value || 'Fechas';
            
            // Experiencias laborales ya se actualizan por separado
        }
        
        // Añadir eventos de entrada a todos los campos del formulario
        Object.values(formInputs).forEach(input => {
            if (input && input.tagName !== 'BUTTON') {
                input.addEventListener('input', updatePreview);
            }
        });
        
        // Añadir nueva habilidad
        addSkillBtn.addEventListener('click', function() {
            const newSkill = formInputs.newSkill.value.trim();
            if (newSkill && !skills.includes(newSkill)) {
                skills.push(newSkill);
                updateSkillsDisplay();
                formInputs.newSkill.value = '';
            }
        });
        
        // Permitir agregar habilidad con Enter
        formInputs.newSkill.addEventListener('keypress', function(e) {
            if (e.key === 'Enter') {
                addSkillBtn.click();
            }
        });
        
        // Añadir nueva experiencia laboral
        addJobBtn.addEventListener('click', function() {
            addJobToForm();
        });
        
        // Crear CV limpio para PDF con pie de página personalizado
        function createCleanCVForPDF() {
            const cleanCV = document.createElement('div');
            cleanCV.className = 'cv-pdf-clean';
            
            // Ordenar trabajos: primero los actuales, luego los anteriores
            const sortedJobs = [...jobs].sort((a, b) => {
                if (a.current && !b.current) return -1;
                if (!a.current && b.current) return 1;
                return 0;
            });
            
            // Filtrar trabajos que tengan al menos un campo completado
            const filteredJobs = sortedJobs.filter(job => 
                job.title || job.company || job.dates || job.description
            );
            
            // Crear encabezado según si hay foto o no
            let headerHTML = '';
            if (userPhoto) {
                // Con foto: diseño moderno
                headerHTML = `
                    <div style="background-color: #2c3e50; color: white; padding: 30px; border-radius: 8px; margin-bottom: 30px; display: flex; justify-content: space-between; align-items: flex-start;">
                        <div style="flex: 1;">
                            <div style="font-size: 28pt; font-weight: bold; margin-bottom: 5px;">${formInputs.name.value || 'Nombre Completo'}</div>
                            <div style="font-size: 18pt; opacity: 0.9; margin-bottom: 15px;">${formInputs.title.value || 'Título Profesional'}</div>
                            <div style="display: flex; flex-wrap: wrap; gap: 20px; margin-top: 15px;">
                                <div style="display: flex; align-items: center; gap: 8px; font-size: 11pt;">
                                    <i class="fas fa-envelope"></i> ${formInputs.email.value || 'correo@ejemplo.com'}
                                </div>
                                <div style="display: flex; align-items: center; gap: 8px; font-size: 11pt;">
                                    <i class="fas fa-phone"></i> ${formInputs.phone.value || '+34 612 345 678'}
                                </div>
                                <div style="display: flex; align-items: center; gap: 8px; font-size: 11pt;">
                                    <i class="fas fa-map-marker-alt"></i> ${formInputs.location.value || 'Ciudad, País'}
                                </div>
                            </div>
                        </div>
                        
                        <div style="width: 120px; height: 150px; border-radius: 8px; overflow: hidden; border: 3px solid rgba(255, 255, 255, 0.3); margin-left: 20px; flex-shrink: 0;">
                            <img src="${userPhoto}" style="width: 100%; height: 100%; object-fit: cover;">
                        </div>
                    </div>
                `;
            } else {
                // Sin foto: diseño clásico (como versión anterior)
                headerHTML = `
                    <div style="background-color: #2c3e50; color: white; padding: 30px; border-radius: 8px; margin-bottom: 30px;">
                        <div style="font-size: 28pt; font-weight: bold; margin-bottom: 5px;">${formInputs.name.value || 'Nombre Completo'}</div>
                        <div style="font-size: 18pt; opacity: 0.9; margin-bottom: 15px;">${formInputs.title.value || 'Título Profesional'}</div>
                        <div style="display: flex; flex-wrap: wrap; gap: 20px; margin-top: 15px;">
                            <div style="display: flex; align-items: center; gap: 8px; font-size: 11pt;">
                                <i class="fas fa-envelope"></i> ${formInputs.email.value || 'correo@ejemplo.com'}
                            </div>
                            <div style="display: flex; align-items: center; gap: 8px; font-size: 11pt;">
                                <i class="fas fa-phone"></i> ${formInputs.phone.value || '+34 612 345 678'}
                            </div>
                            <div style="display: flex; align-items: center; gap: 8px; font-size: 11pt;">
                                <i class="fas fa-map-marker-alt"></i> ${formInputs.location.value || 'Ciudad, País'}
                            </div>
                        </div>
                    </div>
                `;
            }
            
            cleanCV.innerHTML = `
                ${headerHTML}
                
                <div class="cv-pdf-section">
                    <div class="cv-pdf-section-title">Resumen Profesional</div>
                    <div>${formInputs.summary.value || 'Breve descripción de tu experiencia, habilidades y objetivos profesionales.'}</div>
                </div>
                
                <div class="cv-pdf-section">
                    <div class="cv-pdf-section-title">Experiencia Laboral</div>
                    ${filteredJobs.map(job => `
                        <div class="cv-pdf-item">
                            <div class="cv-pdf-item-title">${job.title || 'Puesto de Trabajo'}</div>
                            <div class="cv-pdf-item-subtitle">${job.company || 'Empresa'}</div>
                            <div class="cv-pdf-item-dates">
                                ${job.dates || 'Fechas'}
                                ${job.current ? '<span style="background-color: #27ae60; color: white; padding: 2px 8px; border-radius: 12px; font-size: 0.8rem; margin-left: 8px;">Actual</span>' : ''}
                            </div>
                            <div>${job.description || 'Descripción de responsabilidades y logros en este puesto.'}</div>
                        </div>
                    `).join('')}
                </div>
                
                <div class="cv-pdf-section">
                    <div class="cv-pdf-section-title">Educación</div>
                    <div class="cv-pdf-item">
                        <div class="cv-pdf-item-title">${formInputs.education1Degree.value || 'Título Académico'}</div>
                        <div class="cv-pdf-item-subtitle">${formInputs.education1School.value || 'Institución'}</div>
                        <div class="cv-pdf-item-dates">${formInputs.education1Dates.value || 'Fechas'}</div>
                    </div>
                </div>
                
                <div class="cv-pdf-section">
                    <div class="cv-pdf-section-title">Habilidades</div>
                    <div class="cv-pdf-skills">
                        ${skills.map(skill => `<div class="cv-pdf-skill">${skill}</div>`).join('')}
                    </div>
                </div>
                
                <!-- Pie de página para el PDF -->
                <div class="pdf-footer">
                    <div class="page-number">Página <span class="page"></span> de <span class="total"></span></div>
                </div>
            `;
            
            // Agregar estilos CSS para el PDF
            const style = document.createElement('style');
            style.textContent = `
                .cv-pdf-clean {
                    width: 100%;
                    background-color: white;
                    padding: 40px;
                    font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
                    color: #333;
                    line-height: 1.6;
                    position: relative;
                    min-height: 100vh;
                }
                
                .cv-pdf-section {
                    margin-bottom: 25px;
                }
                
                .cv-pdf-section-title {
                    color: #2c3e50;
                    border-bottom: 2px solid #4a6491;
                    padding-bottom: 5px;
                    margin-bottom: 15px;
                    font-size: 16pt;
                    font-weight: bold;
                }
                
                .cv-pdf-item {
                    margin-bottom: 15px;
                    padding-bottom: 15px;
                    border-bottom: 1px solid #eee;
                }
                
                .cv-pdf-item:last-child {
                    border-bottom: none;
                }
                
                .cv-pdf-item-title {
                    font-weight: 600;
                    color: #333;
                    margin-bottom: 5px;
                    font-size: 12pt;
                }
                
                .cv-pdf-item-subtitle {
                    color: #4a6491;
                    font-style: italic;
                    margin-bottom: 5px;
                    font-size: 11pt;
                }
                
                .cv-pdf-item-dates {
                    color: #666;
                    font-size: 10pt;
                    margin-bottom: 8px;
                    display: flex;
                    align-items: center;
                }
                
                .cv-pdf-skills {
                    display: flex;
                    flex-wrap: wrap;
                    gap: 10px;
                    margin-bottom: 60px;
                }
                
                .cv-pdf-skill {
                    background-color: #eef2f7;
                    padding: 6px 12px;
                    border-radius: 4px;
                    font-size: 10pt;
                }
                
                .pdf-footer {
                    position: fixed;
                    bottom: 0;
                    left: 0;
                    right: 0;
                    text-align: center;
                    font-size: 10pt;
                    color: #666;
                    padding: 10px;
                    background-color: white;
                    border-top: 1px solid #eee;
                    height: 40px;
                }
                
                .page-number {
                    font-family: Arial, sans-serif;
                }
            `;
            
            cleanCV.appendChild(style);
            return cleanCV;
        }
        
        // Generar PDF limpio con numeración de páginas personalizada
        generatePdfBtn.addEventListener('click', async function() {
            const cleanCV = createCleanCVForPDF();
            
            // Opciones para html2pdf
            const options = {
                margin: [0.5, 0.8, 0.5, 0.8], // [top, right, bottom, left]
                filename: `CV_${formInputs.name.value || 'Mi_CV'}.pdf`,
                image: { type: 'jpeg', quality: 0.98 },
                html2canvas: { 
                    scale: 2,
                    useCORS: true,
                    logging: false,
                    windowWidth: 794,
                    windowHeight: 1123
                },
                jsPDF: { 
                    unit: 'mm', 
                    format: 'a4', 
                    orientation: 'portrait',
                    putTotalPages: '{total_pages_count_string}'
                },
                pagebreak: { mode: ['avoid-all', 'css', 'legacy'] }
            };
            
            // Configurar el pie de página con números de página
            const worker = html2pdf().set(options).from(cleanCV);
            
            worker.toPdf().get('pdf').then(function(pdf) {
                const totalPages = pdf.internal.getNumberOfPages();
                
                for (let i = 1; i <= totalPages; i++) {
                    pdf.setPage(i);
                    pdf.setFontSize(10);
                    pdf.setFont("helvetica", "normal");
                    pdf.text(`Página ${i} de ${totalPages}`, pdf.internal.pageSize.width / 2, 
                            pdf.internal.pageSize.height - 10, {align: 'center'});
                }
            }).save();
        });
        
        // Imprimir CV limpio
        printCvBtn.addEventListener('click', function() {
            const cleanCV = createCleanCVForPDF();
            
            // Crear ventana de impresión
            const printWindow = window.open('', '_blank');
            printWindow.document.write(`
                <!DOCTYPE html>
                <html>
                <head>
                    <title>Curriculum Vitae - ${formInputs.name.value || 'Mi CV'}</title>
                    <style>
                        body {
                            font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
                            color: #333;
                            line-height: 1.6;
                            margin: 0;
                            padding: 20px;
                        }
                        
                        .cv-pdf-header {
                            background-color: #2c3e50;
                            color: white;
                            padding: 30px;
                            border-radius: 8px;
                            margin-bottom: 30px;
                        }
                        
                        .cv-pdf-header-with-photo {
                            display: flex;
                            justify-content: space-between;
                            align-items: flex-start;
                        }
                        
                        .cv-pdf-name {
                            font-size: 28pt;
                            margin-bottom: 5px;
                            font-weight: bold;
                        }
                        
                        .cv-pdf-title {
                            font-size: 18pt;
                            opacity: 0.9;
                            margin-bottom: 15px;
                        }
                        
                        .cv-pdf-contact {
                            display: flex;
                            flex-wrap: wrap;
                            gap: 20px;
                            margin-top: 15px;
                        }
                        
                        .cv-pdf-contact-item {
                            display: flex;
                            align-items: center;
                            gap: 8px;
                            font-size: 11pt;
                        }
                        
                        .cv-photo-container {
                            width: 120px;
                            height: 150px;
                            border-radius: 8px;
                            overflow: hidden;
                            border: 3px solid rgba(255, 255, 255, 0.3);
                            margin-left: 20px;
                        }
                        
                        .cv-photo-container img {
                            width: 100%;
                            height: 100%;
                            object-fit: cover;
                        }
                        
                        .cv-pdf-section {
                            margin-bottom: 25px;
                        }
                        
                        .cv-pdf-section-title {
                            color: #2c3e50;
                            border-bottom: 2px solid #4a6491;
                            padding-bottom: 5px;
                            margin-bottom: 15px;
                            font-size: 16pt;
                            font-weight: bold;
                        }
                        
                        .cv-pdf-item {
                            margin-bottom: 15px;
                            padding-bottom: 15px;
                            border-bottom: 1px solid #eee;
                        }
                        
                        .cv-pdf-item:last-child {
                            border-bottom: none;
                        }
                        
                        .cv-pdf-item-title {
                            font-weight: 600;
                            color: #333;
                            margin-bottom: 5px;
                            font-size: 12pt;
                        }
                        
                        .cv-pdf-item-subtitle {
                            color: #4a6491;
                            font-style: italic;
                            margin-bottom: 5px;
                            font-size: 11pt;
                        }
                        
                        .cv-pdf-item-dates {
                            color: #666;
                            font-size: 10pt;
                            margin-bottom: 8px;
                            display: flex;
                            align-items: center;
                        }
                        
                        .cv-pdf-skills {
                            display: flex;
                            flex-wrap: wrap;
                            gap: 10px;
                        }
                        
                        .cv-pdf-skill {
                            background-color: #eef2f7;
                            padding: 6px 12px;
                            border-radius: 4px;
                            font-size: 10pt;
                        }
                        
                        @media print {
                            body {
                                padding: 0;
                            }
                            
                            @page {
                                margin: 20mm;
                                @bottom-center {
                                    content: "Página " counter(page) " de " counter(pages);
                                    font-family: Arial, sans-serif;
                                    font-size: 10pt;
                                    color: #666;
                                }
                            }
                        }
                    </style>
                </head>
                <body>
                    ${cleanCV.innerHTML}
                </body>
                </html>
            `);
            
            printWindow.document.close();
            printWindow.focus();
            
            setTimeout(() => {
                printWindow.print();
                setTimeout(() => {
                    printWindow.close();
                }, 500);
            }, 500);
        });
        
        // Reiniciar formulario
        resetFormBtn.addEventListener('click', function() {
            if (confirm('¿Estás seguro de que quieres reiniciar el formulario? Se perderán todos los datos.')) {
                // Limpiar campos del formulario
                Object.values(formInputs).forEach(input => {
                    if (input && input.tagName !== 'BUTTON') {
                        input.value = '';
                    }
                });
                
                // Restablecer habilidades
                skills = ['JavaScript', 'HTML/CSS', 'React', 'Git'];
                updateSkillsDisplay();
                
                // Restablecer experiencias laborales
                jobs = [
                    {
                        id: 1,
                        title: '',
                        company: '',
                        dates: '',
                        description: '',
                        current: false
                    }
                ];
                initializeJobs();
                
                // Eliminar foto si existe
                if (userPhoto) {
                    removePhoto();
                }
                
                // Actualizar vista previa
                updatePreview();
            }
        });
        
        // Datos de ejemplo para probar
        function loadSampleData() {
            // Solo cargar si no hay datos en el formulario
            if (!formInputs.name.value) {
                formInputs.name.value = 'María López García';
                formInputs.title.value = 'Desarrolladora Full Stack';
                formInputs.email.value = 'maria.lopez@email.com';
                formInputs.phone.value = '+34 634 567 890';
                formInputs.location.value = 'Barcelona, España';
                formInputs.summary.value = 'Desarrolladora con 5 años de experiencia en el desarrollo de aplicaciones web. Especializada en JavaScript, React y Node.js. Apasionada por crear soluciones eficientes y escalables.';
                formInputs.education1Degree.value = 'Grado en Ingeniería Informática';
                formInputs.education1School.value = 'Universidad Politécnica de Cataluña';
                formInputs.education1Dates.value = '2015 - 2019';
                
                // Actualizar vista previa
                updatePreview();
            }
        }
        
        // Inicializar la aplicación
        function init() {
            initializePhoto();
            initializeSkills();
            initializeJobs();
            updatePreview();
            loadSampleData();
        }
        
        // Iniciar cuando el DOM esté listo
        document.addEventListener('DOMContentLoaded', init);
    </script>
</body>
</html>
{
	"blocks": [
		{
			"type": "header",
			"text": {
				"type": "plain_text",
				"text": ":sunshine: :x-connect: Boost Days: What's on this week :x-connect: :sunshine:"
			}
		},
		{
			"type": "section",
			"text": {
				"type": "mrkdwn",
				"text": "Good morning Melbourne and hope you all had a fab weekend! :sunshine: \n\n Please see below for what's on this week! "
			}
		},
		{
			"type": "divider"
		},
		{
			"type": "header",
			"text": {
				"type": "plain_text",
				"text": ":calendar-date-21: Wednesday, 21st January",
				"emoji": true
			}
		},
		{
			"type": "section",
			"text": {
				"type": "mrkdwn",
				"text": "\n:coffee:  :donutss: *Xero Café* – A selection of Donuts \n :coffee:*Barista Special* – Rod Laver Latte :tennis: \n :spaghetti:Join us at *12.00pm* for some * Delicious Pasta from Pasta Prego* in the Wominjeka Breakout Space on Level 3. Check out the:thread:"
			}
		},
		{
			"type": "header",
			"text": {
				"type": "plain_text",
				"text": ":calendar-date-22: Thursday, 22nd January",
				"emoji": true
			}
		},
		{
			"type": "section",
			"text": {
				"type": "mrkdwn",
				"text": ":coffee: *Xero Cafe*: A selection of Donuts \n :coffee: *Barista Special* – Rod Laver Latte \n :Breakfast: Join us at *8.30am -10.30am* for a * Breakfast Buffet* in the Wominjeka Breakout Space in the Level 3 breakout space."
			}
		},
		{
			"type": "divider"
		},
		{
			"type": "section",
			"text": {
				"type": "mrkdwn",
				"text": " What else? :heart:  \n\nStay tuned to this channel, and make sure you’re subscribed to the <https://calendar.google.com/calendar/u/0/r?cid=Y19xczkyMjk5ZGlsODJzMjA4aGt1b3RnM2t1MEBncm91cC5jYWxlbmRhci5nb29nbGUuY29t /|*Melbourne Social Calendar*> for all upcoming events. \n\n :tennis: :pingpong: In celebration of the Australian Open, On *Friday 30th January*, we are inviting 20 teams to compete in our own Xero Melbourne Open :melbourne:. Yes, we are hosting a Ping Pong competition during our Social at 4pm in the Wominjeka Breakout Space. Please sign up in the :thread: for a chance to win some official AO merchandise. \n\n DON'T MISS OUT! :yay:\t\t"
			}
		}
	]
}
 // page view counter
        function incrementPageViewCount() {
            const key = "page_view_count";
            const count = Number(sessionStorage.getItem(key)) || 0;
            const newCount = count + 1;
            sessionStorage.setItem(key, newCount);
            return newCount;
        }
import $, { contains } from 'jquery';

class Property {
  constructor(element) {
    this.$element = $(element);
    this.$tabs = this.$element.find(".property--tab li");
    this.$tabsContent = this.$element.find(".property--main-row");
    this.init();
  }

  init() {
    this.$tabs.first().addClass("active");
    this.$tabsContent.hide();
    this.$tabsContent.first().show();

    this.$tabs.click((e) => {
      e.preventDefault();
      if ($(e.currentTarget).hasClass("active")) {
        return;
      }
      this.$tabs.removeClass("active");
      $(e.currentTarget).addClass("active");
      this.$tabsContent.hide();

      var activeTab = $(e.currentTarget).find("a").attr("href");
      $(activeTab).fadeIn(700);
      return false;
    });
  }
}

$('[data-property]').each((index, element) => new Property(element));
A reliable approach is to work with a development team that delivers cryptocurrency exchange software built for security, speed, and operational stability. The right solution should support centralized, decentralized, or hybrid exchange models and adapt to specific business and market requirements. Features such as robust security controls, fast transaction processing, real-time updates, and comprehensive management tools are essential for serving both beginner and professional traders. Softean’s cryptocurrency exchange development follow this approach, enabling businesses to scale confidently while maintaining trust, performance, and long-term reliability >> https://www.softean.com/cryptocurrency-exchange-development  
function getRandomChar(charset: string) {
  const index = Math.floor(Math.random() * charset.length);
  return charset[index] ?? "";
}

function generateRandomString(length: number, charset: string) {
  let result = "";
  for (let i = 0; i < length; i += 1) {
    result += getRandomChar(charset);
  }
  return result;
}
// px -> rem 转换
const pxToRem = (px: number, base: number) => {
  if (base === 0) return "0";
  return (px / base).toFixed(4).replace(/\.?0+$/, "");
};

// rem -> px 转换
const remToPx = (rem: number, base: number) => {
  return (rem * base).toFixed(2).replace(/\.?0+$/, "");
};
Within the vast body of industry, where structures stand on the shoulders of strength and machinery operates on the foundation of reliability, small components play a key role that might go unnoticed at first glance. Among these silent heroes, the Grade 8.8 bolt holds a lofty and unparalleled position. This name, familiar in the Iranian market vernacular, in fact narrates a story of steel, carbon, and tireless resistance. But the fundamental question is: What differentiates this seemingly simple piece from ordinary bolts? Why has purchasing  <a href="https://screw.full-design.com/grade-8-8-bolt-the-steel-elixir-in-the-fastening-industry-81814969" onclick="window.open(this.href, '', 'resizable=no,status=no,location=no,toolbar=no,menubar=no,fullscreen=no,scrollbars=no,dependent=no'); return false;">Grade 8.8 bolts</a>
{
	"blocks": [
		{
			"type": "header",
			"text": {
				"type": "plain_text",
				"text": ":sunshine: :x-connect: Boost Days: What's on this week :x-connect: :sunshine:"
			}
		},
		{
			"type": "section",
			"text": {
				"type": "mrkdwn",
				"text": "Good morning Brisbane and hope you all had a fab weekend! :sunshine: \n\n Please see below for what's on this week! :yay: Due to the low attendance last week in the office, we have changes to the boost program this week. "
			}
		},
		{
			"type": "divider"
		},
		{
			"type": "header",
			"text": {
				"type": "plain_text",
				"text": ":calendar-date-12: Monday, 12th January",
				"emoji": true
			}
		},
		{
			"type": "section",
			"text": {
				"type": "mrkdwn",
				"text": "\n:coffee: *Café Partnership*: Enjoy free coffee and café-style beverages from our Cafe partner *Industry Beans*.\n\n :croissant: Morning tea from 9am in the kitchen. \n:Lunch: Delicious *Greenstreat salads* provided in the kitchen from *12pm* in the kitchen."
			}
		},
		{
			"type": "header",
			"text": {
				"type": "plain_text",
				"text": ":calendar-date-14: Wednesday, 14th January",
				"emoji": true
			}
		},
		{
			"type": "section",
			"text": {
				"type": "mrkdwn",
				"text": ":coffee: *Café Partnership*: Enjoy free coffee and café-style beverages from our Cafe partner *Industry Beans*. \n\n:lunch: *Morning Tea*:from *9am* in the kitchen!"
			}
		},
		{
			"type": "header",
			"text": {
				"type": "plain_text",
				"text": ":calendar-date-16:Friday, 16th January ",
				"emoji": true
			}
		},
		{
			"type": "section",
			"text": {
				"type": "mrkdwn",
				"text": ":cheers-9743: :cheese: *Happy Hour & Happy Friday:* from 3pm - 4pm in the kitchen! Wind down for the week over some drinks and nibbles."
			}
		},
		{
			"type": "divider"
		},
		{
			"type": "section",
			"text": {
				"type": "mrkdwn",
				"text": "Stay tuned to this channel for more details, check out the <https://calendar.google.com/calendar/u/0?cid=Y19uY2M4cDN1NDRsdTdhczE0MDhvYjZhNnRjb0Bncm91cC5jYWxlbmRhci5nb29nbGUuY29t|*Brisbane Social Calendar*>, and get ready to Boost your workdays!\n\nLove,\nWX Team :party-wx:"
			}
		}
	]
}
{
	"blocks": [
		{
			"type": "header",
			"text": {
				"type": "plain_text",
				"text": ":sunshine: :x-connect: Boost Days: What's on this week :x-connect: :sunshine:"
			}
		},
		{
			"type": "section",
			"text": {
				"type": "mrkdwn",
				"text": "Good morning Melbourne and hope you all had a fab weekend! :sunshine: \n\n Please see below for what's on this week! "
			}
		},
		{
			"type": "divider"
		},
		{
			"type": "header",
			"text": {
				"type": "plain_text",
				"text": ":calendar-date-14: Wednesday, 14th January",
				"emoji": true
			}
		},
		{
			"type": "section",
			"text": {
				"type": "mrkdwn",
				"text": "\n:coffee: :strawberry: :muffin: *Xero Café* – Cookies \n :coffee::gingerman: *Barista Special* – Lavender iced Latte \n :Lunch:Join us at *12.00pm* for some Mexican in the Wominjeka Breakout Space on Level 3. Check out the :thread:"
			}
		},
		{
			"type": "header",
			"text": {
				"type": "plain_text",
				"text": ":calendar-date-15: Thursday, 15th January",
				"emoji": true
			}
		},
		{
			"type": "section",
			"text": {
				"type": "mrkdwn",
				"text": ":coffee: *Xero Cafe*: Cookies \n :coffee: *Barista Special* – Lavender iced latte \n :Breakfast: Join us at *8.30am -10.30am* for a * Breakfast Buffet* in the Wominjeka Breakout Space in the Level 3 breakout space. \n :pizza::party: Join us at *4.00pm* in the Level 3 breakout space for some cheese and drinks"
			}
		},
		{
			"type": "divider"
		},
		{
			"type": "section",
			"text": {
				"type": "mrkdwn",
				"text": " What else?  \n\nStay tuned to this channel, and make sure you’re subscribed to the <https://calendar.google.com/calendar/u/0/r?cid=Y19xczkyMjk5ZGlsODJzMjA4aGt1b3RnM2t1MEBncm91cC5jYWxlbmRhci5nb29nbGUuY29t /|*Melbourne Social Calendar*> for all upcoming events."
			}
		}
	]
}
function footerNavTracking() {
        const footerWrapper = document.querySelector(".footer__wrapper");
        if (!footerWrapper) return;

        footerWrapper.addEventListener("click", (e) => {
            // Find the closest relevant link
            const link = e.target.closest(
                ".footer__col-second-button_wrapper a, .footer__logo-link",
            );
            if (!link) return;

            e.preventDefault();

            // Get the URL from the <a> tag
            const url = link.getAttribute("href") || "";

            // Social links
            if (link.matches(".footer__col-second-button_wrapper a")) {
                // Get the visible text (after the span/SVG)
                const linkTextRaw = link.lastChild.textContent.trim();
                const linkText = linkTextRaw.replace(/\s+/g, " ").trim() || "";

                gtmPush({
                    event: "navigation_click",
                    navigation_type: "footer_nav",
                    click_text: linkText,
                    click_url: url,
                    level: 1,
                });
            }

            // Logo links
            if (link.matches(".footer__logo-link")) {
                const linkTextRaw =
                    link.querySelector("svg title")?.textContent || "";
                const linkText = linkTextRaw.replace(/\s+/g, " ").trim() || "";
                const url = link.getAttribute("href") || "";

                gtmPush({
                    event: "navigation_click",
                    navigation_type: "footer_nav",
                    click_text: linkText,
                    click_url: url,
                    level: 1,
                });
            }
        });
    }
function navigationTracking() {
        const mainNav = document.querySelector(".main-navigation__main");
        if (!mainNav) return;

        const navItems = [
            {
                selector: ".main-navigation__control-link",
                type: "nav-links",
                handler: (e, el) => {
                    const btnText = el.textContent.trim() || "";
                    const isPressed =
                        el.getAttribute("aria-pressed") === "true";

                    navigationClickTracking(btnText, null, 1, isPressed);
                },
            },
            {
                selector: "#main-nav-bookmarks",
                type: "nav-bookmark",
                handler: (e, el) => {
                    const btnText = el.innerText.trim();
                    navigationClickTracking(btnText, "/sruh/bookmarks", 1);
                },
            },
            {
                selector: "#main-nav-close",
                type: "nav-search",
                handler: (e, el) => {
                    const btnText = el.outerText.trim();
                    navigationClickTracking(btnText, null, 1);
                },
            },
            {
                selector: "#main-nav-quiz",
                type: "nav-quiz",
                handler: (e, el) => {
                    const cleanText = el.textContent
                        .replace(/\s+/g, " ")
                        .trim();

                    navigationClickTracking(cleanText, "/sruh/quiz", 1);
                },
            },
        ];

        navItems.forEach((item) => {
            const elements = mainNav.querySelectorAll(item.selector);

            elements.forEach((el) => {
                // ✅ prevent duplicate listeners
                if (el.dataset.navTracked) return;
                el.dataset.navTracked = "true";

                el.addEventListener("click", (e) => {
                    e.preventDefault();
                    item.handler(e, el);
                });
            });
        });

        /*  SUB MENU LINK TRACKING */

        const subNavButtons = document.querySelectorAll(
            ".main-navigation__control-link",
        );
        if (!subNavButtons.length) return;

        subNavButtons.forEach((button) => {
            // prevent duplicate listeners
            if (button.dataset.subNavTracked) return;
            button.dataset.subNavTracked = "true";

            button.addEventListener("click", (e) => {
                e.preventDefault();

                const isActive = button.classList.contains("active");
                if (!isActive) return;

                const subNavWrapper = document.querySelector(
                    ".main-navigation__aside",
                );
                if (!subNavWrapper) return;

                const contentMenus = subNavWrapper.querySelectorAll(
                    ".main-navigation__content",
                );

                // wait for active class toggle
                setTimeout(() => {
                    contentMenus.forEach((menu) => {
                        if (!menu.classList.contains("active")) return;

                        const links = menu.querySelectorAll(
                            ".main-navigation__content-list li a",
                        );

                        links.forEach((link) => {
                            // ✅ prevent duplicate listeners
                            if (link.dataset.subNavLinkTracked) return;
                            link.dataset.subNavLinkTracked = "true";

                            link.addEventListener("click", (e) => {
                                e.preventDefault();

                                const linkText = link.textContent
                                    .replace(/\s+/g, " ")
                                    .trim();

                                const linkUrl = link.getAttribute("href") || "";

                                navigationClickTracking(linkText, linkUrl, 2);
                            });
                        });
                    });
                }, 100);
            });
        });
    }
A P2P crypto exchange allows users to trade directly with each other in a secure and transparent environment. At Beleaf Technologies, we make this process simple and reliable by building user-friendly P2P exchange platforms designed around real trading needs. Our solutions include trusted escrow systems, multiple payment options, and strong security measures to protect every transaction. Whether you’re a startup or a growing business, Beleaf Technologies supports you from idea to launch, helping you create a scalable P2P exchange that builds trust and grows with your users.

Visit Our Website >> https://beleaftechnologies.com/p2p-cryptocurrency-exchange-development-company

Reach Now 

Whatsapp : +91 8056786622

Email id : business@beleaftechnologies.com 


star

Fri Jan 23 2026 05:14:21 GMT+0000 (Coordinated Universal Time)

@@ankita123

star

Fri Jan 23 2026 05:06:59 GMT+0000 (Coordinated Universal Time)

@@ankita123

star

Thu Jan 22 2026 23:32:55 GMT+0000 (Coordinated Universal Time)

@nofil

star

Thu Jan 22 2026 21:34:59 GMT+0000 (Coordinated Universal Time) https://wedevs.com/blog/105501/disable-woocommerce-variable-product-price/

@EssquePro

star

Thu Jan 22 2026 15:50:27 GMT+0000 (Coordinated Universal Time) https://mergecsv.org

@SoulDee #typescript

star

Thu Jan 22 2026 10:28:49 GMT+0000 (Coordinated Universal Time)

@tanuja123

star

Thu Jan 22 2026 07:01:29 GMT+0000 (Coordinated Universal Time)

@Saravana_Kumar #python

star

Thu Jan 22 2026 02:46:30 GMT+0000 (Coordinated Universal Time)

@FOHWellington

star

Wed Jan 21 2026 14:40:04 GMT+0000 (Coordinated Universal Time)

@jrg_300i #javascript

star

Wed Jan 21 2026 07:27:14 GMT+0000 (Coordinated Universal Time) https://www.dappsfirm.com/pragmatic-play-clone-script

@TimDavid16 #pragmaticplayclonescript

star

Wed Jan 21 2026 03:22:42 GMT+0000 (Coordinated Universal Time)

@davidmchale #dataset #prevent #duplicate #event #listener

star

Wed Jan 21 2026 00:06:47 GMT+0000 (Coordinated Universal Time) https://curc.readthedocs.io/en/latest/programming/OpenMP-C.html

@luisjdominguezp

star

Wed Jan 21 2026 00:02:40 GMT+0000 (Coordinated Universal Time) https://curc.readthedocs.io/en/latest/programming/OpenMP-C.html

@luisjdominguezp

star

Tue Jan 20 2026 07:17:14 GMT+0000 (Coordinated Universal Time)

@usman13

star

Tue Jan 20 2026 03:36:44 GMT+0000 (Coordinated Universal Time)

@davidmchale #toggle #aria-label

star

Mon Jan 19 2026 12:14:31 GMT+0000 (Coordinated Universal Time) https://www.blockintelligence.io/Blockchain-Development

@Zarafernandes #trustedtechnology #blockchainbusiness #blockchainexperts #blockchain #blockchaininnovation

star

Mon Jan 19 2026 11:38:07 GMT+0000 (Coordinated Universal Time) https://www.thecryptoape.com/poloniex-clone-script

@Davidbrevis

star

Mon Jan 19 2026 07:24:06 GMT+0000 (Coordinated Universal Time) https://indextts-2.online/

@SoulDee

star

Mon Jan 19 2026 07:22:22 GMT+0000 (Coordinated Universal Time) https://parquettocsv.online

@SoulDee

star

Mon Jan 19 2026 02:20:47 GMT+0000 (Coordinated Universal Time)

@FOHWellington

star

Sun Jan 18 2026 22:55:00 GMT+0000 (Coordinated Universal Time)

@nurud43

star

Sun Jan 18 2026 22:52:56 GMT+0000 (Coordinated Universal Time)

@nurud43

star

Sun Jan 18 2026 22:48:58 GMT+0000 (Coordinated Universal Time)

@nurud43

star

Fri Jan 16 2026 19:38:22 GMT+0000 (Coordinated Universal Time)

@1234_5

star

Fri Jan 16 2026 08:19:25 GMT+0000 (Coordinated Universal Time) https://www.nativeassignmenthelp.co.uk/

@jamesstone

star

Fri Jan 16 2026 07:48:22 GMT+0000 (Coordinated Universal Time)

@2late #excel

star

Thu Jan 15 2026 03:59:56 GMT+0000 (Coordinated Universal Time)

@davidmchale #closest

star

Thu Jan 15 2026 03:58:28 GMT+0000 (Coordinated Universal Time)

@davidmchale #preventdefault #testing

star

Thu Jan 15 2026 03:56:34 GMT+0000 (Coordinated Universal Time)

@davidmchale #preventdefault #testing

star

Wed Jan 14 2026 16:15:27 GMT+0000 (Coordinated Universal Time)

@jrg_300i #javascript

star

Wed Jan 14 2026 06:36:01 GMT+0000 (Coordinated Universal Time) https://www.coinsclone.com/top-centralized-exchange-clone-scripts/

@Emmawoods

star

Wed Jan 14 2026 06:13:23 GMT+0000 (Coordinated Universal Time) https://medium.com/cryptocurrency-scripts/how-does-opensea-make-money-f443b0bc8794

@LilianAnderson #opensearevenue #nftmarketplacegrowth #howopenseamakesmoney #nftbusinessmodel #openseamarketvalue

star

Wed Jan 14 2026 03:40:17 GMT+0000 (Coordinated Universal Time)

@FOHWellington

star

Tue Jan 13 2026 23:36:38 GMT+0000 (Coordinated Universal Time)

@davidmchale #sessionstorage

star

Tue Jan 13 2026 13:26:20 GMT+0000 (Coordinated Universal Time)

@divyasoni23 #javascript #jquery

star

Mon Jan 12 2026 05:59:18 GMT+0000 (Coordinated Universal Time) https://www.softean.com/cryptocurrency-exchange-development

@Amybonbo

star

Sun Jan 11 2026 15:13:56 GMT+0000 (Coordinated Universal Time) https://stringart.online/

@SoulDee

star

Sun Jan 11 2026 15:13:03 GMT+0000 (Coordinated Universal Time) https://stringart.online/random-string/

@SoulDee #typescript

star

Sun Jan 11 2026 15:08:37 GMT+0000 (Coordinated Universal Time) https://px2rem.com/

@SoulDee

star

Sun Jan 11 2026 15:03:15 GMT+0000 (Coordinated Universal Time) https://px2rem.com/

@SoulDee #typescript

star

Thu Jan 08 2026 13:08:51 GMT+0000 (Coordinated Universal Time) https://screw.full-design.com/grade-8-8-bolt-the-steel-elixir-in-the-fastening-industry-81814969

@hamilton222

star

Thu Jan 08 2026 00:27:13 GMT+0000 (Coordinated Universal Time)

@FOHWellington

star

Thu Jan 08 2026 00:09:04 GMT+0000 (Coordinated Universal Time)

@FOHWellington

star

Wed Jan 07 2026 05:48:40 GMT+0000 (Coordinated Universal Time)

@davidmchale #matches() #click #event

star

Wed Jan 07 2026 05:46:55 GMT+0000 (Coordinated Universal Time)

@davidmchale #map #selectors #tracking

star

Tue Jan 06 2026 08:02:30 GMT+0000 (Coordinated Universal Time) https://www.thecryptoape.com/pancakeswap-clone-script

@nancywheeler

star

Tue Jan 06 2026 05:27:03 GMT+0000 (Coordinated Universal Time) https://www.nextwisi.com/blockchain-development-company-germany

@nextwisi #blockchain

Save snippets that work with our extensions

Available in the Chrome Web Store Get Firefox Add-on Get VS Code extension