Snippets Collections
###Inicializacion

git init 			 			#inicializa un nuevo repositorio
git clone <repo-url> 			#clona desde una url

###desarrollo diario
git status 			 			#Muestra el estado de la rama
git add <archivo>	 			#a~adir cambios al area de preparacion
git commit -m "mensaje"			#confirma los cambios con un mensaje

###Gestion de ramas
git branch						#lista las ramas
git branch <nombre-rama>		#crea una rama nueva
git switch <nombre-rama>		#cambia a una rama
git branch -d <nombre-rama>		#elimina una rama

###Integracion y colaboracion
git merge <rama>				#fusiona los cambios de una rama
git remote add <nombre> <url> 	#a~ade un repositorio remoto
git push <remoto> <rama>
git pull <remoto> <rama>
<!DOCTYPE html>
<html lang="es">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Manejo de Eventos en HTML y JavaScript</title>
    <script src="https://unpkg.com/xlsx/dist/xlsx.full.min.js"></script>
    <style>
        :root {
            --primary: #2563eb;
            --secondary: #4b5563;
            --accent: #10b981;
            --light: #f3f4f6;
            --dark: #1f2937;
        }
        
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
            font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
        }
        
        body {
            background-color: #f9fafb;
            color: var(--dark);
            line-height: 1.6;
            padding: 20px;
        }
        
        header {
            text-align: center;
            margin-bottom: 40px;
            padding: 20px;
            background: linear-gradient(135deg, var(--primary), var(--accent));
            color: white;
            border-radius: 10px;
            box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
        }
        
        h1 {
            font-size: 2.5rem;
            margin-bottom: 10px;
        }
        
        .description {
            max-width: 800px;
            margin: 0 auto 30px;
            font-size: 1.1rem;
        }
        
        .container {
            max-width: 1200px;
            margin: 0 auto;
        }
        
        .download-btn {
            display: inline-block;
            background-color: var(--accent);
            color: white;
            padding: 12px 24px;
            border-radius: 5px;
            text-decoration: none;
            font-weight: bold;
            margin: 20px 0;
            cursor: pointer;
            border: none;
            transition: background-color 0.3s;
        }
        
        .download-btn:hover {
            background-color: #059669;
        }
        
        .events-grid {
            display: grid;
            grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
            gap: 20px;
            margin-bottom: 40px;
        }
        
        .event-card {
            background-color: white;
            border-radius: 8px;
            padding: 20px;
            box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
            transition: transform 0.3s, box-shadow 0.3s;
        }
        
        .event-card:hover {
            transform: translateY(-5px);
            box-shadow: 0 4px 8px rgba(0, 0, 0, 0.15);
        }
        
        .event-card h3 {
            color: var(--primary);
            margin-bottom: 15px;
            border-bottom: 2px solid var(--light);
            padding-bottom: 10px;
        }
        
        .event-card p {
            margin-bottom: 15px;
        }
        
        .code-example {
            background-color: var(--light);
            padding: 12px;
            border-radius: 5px;
            font-family: monospace;
            font-size: 0.9rem;
            overflow-x: auto;
            margin-bottom: 15px;
        }
        
        .demo-area {
            background-color: white;
            padding: 20px;
            border-radius: 8px;
            box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
            margin-bottom: 40px;
        }
        
        .demo-area h2 {
            color: var(--primary);
            margin-bottom: 20px;
            text-align: center;
        }
        
        .demo-buttons {
            display: flex;
            flex-wrap: wrap;
            gap: 15px;
            justify-content: center;
        }
        
        .demo-btn {
            padding: 10px 20px;
            background-color: var(--primary);
            color: white;
            border: none;
            border-radius: 5px;
            cursor: pointer;
            transition: background-color 0.3s;
        }
        
        .demo-btn:hover {
            background-color: #1d4ed8;
        }
        
        .demo-output {
            margin-top: 20px;
            padding: 15px;
            background-color: var(--light);
            border-radius: 5px;
            min-height: 100px;
        }
        
        footer {
            text-align: center;
            margin-top: 40px;
            padding: 20px;
            color: var(--secondary);
        }
        
        @media (max-width: 768px) {
            .events-grid {
                grid-template-columns: 1fr;
            }
            
            h1 {
                font-size: 2rem;
            }
        }
    </style>
</head>
<body>
    <header>
        <h1>Manejo de Eventos en HTML y JavaScript</h1>
        <p class="description">Guía completa de todas las formas de manejar eventos en HTML y JavaScript con ejemplos prácticos y la posibilidad de descargar la información en formato Excel.</p>
    </header>

    <div class="container">
        <button class="download-btn" onclick="exportToExcel()">Descargar en Excel</button>

        <div class="events-grid">
            <div class="event-card">
                <h3>Atributos HTML inline</h3>
                <p>Manejo de eventos directamente en los elementos HTML usando atributos como onclick, onmouseover, etc.</p>
                <div class="code-example">
                    &lt;button onclick="miFuncion()"&gt;Click me&lt;/button&gt;
                </div>
            </div>

            <div class="event-card">
                <h3>Propiedades del objeto DOM</h3>
                <p>Asignación de eventos mediante propiedades del objeto DOM como element.onclick.</p>
                <div class="code-example">
                    element.onclick = function() {<br>
                    &nbsp;&nbsp;console.log('Click detectado');<br>
                    };
                </div>
            </div>

            <div class="event-card">
                <h3>addEventListener()</h3>
                <p>Método moderno y recomendado para manejar eventos. Permite múltiples manejadores para el mismo evento.</p>
                <div class="code-example">
                    element.addEventListener('click', function() {<br>
                    &nbsp;&nbsp;console.log('Click con addEventListener');<br>
                    });
                </div>
            </div>

            <div class="event-card">
                <h3>removeEventListener()</h3>
                <p>Elimina manejadores de eventos previamente agregados con addEventListener().</p>
                <div class="code-example">
                    element.removeEventListener('click', miFuncion);
                </div>
            </div>

            <div class="event-card">
                <h3>Delegación de eventos</h3>
                <p>Manejo eficiente de eventos para múltiples elementos mediante un ancestro común.</p>
                <div class="code-example">
                    parent.addEventListener('click', function(e) {<br>
                    &nbsp;&nbsp;if (e.target.tagName === 'LI') {<br>
                    &nbsp;&nbsp;&nbsp;&nbsp;console.log('Item clickeado');<br>
                    &nbsp;&nbsp;}<br>
                    });
                </div>
            </div>

            <div class="event-card">
                <h3>Eventos personalizados</h3>
                <p>Creación y despacho de eventos personalizados según las necesidades de la aplicación.</p>
                <div class="code-example">
                    const evento = new CustomEvent('miEvento', {<br>
                    &nbsp;&nbsp;detail: { mensaje: 'Hola' }<br>
                    });<br>
                    element.dispatchEvent(evento);
                </div>
            </div>

            <div class="event-card">
                <h3>Eventos de teclado</h3>
                <p>Manejo de eventos relacionados con el teclado como keydown, keyup y keypress.</p>
                <div class="code-example">
                    document.addEventListener('keydown', function(e) {<br>
                    &nbsp;&nbsp;console.log('Tecla presionada:', e.key);<br>
                    });
                </div>
            </div>

            <div class="event-card">
                <h3>Eventos de formulario</h3>
                <p>Manejo de eventos específicos de formularios como submit, change, input, focus y blur.</p>
                <div class="code-example">
                    form.addEventListener('submit', function(e) {<br>
                    &nbsp;&nbsp;e.preventDefault();<br>
                    &nbsp;&nbsp;console.log('Formulario enviado');<br>
                    });
                </div>
            </div>

            <div class="event-card">
                <h3>Eventos de mouse</h3>
                <p>Manejo de eventos del ratón como click, dblclick, mousemove, mouseover, mouseout, etc.</p>
                <div class="code-example">
                    element.addEventListener('mouseover', function() {<br>
                    &nbsp;&nbsp;console.log('Mouse sobre el elemento');<br>
                    });
                </div>
            </div>
        </div>

        <div class="demo-area">
            <h2>Zona de Demostración</h2>
            <div class="demo-buttons">
                <button class="demo-btn" onclick="demoClick()">onclick</button>
                <button class="demo-btn" id="domEvent">DOM Event</button>
                <button class="demo-btn" id="listenerEvent">addEventListener</button>
                <button class="demo-btn" onmouseover="demoMouseOver()" onmouseout="demoMouseOut()">Mouse Over/Out</button>
            </div>
            <div class="demo-output" id="demoOutput">
                El resultado de las demostraciones aparecerá aquí.
            </div>
        </div>
    </div>

    <footer>
        <p>Guía completa de manejo de eventos en HTML y JavaScript</p>
    </footer>

    <script>
        // Datos para exportar a Excel
        const eventosData = [
            ["Método", "Descripción", "Ejemplo"],
            ["Atributos HTML inline", "Manejo de eventos directamente en los elementos HTML", "&lt;button onclick=\"miFuncion()\"&gt;Click me&lt;/button&gt;"],
            ["Propiedades del objeto DOM", "Asignación de eventos mediante propiedades del objeto DOM", "element.onclick = function() { console.log('Click detectado'); };"],
            ["addEventListener()", "Método moderno y recomendado para manejar eventos", "element.addEventListener('click', function() { console.log('Click con addEventListener'); });"],
            ["removeEventListener()", "Elimina manejadores de eventos previamente agregados", "element.removeEventListener('click', miFuncion);"],
            ["Delegación de eventos", "Manejo eficiente de eventos para múltiples elementos", "parent.addEventListener('click', function(e) { if (e.target.tagName === 'LI') { console.log('Item clickeado'); } });"],
            ["Eventos personalizados", "Creación y despacho de eventos personalizados", "const evento = new CustomEvent('miEvento', { detail: { mensaje: 'Hola' } }); element.dispatchEvent(evento);"],
            ["Eventos de teclado", "Manejo de eventos relacionados con el teclado", "document.addEventListener('keydown', function(e) { console.log('Tecla presionada:', e.key); });"],
            ["Eventos de formulario", "Manejo de eventos específicos de formularios", "form.addEventListener('submit', function(e) { e.preventDefault(); console.log('Formulario enviado'); });"],
            ["Eventos de mouse", "Manejo de eventos del ratón", "element.addEventListener('mouseover', function() { console.log('Mouse sobre el elemento'); });"]
        ];

        // Función para exportar a Excel
        function exportToExcel() {
            const wb = XLSX.utils.book_new();
            const ws = XLSX.utils.aoa_to_sheet(eventosData);
            
            XLSX.utils.book_append_sheet(wb, ws, "Manejo de Eventos");
            XLSX.writeFile(wb, "Manejo_Eventos_HTML_JavaScript.xlsx");
        }

        // Configuración de demostraciones
        document.getElementById('domEvent').onclick = function() {
            document.getElementById('demoOutput').innerHTML += '<p>Evento manejado mediante propiedad DOM</p>';
        };

        document.getElementById('listenerEvent').addEventListener('click', function() {
            document.getElementById('demoOutput').innerHTML += '<p>Evento manejado con addEventListener</p>';
        });

        // Funciones de demostración
        function demoClick() {
            document.getElementById('demoOutput').innerHTML += '<p>Evento onclick ejecutado</p>';
        }

        function demoMouseOver() {
            document.getElementById('demoOutput').innerHTML += '<p>Mouse sobre el botón</p>';
        }

        function demoMouseOut() {
            document.getElementById('demoOutput').innerHTML += '<p>Mouse fuera del botón</p>';
        }

        // Evento de teclado para demostración
        document.addEventListener('keydown', function(e) {
            document.getElementById('demoOutput').innerHTML += `<p>Tecla presionada: ${e.key}</p>`;
        });
    </script>
</body>
</html>
#!/bin/bash

# Colores para output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
CYAN='\033[0;36m'
NC='\033[0m' # No Color

# Función para mostrar mensajes de error
error() {
    echo -e "${RED}Error: $1${NC}" >&2
}

# Función para mostrar mensajes de éxito
success() {
    echo -e "${GREEN}$1${NC}"
}

# Función para mostrar advertencias
warning() {
    echo -e "${YELLOW}Advertencia: $1${NC}"
}

# Función para mostrar información
info() {
    echo -e "${BLUE}$1${NC}"
}

# Función para mostrar detalles
detail() {
    echo -e "${CYAN}$1${NC}"
}

# Función para normalizar nombres (remover guiones bajos y convertir a minúsculas)
normalizar_nombre() {
    echo "$1" | tr '[:upper:]' '[:lower:]' | sed 's/_//g'
}

# Función para obtener migraciones existentes
obtener_migraciones_existentes() {
    local migraciones=()
    if [ -d "database/migrations" ]; then
        while IFS= read -r -d '' archivo; do
            # Extraer el nombre de la tabla del archivo de migración
            local nombre_archivo=$(basename "$archivo")
            # Buscar varios patrones comunes de migraciones
            if [[ "$nombre_archivo" =~ create_(.*)_table\.php ]] ||
               [[ "$nombre_archivo" =~ _(.*)_table\.php ]] ||
               [[ "$nombre_archivo" =~ create_(.*)\.php ]] ||
               [[ "$nombre_archivo" =~ _(.*)\.php ]]; then
                migraciones+=("${BASH_REMATCH[1]}")
            fi
        done < <(find database/migrations -name "*.php" -print0 2>/dev/null)
    fi
    echo "${migraciones[@]}"
}

# Función para verificar si una migración ya existe (comparación normalizada)
migracion_existe() {
    local tabla="$1"
    local migraciones_existentes=($(obtener_migraciones_existentes))
    
    # Normalizar el nombre de la tabla a verificar
    local tabla_normalizada=$(normalizar_nombre "$tabla")
    
    for migracion in "${migraciones_existentes[@]}"; do
        # Normalizar el nombre de la migración existente
        local migracion_normalizada=$(normalizar_nombre "$migracion")
        
        if [ "$migracion_normalizada" == "$tabla_normalizada" ]; then
            return 0 # true - existe
        fi
    done
    return 1 # false - no existe
}

# Función para obtener el nombre real del archivo de migración existente
obtener_nombre_archivo_existente() {
    local tabla="$1"
    local migraciones_existentes=($(obtener_migraciones_existentes))
    
    local tabla_normalizada=$(normalizar_nombre "$tabla")
    
    for migracion in "${migraciones_existentes[@]}"; do
        local migracion_normalizada=$(normalizar_nombre "$migracion")
        
        if [ "$migracion_normalizada" == "$tabla_normalizada" ]; then
            # Encontrar el archivo real
            archivo_existente=$(find database/migrations -name "*${migracion}*" -print -quit 2>/dev/null)
            if [ -n "$archivo_existente" ]; then
                echo "$(basename "$archivo_existente")"
                return
            fi
        fi
    done
    echo ""
}

# Función para verificar si una palabra está en plural (inglés y español)
es_plural() {
    local palabra="$1"
    local palabra_lower=$(echo "$palabra" | tr '[:upper:]' '[:lower:]')
    
    # Reglas para plural en inglés
    local plural_ingles=(
        "s$" "es$" "ies$" "ves$" "xes$" "zes$" "ches$" "shes$"
    )
    
    # Reglas para plural en español
    local plural_espanol=(
        "s$" "es$" "ces$" "ses$" "res$" "nes$"
    )
    
    # Verificar reglas en inglés
    for regla in "${plural_ingles[@]}"; do
        if [[ "$palabra_lower" =~ $regla ]]; then
            return 0 # true - es plural
        fi
    done
    
    # Verificar reglas en español
    for regla in "${plural_espanol[@]}"; do
        if [[ "$palabra_lower" =~ $regla ]]; then
            return 0 # true - es plural
        fi
    done
    
    # Palabras comunes que son plurales
    local plurales_comunes=(
        "people" "children" "men" "women" "feet" "teeth" "mice" "geese"
        "data" "criteria" "phenomena" "personas" "gentes" "naciones"
        "usuarios" "personas" "productos" "categorias" "articulos"
    )
    
    for plural in "${plurales_comunes[@]}"; do
        if [ "$palabra_lower" == "$plural" ]; then
            return 0 # true - es plural
        fi
    done
    
    return 1 # false - no es plural
}

# Función para sugerir plural en inglés (convención Laravel)
sugerir_plural() {
    local palabra="$1"
    local idioma="${2:-en}" # default inglés
    
    # Convertir a minúsculas para procesamiento
    local palabra_lower=$(echo "$palabra" | tr '[:upper:]' '[:lower:]')
    local resultado=""
    
    if [ "$idioma" == "en" ]; then
        # Reglas de pluralización en inglés
        if [[ "$palabra_lower" =~ y$ ]] && ! [[ "$palabra_lower" =~ [aeiou]y$ ]]; then
            resultado="${palabra_lower%y}ies"
        elif [[ "$palabra_lower" =~ f$ ]]; then
            resultado="${palabra_lower%f}ves"
        elif [[ "$palabra_lower" =~ fe$ ]]; then
            resultado="${palabra_lower%fe}ves"
        elif [[ "$palabra_lower" =~ s$ ]] || [[ "$palabra_lower" =~ x$ ]] || [[ "$palabra_lower" =~ z$ ]] || 
             [[ "$palabra_lower" =~ ch$ ]] || [[ "$palabra_lower" =~ sh$ ]]; then
            resultado="${palabra_lower}es"
        else
            resultado="${palabra_lower}s"
        fi
    else
        # Reglas básicas de pluralización en español
        if [[ "$palabra_lower" =~ [áéíóú]$ ]]; then
            resultado="${palabra_lower}es"
        elif [[ "$palabra_lower" =~ z$ ]]; then
            resultado="${palabra_lower%z}ces"
        elif [[ "$palabra_lower" =~ [aeiou]$ ]]; then
            resultado="${palabra_lower}s"
        elif [[ "$palabra_lower" =~ [bcdfghjklmnpqrstvwxyz]$ ]]; then
            resultado="${palabra_lower}es"
        else
            resultado="${palabra_lower}s"
        fi
    fi
    
    # Mantener capitalización original si la palabra comenzaba con mayúscula
    if [[ "$palabra" =~ ^[A-Z] ]]; then
        resultado="$(echo "${resultado:0:1}" | tr '[:lower:]' '[:upper:]')${resultado:1}"
    fi
    
    echo "$resultado"
}

# Mostrar migraciones existentes
mostrar_migraciones_existentes() {
    local migraciones_existentes=($(obtener_migraciones_existentes))
    
    if [ ${#migraciones_existentes[@]} -eq 0 ]; then
        info "No hay migraciones existentes en la carpeta database/migrations"
    else
        info "Migraciones existentes:"
        for migracion in "${migraciones_existentes[@]}"; do
            detail "  - $migracion"
        done
        echo ""
    fi
}

# Función para solicitar nombre de tabla con todas las validaciones
solicitar_nombre_tabla() {
    local numero_tabla="$1"
    local tablas_existentes=("$2") # Tablas ya ingresadas en esta sesión
    
    while true; do
        read -p "Ingrese el nombre de la tabla $numero_tabla: " nombre_tabla
        
        # Validar que no esté vacío
        if [ -z "$nombre_tabla" ]; then
            error "El nombre no puede estar vacío."
            continue
        fi
        
        # Validar formato
        if [[ ! "$nombre_tabla" =~ ^[a-zA-Z0-9_áéíóúÁÉÍÓÚñÑ]+$ ]]; then
            error "El nombre contiene caracteres no válidos."
            continue
        fi
        
        # Verificar si está en plural
        if ! es_plural "$nombre_tabla"; then
            warning "El nombre '$nombre_tabla' parece estar en singular."
            
            # Ofrecer sugerencias en inglés y español
            sugerencia_en=$(sugerir_plural "$nombre_tabla" "en")
            sugerencia_es=$(sugerir_plural "$nombre_tabla" "es")
            
            echo "Sugerencias:"
            echo "  1) Inglés (recomendado): $sugerencia_en"
            echo "  2) Español: $sugerencia_es"
            echo "  3) Mantener '$nombre_tabla'"
            
            read -p "Seleccione una opción (1/2/3): " opcion_plural
            
            case $opcion_plural in
                1) nombre_tabla="$sugerencia_en" ;;
                2) nombre_tabla="$sugerencia_es" ;;
                3) info "Manteniendo: $nombre_tabla" ;;
                *) 
                    error "Opción no válida. Manteniendo nombre original."
                    ;;
            esac
        fi
        
        # Verificar si la migración ya existe en el sistema (comparación normalizada)
        if migracion_existe "$nombre_tabla"; then
            warning "¡La migración para '$nombre_tabla' ya existe!"
            
            # Mostrar el archivo existente
            archivo_existente=$(obtener_nombre_archivo_existente "$nombre_tabla")
            if [ -n "$archivo_existente" ]; then
                detail "Archivo existente: $archivo_existente"
            fi
            
            # Pedir nuevo nombre inmediatamente
            read -p "Por favor, ingrese un nombre diferente: " nombre_tabla
            
            # Continuar el ciclo para validar el nuevo nombre
            continue
        fi
        
        # Verificar si ya fue ingresado en esta misma sesión (comparación normalizada)
        for tabla_existente in "${tablas_existentes[@]}"; do
            if [ "$(normalizar_nombre "$tabla_existente")" == "$(normalizar_nombre "$nombre_tabla")" ]; then
                warning "El nombre '$nombre_tabla' ya fue ingresado en esta sesión."
                read -p "Por favor, ingrese un nombre diferente: " nombre_tabla
                continue 2
            fi
        done
        
        # Si llegamos aquí, el nombre es válido, no existe y no es duplicado
        echo "$nombre_tabla"
        break
    done
}

# Validar que estamos en un proyecto Laravel
if [ ! -f "artisan" ]; then
    error "No se encontró el archivo artisan. Asegúrate de estar en el directorio raíz de un proyecto Laravel."
    exit 1
fi

# Validar que PHP está disponible
if ! command -v php &> /dev/null; then
    error "PHP no está instalado o no está en el PATH."
    exit 1
fi

# Mostrar migraciones existentes al inicio
mostrar_migraciones_existentes

# Solicitar y validar la cantidad de tablas
while true; do
    read -p "Ingrese la cantidad de tablas que desea crear: " cantidad_tablas
    
    if [[ ! "$cantidad_tablas" =~ ^[0-9]+$ ]]; then
        error "Por favor, ingrese un número válido."
        continue
    fi
    
    if [ "$cantidad_tablas" -lt 1 ]; then
        error "La cantidad debe ser al menos 1."
        continue
    fi
    
    break
done

# Inicializar arreglo para almacenar los nombres de las tablas
tablas=()

info "Convenciones de Laravel:"
info "- Nombres de tablas en PLURAL (inglés preferido)"
info "- Ejemplos: users, products, categories, posts, comments"
info "- También se acepta español: usuarios, productos, categorías"

# Solicitar los nombres de las tablas con validación inmediata
for ((i=1; i<=cantidad_tablas; i++)); do
    nombre_tabla=$(solicitar_nombre_tabla $i "${tablas[@]}")
    
    tablas+=("$nombre_tabla")
    success "Tabla agregada: $nombre_tabla"
done

if [ ${#tablas[@]} -eq 0 ]; then
    warning "No se agregaron tablas para crear migraciones."
    exit 0
fi

# Mostrar resumen antes de proceder
echo -e "\n${YELLOW}Resumen de migraciones a crear:${NC}"
for table in "${tablas[@]}"; do
    echo "  - create_${table}_table"
done

# Confirmar antes de ejecutar
read -p $'\n¿Desea continuar con la creación de las migraciones? (s/N): ' confirmar

if [[ ! "$confirmar" =~ ^[SsYy]$ ]] && [ -n "$confirmar" ]; then
    warning "Operación cancelada por el usuario."
    exit 0
fi

# Crear las migraciones
echo -e "\nCreando migraciones..."
for table in "${tablas[@]}"; do
    echo "Creando migración para: $table"
    
    if php artisan make:migration "create_${table}_table" --quiet; then
        success "✓ Migración para '$table' creada exitosamente."
    else
        error "✗ Error al crear la migración para '$table'"
    fi
done

# Mostrar migraciones actualizadas
echo -e "\n"
mostrar_migraciones_existentes

success "¡Proceso completado! Se crearon ${#tablas[@]} migraciones."
below json settings is for 0 width gutter size
"editor.lineNumbers": "off",
    "scm.diffDecorations": "none",
    "errorLens.gutterIconsEnabled": false,
    "debug.gutterMiddleClickAction": "none",
    "editor.showFoldingControls": "never",
    "editor.hideCursorInOverviewRuler": true,
    "editor.overviewRulerBorder": false,
    "editor.glyphMargin": false,
<ifModule mod_headers.c>
Header set Strict-Transport-Security "max-age=31536000" env=HTTPS
Header set X-XSS-Protection "1; mode=block"
Header set X-Content-Type-Options nosniff
Header set X-Frame-Options SAMEORIGIN
Header set Referrer-Policy: no-referrer-when-downgrade
</ifModule>
واجهة USB عالية السرعة: تجربة نقل البيانات بسرعة مع USB ، مما يضمن معالجة الملفات بكفاءة لجميع احتياجات التخزين الخاصة بك.
``` php-yii2 botón bootstrap
<div>
<?= Html::a('Crear', ['create'], ['class' => 'btn btn-success', 'style' => 'background-color: #4A49C0; border: 1px solid #4A49C0;']) ?>
</div> 
```
La opción -d en un comando Docker significa "modo detached" o "modo desacoplado".

Cuando usas docker run -d, el contenedor se ejecuta en segundo plano (background). No se muestra la salida del contenedor en la terminal, ni se queda "pegada" a ella. Esto es útil para servicios y aplicaciones que quieres que sigan corriendo sin que ocupen tu terminal.

Si no usas -d (modo foreground o primer plano), el contenedor se ejecuta conectado a tu terminal y verás los logs y la salida en tiempo real. La terminal queda ocupada con el proceso del contenedor hasta que lo detengas. Esto es útil para tareas interactivas o para depurar.

Resumiendo:

-d: contenedor en segundo plano, terminal libre para otras tareas.

Sin -d: contenedor en primer plano, salida visible en la terminal.

Esta es una diferencia clave para controlar cómo interactúas con los contenedores al ejecutarlos con Docker.

¿Importa el tipo de red (bridge vs overlay) para elegir entre uno y otro?
-Sí, el tipo de red en Docker se relaciona con el modo de ejecución.
-Las redes bridge suelen ser para contenedores que corren en el mismo host (nodo local). Por eso se usa con docker run.
-Las redes overlay permiten la comunicación segura entre contenedores que corren en diferentes nodos en un clúster Docker Swarm. Por eso se usan con docker service create en modo swarm.

1. Red Bridge (puente) - red local para contenedores en un mismo host
bash
docker network create --driver bridge mi_red_bridge

docker run -d --name postgres_bridge --network mi_red_bridge -e POSTGRES_PASSWORD=contraseña postgres
docker run -d --name php_bridge --network mi_red_bridge php

2. Red Host - el contenedor comparte la red del host (sin aislamiento)
bash
docker run -d --name postgres_host --network host -e POSTGRES_PASSWORD=contraseña postgres
docker run -d --name php_host --network host php

3. Red Overlay - permite que contenedores en diferentes hosts se comuniquen (requiere Docker Swarm)
bash
docker swarm init

docker network create --driver overlay mi_red_overlay

docker service create --name postgres_overlay --network mi_red_overlay -e POSTGRES_PASSWORD=contraseña postgres
docker service create --name php_overlay --network mi_red_overlay php

4. Red Macvlan - contenedores tienen IP propia en la red física del host (requiere configuración de red)
bash
docker network create -d macvlan \
  --subnet=192.168.1.0/24 \
  --gateway=192.168.1.1 \
  -o parent=eth0 mi_red_macvlan

docker run -d --name postgres_macvlan --network mi_red_macvlan -e POSTGRES_PASSWORD=contraseña postgres
docker run -d --name php_macvlan --network mi_red_macvlan php
Nota: Cambia 192.168.1.0/24, 192.168.1.1 y eth0 según tu red física.

5. Red None - el contenedor no tiene red ni acceso a otros contenedores
bash
docker run -d --name postgres_none --network none -e POSTGRES_PASSWORD=contraseña postgres
docker run -d --name php_none --network none php
Con estos comandos tienes ejemplos prácticos de cómo crear y conectar contenedores PHP y PostgreSQL en los distintos tipos de redes Docker.

docker start <nombre_o_id_del_contenedor>

docker stop <nombre_o_id_del_contenedor>
  
docker restart <nombre_o_id_del_contenedor>
docker start <nombre_o_id_del_contenedor>

docker stop <nombre_o_id_del_contenedor>
#Import:

n8n export:workflow --all --output /path/to/export/folder
n8n export:credential --all --output /path/to/export/folder

#Export:

n8n import:workflow --input /path/to/export/folder/workflows.json
n8n import:credential --input /path/to/export/folder/credentials.json
Ver contenedores en ejecución:
docker ps
Este comando muestra solo los contenedores que están en ejecución actualmente.

Ver todos los contenedores (en ejecución y detenidos):
docker ps -a
o
docker container ls -a
1.-Eliminar el contenedor detenido que está usando esa imagen con:
docker rm 8850ff9258c0

1.1-podrás eliminar la imagen normalmente:
docker rmi 1b44b5a3e06a

3.-Forzar la eliminación de la imagen junto con los contenedores detenidos que la usan con:
docker rmi -f 1b44b5a3e06a

Listar y eliminar todos los contenedores en estado "exited" (detenidos) con este comando combinado:

docker rm $(docker ps -a -f status=exited -q)

os parámetros significan lo siguiente:

-a (o --all): En el comando docker ps, muestra todos los contenedores, incluyendo tanto los que están en ejecución como los que están detenidos. Sin esta opción, solo se muestran los contenedores en ejecución.

-f (o --filter): Permite filtrar la lista de contenedores según una condición. En este caso, status=exited filtra para mostrar solo los contenedores que están en estado "exited", es decir, detenidos.

-q (o --quiet): Devuelve solo los IDs de los contenedores, sin mostrar toda la información adicional.

status=exited: Es la condición para el filtro -f, indica que queremos los contenedores cuyo estado es "exited" (detenidos).

docker container prune



En informática, la tabla DMI se refiere principalmente al Desktop Management Interface (DMI). Es un estándar que proporciona un framework común para la gestión y seguimiento de los componentes de hardware en un ordenador de sobremesa, portátil o servidor

sudo dmidecode [opciones] | todo
ejemplo: sudo dmidecode -t 
  bios
  system
  baseboard
  chassis
  processor
  memory
  cache
  connector
  slot

lscpu:
El comando lscpu en Linux se utiliza para mostrar información detallada sobre la unidad central de procesamiento (CPU) del sistema. Proporciona datos estructurados como:

Arquitectura de la CPU (por ejemplo, x86_64)
Número de núcleos y subprocesos (threads)
Modelo y nombre de la CPU
Velocidad de reloj actual (frecuencia MHz)
Modos operativos soportados (32-bit, 64-bit)
Topología de la CPU: núcleos por socket, hilos por núcleo, sockets
Tamaño de caché (L1, L2, L3)
Información sobre soportes de virtualización (VT-x, AMD-V)
Otros detalles técnicos relevantes para la CPU
-Es una herramienta esencial para administradores de sistemas, desarrolladores y usuarios técnicos que quieran conocer la configuración y capacidades del procesador sin necesidad de privilegios de superusuario.

sudo dmidecode [opciones]
Las opciones principales que se pueden usar con el comando sudo dmidecode son:

-t o --type: Muestra solo la información de un tipo específico de hardware o sección de la tabla DMI. Puede recibir el nombre o número del tipo. Ejemplos de tipos:

bios (0 o 13): Información de la BIOS.
system (1, 12, 15, 23, 32): Información general del sistema.
baseboard (2, 10, 41): Información de la placa base.
chassis (3): Información del chasis o carcasa.
processor (4): Información del procesador.
memory (5, 6, 16, 17): Información de la memoria RAM.
cache (7): Información de la memoria cache.
connector (8): Información de conectores.
slot (9): Información de ranuras de expansión.
-q o --quiet: Ejecuta el comando en modo silencioso, suprimiendo la mayoría de mensajes de advertencia y errores para una salida más limpia, útil para scripts.
-s o --string: Muestra solo el valor de una cadena específica DMI que se especifique.
Sin opciones, muestra toda la información disponible en la tabla DMI.
Ejemplo de uso para obtener solo información de la memoria RAM en modo silencioso:
{
	"blocks": [
		{
			"type": "header",
			"text": {
				"type": "plain_text",
				"text": ":star: What's on in Melbourne this week! :star:"
			}
		},
		{
			"type": "section",
			"text": {
				"type": "mrkdwn",
				"text": "\n\n Hey Melbourne, happy Monday and hope you all had a Beautiful weekend! Please see below for what's on this week. "
			}
		},
		{
			"type": "divider"
		},
		{
			"type": "header",
			"text": {
				"type": "plain_text",
				"text": "Xero Café :coffee:",
				"emoji": true
			}
		},
		{
			"type": "section",
			"text": {
				"type": "mrkdwn",
				"text": "\n :new-thing: *This week we are offering:* \n\n :caramel-slice: *Sweet Treats*: Selection of cookies \n\n :coffee: *Weekly Café Special*: Iced Matcha Latte"
			}
		},
		{
			"type": "header",
			"text": {
				"type": "plain_text",
				"text": " Wednesday, 27th August :calendar-date-27:",
				"emoji": true
			}
		},
		{
			"type": "section",
			"text": {
				"type": "mrkdwn",
				"text": "\n\n:lunch: :flag-fr: Join us for an French lunch From *12pm* in the Wominjeka breakout space! Menu in the:thread: "
			}
		},
		{
			"type": "header",
			"text": {
				"type": "plain_text",
				"text": "Thursday, 28th August :calendar-date-28:",
				"emoji": true
			}
		},
		{
			"type": "section",
			"text": {
				"type": "mrkdwn",
				"text": ":breakfast: *Breakfast*: Join us for Breakfast from *8:30am - 10:30am* in the Wominjeka Breakout Space. Menu in the :thread: \n\n\n *What Else? :green_heart:*"
			}
		},
		{
			"type": "divider"
		},
		{
			"type": "section",
			"text": {
				"type": "mrkdwn",
				"text": " Feedback on our Boost Offerings? We want to hear more. Let us know what you love by filling out our form <https://docs.google.com/forms/d/e/1FAIpQLScGOSeS5zUI8WXEl0K4WGoQUkmpIHzAjLlEKWBob4sMPhDXmA/viewform|here.>  \n\n Stay tuned to this channel, and make sure you're subscribed to the <https://calendar.google.com/calendar/u/0?cid=Y19xczkyMjk5ZGlsODJzMjA4aGt1b3RnM2t1MEBncm91cC5jYWxlbmRhci5nb29nbGUuY29t|*Melbourne Social Calendar*> :party-wx:"
			}
		}
	]
}
 error_log(print_r($new_date, true), 3, __DIR__ . '/../error_log');
Estructura general:

xmlelement(name libros, ...): crea un elemento XML raíz llamado <libros>.

xmlagg(...): agrega múltiples elementos XML dentro del elemento raíz.

xmlelement(name libro, xmlforest(...)): crea un elemento <libro> con varios subelementos para los campos del libro.

SELECT
  xmlelement(
    name libros,
    xmlagg(
      xmlelement(
        name libro,
        xmlforest(
          id,
          titulo,
          autor,
          anio
        )
      )
    )
  ) AS libros_xml
FROM libros;
$conn = pg_connect("host=localhost dbname=mi_base user=usuario password=clave");

$sql = "SELECT
  xmlelement(
    name libros,
    xmlagg(
      xmlelement(
        name libro,
        xmlforest(
          id,
          titulo,
          autor,
          anio
        )
      )
    )
  ) AS libros_xml
FROM libros";

$result = pg_query($conn, $sql);
$row = pg_fetch_assoc($result);

$xmlStr = $row['libros_xml'];
$xml = simplexml_load_string($xmlStr);
Cómo funciona cada uno para obtener datos:
XML es un formato de datos estructurado que puede estar almacenado en un archivo local o servido desde un servidor remoto (por ejemplo, desde una API REST).

XPath es un lenguaje para navegar y seleccionar partes específicas dentro de un documento XML.

XQuery es un lenguaje de consulta más avanzado para extraer, transformar y manipular datos XML, que puede usarse para consultar documentos XML o bases de datos XML alojadas en servidores, locales o remotos.

JSON es otro formato muy popular para transmitir datos, especialmente en APIs web modernas, y se pueden consultar y manipular fácilmente con lenguajes como JavaScript o PHP.
Un ejemplo sencillo y representativo de una base de datos XML podría ser un conjunto de documentos XML que almacenan información estructurada, como un catálogo de libros o una lista de contactos.

Aquí tienes una estructura básica de un archivo XML que simula la información en una base de datos de libros:

<?xml version="1.0" encoding="UTF-8"?>
<biblioteca>
  <libro id="1">
    <titulo>El Quijote</titulo>
    <autor>Miguel de Cervantes</autor>
    <anio>1605</anio>
    <genero>Novela</genero>
  </libro>
  <libro id="2">
    <titulo>Cien Años de Soledad</titulo>
    <autor>Gabriel García Márquez</autor>
    <anio>1967</anio>
    <genero>Realismo mágico</genero>
  </libro>
  <libro id="3">
    <titulo>La Sombra del Viento</titulo>
    <autor>Carlos Ruiz Zafón</autor>
    <anio>2001</anio>
    <genero>Novela negra</genero>
  </libro>
</biblioteca>
Base de datos PostgreSQL con datos XML

<biblioteca>
  <libro>
    <titulo>El Quijote</titulo>
    <autor>Miguel de Cervantes</autor>
  </libro>
  <libro>
    <titulo>Cien Años de Soledad</titulo>
    <autor>Gabriel García Márquez</autor>
  </libro>
</biblioteca>
json_encode(): convierte un array u objeto PHP en una cadena JSON.

json_decode(): convierte una cadena JSON en un objeto o array PHP.

// Array PHP a JSON
$array = ['nombre' => 'Juan', 'edad' => 30];
$json = json_encode($array);
echo $json; // {"nombre":"Juan","edad":30}

// JSON a array PHP
$jsonString = '{"nombre":"Ana","edad":25}';
$arrayPHP = json_decode($jsonString, true);
echo $arrayPHP['nombre']; // Ana
convertir una tabla postgres en json en yii2:
<?php
namespace app\controllers;

use yii\web\Controller;
use yii\web\Response;
use app\models\Libro; // Suponiendo que tienes un modelo Libro

class LibroController extends Controller
{
    public function actionLista()
    {
        // Obtener datos como array usando asArray()
        $libros = Libro::find()->asArray()->all();

        \Yii::$app->response->format = Response::FORMAT_JSON;

        // Devolver los datos directamente como JSON
        return $libros;
    }
}


convertir una tabla postgres en json en  laravel:
<?php

namespace App\Http\Controllers;

use App\Models\Libro;
use Illuminate\Http\JsonResponse;

class LibroController extends Controller
{
    public function lista(): JsonResponse
    {
        // Obtener todos los registros como array
        $libros = Libro::all();

        // Devolver los datos como JSON
        return response()->json($libros);
    }
}
Cómo definir la ruta para acceder a este método (en routes/web.php o routes/api.php):
use App\Http\Controllers\LibroController;
Route::get('/libro/lista', [LibroController::class, 'lista']);

javascript:
1. Con promesas (.then)
Cómo consumirlo con JavaScript sería igual que en Yii2-laravel:
fetch('/libro/lista')
  .then(response => response.json())
  .then(data => {
    data.forEach(libro => {
      console.log(libro.titulo);
    });
  });

2. Con async/await (más legible)
Cómo consumirlo en JavaScript sería igual que en Yii2-laravel:
async function obtenerLibros() {
  try {
    const response = await fetch('/libro/lista');
    const data = await response.json();
    data.forEach(libro => {
      console.log(libro.titulo);
    });
  } catch (error) {
    console.error('Error:', error);
  }
}

obtenerLibros();

3. Con callback (usando XMLHttpRequest)
Cómo consumirlo en JavaScript sería igual que en Yii2-laravel:
function obtenerLibrosConCallback(callback) {
  const xhr = new XMLHttpRequest();
  xhr.open('GET', '/libro/lista');
  xhr.onload = function() {
    if (xhr.status === 200) {
      const data = JSON.parse(xhr.responseText);
      callback(null, data);
    } else {
      callback(new Error('Error en la solicitud: ' + xhr.status));
    }
  };
  xhr.onerror = function() {
    callback(new Error('Error de red'));
  };
  xhr.send();
}

// Usando la función con callback
obtenerLibrosConCallback(function(error, data) {
  if (error) {
    console.error(error);
  } else {
    data.forEach(libro => {
      console.log(libro.titulo);
    });
  }
});

jquery:
1.-Ejemplo usando $.getJSON()

<!DOCTYPE html>
<html>
<head>
  <title>Consumir API con jQuery</title>
  <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
</head>
<body>

<ul id="lista-libros"></ul>

<script>
$(document).ready(function() {
  $.getJSON('/libro/lista')
    .done(function(data) {
      // data es un array de objetos libros
      $.each(data, function(index, libro) {
        $('#lista-libros').append('<li>' + libro.titulo + '</li>');
      });
    })
    .fail(function(jqxhr, textStatus, error) {
      console.error('Error al consumir la API:', textStatus, error);
    });
});
</script>

</body>
</html>

2.-Ejemplo usando $.ajax():
$.ajax({
  url: '/libro/lista',
  type: 'GET',
  dataType: 'json',
  success: function(data) {
    data.forEach(function(libro) {
      console.log(libro.titulo);
    });
  },
  error: function(jqxhr, textStatus, error) {
    console.error('Error:', error);
  }
});

laravel:
use Illuminate\Support\Facades\Http;

$response = Http::get('https://api.example.com/data');

if ($response->successful()) {
    $data = $response->json(); // decodifica JSON automáticamente a array
    // procesar $data
} else {
    // manejar error
}

yii2:
Formas comunes de consumir APIs en PHP / Yii2:
1. Usar cURL directamente (funciona en cualquier PHP)

use yii\httpclient\Client;

$client = new Client();
$response = $client->get('https://api.ejemplo.com/data')->send();

if ($response->isOk) {
    $data = $response->getData(); // Obtienes array o json decodificado automáticamente
}

2. Usar la clase yii\httpclient\Client que trae Yii2 (más moderno y elegante)
use yii\httpclient\Client;

$client = new Client();
$response = $client->get('https://api.ejemplo.com/data')->send();

if ($response->isOk) {
    $data = $response->getData(); // Obtienes array o json decodificado automáticamente
}




// Component Registry (same as before)

const ComponentRegistry = {

  // Button Component

  Button: {

    template: (props = {}) => `

          <button class="neo-brutalist bg-${props.color || 'yellow-400'} text-${props.textColor || 'black'} text-lg font-bold py-3 px-6 hover:bg-${props.color || 'yellow-400'}-600 transition-none">

            ${props.text || 'Click Me'}

          </button>

        `,

    init: (element, props) => {

      element.innerHTML = ComponentRegistry.Button.template(props);

    }

  },

​

  // Card Component

  Card: {

    template: (props = {}) => `

          <div class="neo-brutalist bg-white p-6">
body {

  font-family: 'Roboto Mono', monospace;

  background-color: #ff5f5;

  color: #1a1a1a;
5
}

.neo-brutalist {

  border: 4px solid #000;

  box-shadow: 8px 8px 0 #000;

  transition: all 0.2s ease;

  border-radius: 4px;

}

.neo-brutalist:hover {

  box-shadow: 6px 6px 0 #000;

  transform: translate(2px, 2px);

}

h1, h2, h3 {

  font-family: 'Bebas Neue', sans-serif;

  letter-spacing: 2px;

}

.component-card {

  height: 100%;
<body class="p- md:p-">

  <div class="container mx-auto">

    <h1 class="text-4xl md:text-xl mb- md:mb-8 text-center">Neo Brutalist Component Library</h1>
4
​
5
    <!-- Component Grid -->
6
    <div id="components" class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-2 gap-6">

      <!-- Components will be injected here by JavaScript -->
8
    </div>

  </div>

  

  

<a target="_blank" href="https://www.rustcodeweb.com/" 

   style="position: fixed; bottom: 0.6rem; right: 0.6rem; background: #FFD6; color: #073B4C; text-decoration: none; padding: 0.5rem 1rem; border: 3px solid #073B4C; box-shadow: 3px 3px 0 #073B4C; font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif; font-weight: 600; font-size: 0.875rem; transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); z-index: 1200; border-radius: 2px; display: flex; align-items: center; gap: 0.5rem;"

   onmouseover="this.style.background='#F8F9FA'; this.style.transform='translate(-1px, -1px)'; this.style.boxShadow='4px 4px 0 #073B4C';"

   onmouseout="this.style.background='#FFD166'; this.style.transform='translate(0, 0)'; this.style.boxShadow='3px 3px 0 #073B4C';"
16
   onmousedown="this.style.transform='translate(0, 0)'; this.style.boxShadow='2px 2px 0 #073B4C';"

   onmouseup="this.style.transform='translate(-1px, -1px)'; this.style.boxShadow='4px 4px 0 #073B4C';"
<body class="p- md:p-">

  <div class="container mx-auto">

    <h1 class="text-4xl md:text-xl mb- md:mb-8 text-center">Neo Brutalist Component Library</h1>
4
​
5
    <!-- Component Grid -->
6
    <div id="components" class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-2 gap-6">

      <!-- Components will be injected here by JavaScript -->
8
    </div>

  </div>

  

  

<a target="_blank" href="https://www.rustcodeweb.com/" 

   style="position: fixed; bottom: 0.6rem; right: 0.6rem; background: #FFD6; color: #073B4C; text-decoration: none; padding: 0.5rem 1rem; border: 3px solid #073B4C; box-shadow: 3px 3px 0 #073B4C; font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif; font-weight: 600; font-size: 0.875rem; transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); z-index: 1200; border-radius: 2px; display: flex; align-items: center; gap: 0.5rem;"

   onmouseover="this.style.background='#F8F9FA'; this.style.transform='translate(-1px, -1px)'; this.style.boxShadow='4px 4px 0 #073B4C';"

   onmouseout="this.style.background='#FFD166'; this.style.transform='translate(0, 0)'; this.style.boxShadow='3px 3px 0 #073B4C';"
16
   onmousedown="this.style.transform='translate(0, 0)'; this.style.boxShadow='2px 2px 0 #073B4C';"

   onmouseup="this.style.transform='translate(-1px, -1px)'; this.style.boxShadow='4px 4px 0 #073B4C';"
php-nativo:
php -S 192.168.1.50:9000
php -S localhost:9000
php -S 127.0.0.1:9000 //localhost

php-yii2
php -S localhost:8001 -t backend/web
php -S 172.xx.xx.xx:8081 -t backend/web

php-laravel
php artisan serve --port=8080
php -S 192.168.1.50:9000 -t public

laravel
php artisan serve
php artisan serve --host=172.xx.xx.xx:8081 --port=8000
php artisan serve --host=localhost --port=8000


yii2
php yii serve --port=8080
php yii serve --docroot="backend/web"
php yii serve --docroot="backend/web" --address=192.168.1.50 --port=9000
import json
import os
from typing import Dict

import torch
import torch.fx as fx
import torch.nn as nn
from core.frameworks.pytorch.quantization.sdk.graph_utils import ModelGraphUtils
from core.frameworks.pytorch.quantization.sdk.model_preparer import (
    _get_module_for_dotted_name,
    prepare_model,
)
from core.frameworks.pytorch.quantization.sdk.modules.fake_quantize import (
    FakeQuantize as CustomFakeQuantize,
)
from core.frameworks.pytorch.quantization.sdk.modules.module_replacement_registry import (
    ModuleReplacements,
)
from core.frameworks.pytorch.quantization.sdk.modules.quantized_modules import (
    QuantizationMixin,
)
from core.frameworks.pytorch.quantization.sdk.quantization_utils import (
    build_clean_onnx_path,
    build_onnx_path_from_torch_name,
    get_bitwidth_from_string_dtype,
    normalize_class_name,
    print_quant_summary,
)
from core.frameworks.pytorch.utils.utils import is_leaf_module
from logger.logger_registry import get_logger
from utils.config_utils import (
    get_model_configs,
    get_task_configs,
)
from utils.helpers import set_nested_attr

from .modules.function_modules import REPLACEMENTS
from .quantsim_config.quantsim_parser import QuantSimConfigParser

logger = get_logger("main")


class QuantizerEnginer:
    def __init__(
        self,
        model: torch.nn.Module,
        configs,
        device: torch.device,
    ):
        self._model: torch.nn.Module = model
        self.config = configs
        self.logger = get_logger("main")
        self.device = device
        self.task_type = (
            "qft" if self.config["qft"] else "ptq" if self.config["ptq"] else "pruning"
        )

        self.quantsim_config = QuantSimConfigParser()

    def update_model(self, model: torch.nn.Module):
        self._model = model

    def fuse_all_conv_bn(self, module):
        fusion_list = []
        module_list = list(module.named_modules())

        for (name1, mod1), (name2, mod2) in zip(module_list, module_list[1:]):
            if isinstance(mod1, torch.nn.Conv2d) and isinstance(
                mod2, torch.nn.BatchNorm2d
            ):
                fusion_list.append([name1, name2])

        if fusion_list:
            torch.ao.quantization.fuse_modules(module, fusion_list, inplace=True)

    def _check_super_groups_module(self, layer_name: str) -> bool:
        super_groups = self.quantsim_config.get_super_groups_ops()
        next_node_name = self.graphutils.get_next_non_identity_module(layer_name)
        if not next_node_name:
            return self.quantsim_config.is_default_ops_output_quantized()
        layer_name = self.graphutils.get_normalized_layer_name(layer_name)
        next_node_name = self.graphutils.get_normalized_layer_name(next_node_name)
        if next_node_name:
            next_node_name_lower = next_node_name.lower()
            node_name_lower = layer_name.lower()
            for super_group in super_groups:
                super_group_first = super_group[0].lower()
                super_group_second = super_group[1].lower()
                if (
                    node_name_lower in super_group_first
                    and next_node_name_lower in super_group_second
                ):
                    return False
                elif next_node_name_lower in super_group_second:
                    return True
        return self.quantsim_config.is_default_ops_output_quantized()

    def apply_io_quantizer_flags_to_graph(
        self, graph: torch.fx.GraphModule
    ) -> dict[str, tuple[bool, bool]]:
        """
        Iterate over all nodes in the graph and determine IO quantizer flags.

        Returns:
            A dictionary mapping node names to (add_input_quantizer, add_output_quantizer) flags.
        """
        io_flags = {}
        for node in graph.graph.nodes:
            if node.op == "call_module":
                flags = self.__get_io_quantizer_flags(graph, node, None)
                io_flags[node.name] = flags
                print(
                    f"[IO Quantizer Flags] {node.name}: Input={flags[0]}, Output={flags[1]}"
                )
                module: QuantizationMixin = _get_module_for_dotted_name(
                    graph, node.target
                )
                if not isinstance(module, QuantizationMixin):
                    continue
                if not flags[0]:
                    module.disable_input_quantization()
                if not flags[1]:
                    module.disable_output_quantization()
                if flags[0]:
                    module.enable_input_quantization()
                if flags[1]:
                    module.enable_output_quantization()

        return io_flags

    def _check_super_groups_node(
        self,
        layer_name: str,
        node: torch.fx.Node,
        next_node: torch.fx.Node,
        next_node_name: str,
    ) -> bool:
        super_groups = self.quantsim_config.get_super_groups_ops()
        for super_group in super_groups:
            if (
                layer_name in super_group[0].lower()
                and next_node
                and next_node_name in super_group[1].lower()
            ):
                print("#" * 20, layer_name, next_node_name, "#" * 20)
                return False
            elif layer_name in super_group[1].lower():
                return True
        return True

    def __is_quantizable_module(self, module: torch.nn.Module):
        return ModuleReplacements.get_replacement(module) is not None

    @classmethod
    def __is_quantized_module(cls, module: torch.nn.Module):
        return isinstance(module, QuantizationMixin)

    def _normalized_op_name(self, module: torch.nn.Module) -> str:
        """Map a module to the normalized op name used in your config."""
        return self.graphutils.get_normalized_layer_name(module)

    def _match_pattern_from(
        self,
        graph: torch.fx.GraphModule,
        start_node: torch.fx.Node,
        pattern_ops: list[str],
    ):
        """
        Try to match a supergroup pattern starting at start_node.
        Returns list of nodes if matched, else None.
        """
        if start_node.op != "call_module":
            return None

        try:
            first_module = _get_module_for_dotted_name(graph, start_node.target)
        except Exception:
            return None

        first_name = self._normalized_op_name(first_module)
        if first_name != pattern_ops[0]:
            return None

        matched_nodes = [start_node]
        curr = start_node
        for expected in pattern_ops[1:]:
            next_node = self.graphutils.get_next_non_identity_node(curr)
            if not next_node or next_node.op != "call_module":
                return None
            try:
                next_module = _get_module_for_dotted_name(graph, next_node.target)
            except Exception:
                return None
            next_name = self._normalized_op_name(next_module)
            if next_name != expected:
                return None
            matched_nodes.append(next_node)
            curr = next_node

        return matched_nodes

    def _collect_supergroup_patterns(self):
        """
        Read patterns from your quantsim config.
        Expected structure: [["Conv", "BatchNorm", "Relu"], ["MatMul", "Add"]]
        """
        cfg = self.quantsim_config.get_model_quantization_config()
        patterns = cfg.get("supergroups", [])
        cleaned = []
        for pat in patterns:
            if isinstance(pat, (list, tuple)) and len(pat) >= 2:
                cleaned.append([str(x) for x in pat])
        return cleaned

    def _apply_super_groups_config(self, graph: torch.fx.GraphModule):
        """
        Find and apply super-group quantization configuration.
        Works for groups of any length >= 2. Prevents overlaps.
        """
        patterns = self._collect_supergroup_patterns()
        self._supergroup_members = {}
        claimed = set()
        group_id = 0

        for node in graph.graph.nodes:
            if node.op != "call_module" or node in claimed:
                continue

            for pat in patterns:
                match = self._match_pattern_from(graph, node, pat)
                if not match:
                    continue
                if any(n in claimed for n in match):
                    continue

                size = len(match)
                for idx, member in enumerate(match):
                    self._supergroup_members[member] = (group_id, idx, size)
                    claimed.add(member)

                # Gather actual modules
                modules = []
                for mnode in match:
                    m = _get_module_for_dotted_name(graph, mnode.target)
                    modules.append(m)

                # Apply quantizer sharing
                self._apply_super_group_action_general(modules)

                if getattr(self, "verbose", False):
                    names = [self._normalized_op_name(m) for m in modules]
                    print(f"[SuperGroup] id={group_id} matched: {' -> '.join(names)}")

                group_id += 1
                break

    def _belongs_to_super_group(self, node: torch.fx.Node) -> bool:
        return hasattr(self, "_supergroup_members") and node in self._supergroup_members

    def _supergroup_position(self, node: torch.fx.Node):
        """Return (group_id, idx, size) if node is in a supergroup, else None."""
        if self._belongs_to_super_group(node):
            return self._supergroup_members[node]
        return None

    def _apply_super_group_action_general(self, modules: list):
        n = len(modules)
        if n < 2:
            return

        last_module = modules[-1]
        if not hasattr(last_module, "output_quantizer"):
            return

        shared_output_q = last_module.output_quantizer

        for m in modules[:-1]:
            if hasattr(m, "output_quantizer"):
                m.output_quantizer = shared_output_q

    def __get_io_quantizer_flags(
        self, graph, node: torch.fx.Node, layer_name: str | None
    ) -> tuple[bool, bool]:
        """
        Decide whether to add input/output quantizers for a node,
        respecting model IO policy and per-layer overrides.
        """
        layer_name = layer_name or self.graphutils.get_normalized_layer_name(node)
        model_quant_config = self.quantsim_config.get_model_quantization_config()

        add_input_quantizer = self.quantsim_config.is_default_ops_input_quantized()
        add_output_quantizer = self.quantsim_config.is_default_ops_output_quantized()

        if self.graphutils._is_first(node, layer_name):
            add_input_quantizer = model_quant_config.get("input_quantized", False)
        if self.graphutils._is_last(node, layer_name):
            add_output_quantizer = model_quant_config.get("output_quantized", False)

        # No need for explicit supergroup override:
        # all sharing is already handled in _apply_super_group_action_general.

        add_input_quantizer = self._apply_layer_override(
            layer_name, add_input_quantizer, is_input=True
        )
        add_output_quantizer = self._apply_layer_override(
            layer_name, add_output_quantizer, is_input=False
        )
        _module = _get_module_for_dotted_name(graph, node.target)
        return add_input_quantizer, add_output_quantizer

    def _apply_layer_override(
        self, layer_name: str, current_value: bool, is_input: bool
    ) -> bool:
        if is_input:
            if (
                layer_name
                in self.quantsim_config.get_layers_to_skip_from_input_quantizers()
            ):
                return False
            if layer_name in self.quantsim_config.get_layers_to_add_input_quantizers():
                return True
        else:
            if (
                layer_name
                in self.quantsim_config.get_layers_to_skip_from_output_quantizers()
            ):
                return False
            if layer_name in self.quantsim_config.get_layers_to_add_output_quantizers():
                return True
        return current_value

    def _add_quantization_wrappers(self, module, prefix=""):

        if self.__is_quantized_module(module):
            return
        for module_name, module_ref in module.named_children():
            full_name = f"{prefix}.{module_name}" if prefix else module_name
            self.logger.info("nn.Module found : %s", module_ref)
            print("nn.Module found : %s", module_ref)

            if self.__is_quantizable_module(module_ref) and is_leaf_module(module_ref):
                quantized_module = self._create_quantizer_module(module_ref, full_name)
                if not quantized_module:
                    self.logger.info(f"Please register {full_name}")
                    continue
                setattr(module, module_name, quantized_module)
            else:
                self._add_quantization_wrappers(module_ref, prefix=full_name)

    def _create_quantizer_module(
        self, module_to_quantize: torch.nn.Module, module_name: str
    ):

        param_per_channel = get_task_configs(
            self.config, "ptq", "parameter_per_channel", False
        )
        act_per_channel = get_task_configs(
            self.config, "ptq", "activation_per_channel", False
        )
        param_per_tensor = get_task_configs(
            self.config, "ptq", "parameter_per_tensor", True
        )
        act_per_tensor = get_task_configs(
            self.config, "ptq", "activation_per_tensor", True
        )
        param_is_symmetric = get_task_configs(
            self.config, "ptq", "parameter_is_symmetric", True
        )
        act_is_symmetric = get_task_configs(
            self.config, "ptq", "activation_is_symmetric", False
        )

        global_activation_dtype = get_task_configs(
            self.config, "ptq", "activation_dtype", "int4"
        )
        global_param_dtype = get_task_configs(
            self.config, "ptq", "parameter_dtype", "int4"
        )
        global_activation_observer = get_task_configs(
            self.config, "ptq", "activation_observer"
        )
        global_weight_observer = get_task_configs(self.config, "ptq", "weight_observer")

        quantizer = ModuleReplacements.get_replacement(module_to_quantize)

        if not quantizer:
            self.logger.info(f"Please register {type(module_to_quantize)}")
            return None

        # registering parameter and activation dtype
        setattr(module_to_quantize, "activation_dtype", global_activation_dtype)
        setattr(module_to_quantize, "parameter_dtype", global_param_dtype)
        setattr(module_to_quantize, "parameter_observer", global_weight_observer)
        setattr(module_to_quantize, "activation_observer", global_activation_observer)
        setattr(module_to_quantize, "param_per_channel", param_per_channel)
        setattr(module_to_quantize, "act_per_channel", act_per_channel)
        setattr(module_to_quantize, "param_per_tensor", param_per_tensor)
        setattr(module_to_quantize, "act_per_tensor", act_per_tensor)
        setattr(module_to_quantize, "param_is_symmetric", param_is_symmetric)
        setattr(module_to_quantize, "act_is_symmetric", act_is_symmetric)

        self.logger.info(
            f"Replacing {type(module_to_quantize)} with {quantizer.__name__} for quantization"
        )
        quantized_module = quantizer(
            _module_to_wrap=module_to_quantize,
            # add_input_quantizers=add_input_quantizers,
            # add_output_quantizers=add_output_quantizers,
        )
        return quantized_module

    def prepare(
        self,
    ):
        """
        Recursively replace every weight-bearing layer with a QuantWrapper.

        Args:
            layer_types: if provided, only wrap these types; else wrap all with 'weight'.
            model:       internal use for recursion (initially None → uses self._model).
        """
        self.logger.info("=" * 60)
        self.logger.info("Preparing model for QFT (Quantization-Aware Fine-Tuning)")
        self.logger.info("=" * 60)
        self._model.eval()
        self.fuse_all_conv_bn(self._model)

        # try:
        self.traced_graph = fx.symbolic_trace(self._model)
        self.graphutils = ModelGraphUtils(self._model, self.traced_graph)
        self._model = prepare_model(self._model)
        self.graphutils.update_model(self._model)
        self._add_quantization_wrappers(self._model)
        self.graphutils.update_graph(self._model)
        self.apply_io_quantizer_flags_to_graph(self._model)
        self.logger.info(self._model)

        # except Exception as e:
        #     print(e)
        #     self.graphutils = ModelGraphUtils(self._model, None)
        #     self.logger.info("Model is not graph tracable. Cannot replace math ops.")
        #     self._add_quantization_wrappers(self._model)

        self.logger.info("Model after preparing")
        self.logger.info(self._model)
        print_quant_summary(self._model)
        return self._model

    def convert(self, model) -> None:
        """
        Convert the model to a quantized model.
        This method is called after the training and preparation steps.
        """

        for name, child in list(model.named_children()):
            if isinstance(child, QuantizationMixin):
                self.logger.info(f"Replacing Quantized module: {name}")
                module: QuantizationMixin = child
                scale_fp = module.get_scale_fp()
                wrapped_module: nn.Module = module._module_to_wrap
                for k, v in scale_fp.items():
                    wrapped_module.register_buffer(f"{k}_scale", v["scale"])
                    wrapped_module.register_buffer(f"{k}_zero_point", v["zero_point"])
                    setattr(wrapped_module, f"{k}_scale", v["scale"])
                    setattr(wrapped_module, f"{k}_zero_point", v["zero_point"])

                setattr(model, name, wrapped_module)
                continue

            self.convert(child)

    def export_model(self, model: torch.nn.Module, task_type: str) -> None:
        """
        Export the quantized model to a format suitable for deployment.
        """
        self.model_name = get_model_configs(self.config)["name"]
        export_format = self.config["export"]["format"]
        output_dir = self.config["export"]["output_dir"] + "/" + self.model_name
        if export_format == "onnx":
            export_model = model.apply(torch.ao.quantization.disable_observer)
            export_model.cpu()
            if not os.path.exists(output_dir):
                os.makedirs(output_dir, exist_ok=True)

            param_dtype = get_task_configs(
                self.config, task_type, "parameter_dtype", "int4"
            )
            opset = get_task_configs(self.config, "export", "opset", 13)
            attribute = get_task_configs(self.config, "export", "attribute", None)
            if attribute is not None:
                if not hasattr(export_model, attribute):
                    raise ValueError(f"Model has no attribute '{attribute}' for export")
                export_model = getattr(export_model, attribute)

            onnx_file = (
                f"{self.model_name}_quantized_model_{task_type}_{param_dtype}.onnx"
            )

            output_path = os.path.join(output_dir, onnx_file)

            self.logger.info("=" * 60)
            self.logger.info("Exporting the quantized model")
            self.logger.info("=" * 60)
            self.logger.info(f"Model Name      : {self.model_name}")
            self.logger.info(f"Export Format   : {export_format}")
            self.logger.info(f"Output Path     : {output_path}")
            self.logger.info("=" * 60)
            try:
                torch.onnx.export(
                    export_model,
                    torch.randn(self._input_shape),  # type: ignore
                    output_path,
                    export_params=True,
                    opset_version=opset,
                    do_constant_folding=True,
                    input_names=["input_image"],
                    output_names=["output"],
                )
                self.logger.info("Model export completed successfully.")
            except Exception as e:
                self.logger.error("Model export failed.")
                self.logger.exception(e)

    def __extract_encoding(self, module: CustomFakeQuantize) -> Dict:
        scale = module.scale
        zero_point = module.zero_point
        quant_min = module.quant_min
        quant_max = module.quant_max
        qscheme = module.qscheme
        dtype = str(module.dtype)

        bitwidth = get_bitwidth_from_string_dtype(dtype)

        is_symmetric = qscheme in [
            torch.per_tensor_symmetric,
            torch.per_channel_symmetric,
        ]

        if is_symmetric:
            encoding_min = -scale * ((quant_max - quant_min) / 2)
            encoding_max = scale * ((quant_max - quant_min) / 2)
        else:
            encoding_min = scale * (quant_min - zero_point)
            encoding_max = scale * (quant_max - zero_point)

        base_info = {
            "bitwidth": bitwidth,
            "quant_min": quant_min,
            "quant_max": quant_max,
            "qscheme": str(qscheme),
            "dtype": dtype,
            "is_symmetric": is_symmetric,
        }

        if scale.numel() == 1:
            return {
                **base_info,
                "encodings": [
                    {
                        "scale": scale.item(),
                        "offset": zero_point.item(),
                        "min": encoding_min.item(),
                        "max": encoding_max.item(),
                    }
                ],
            }

        else:
            encodings = []
            for i in range(scale.numel()):
                encodings.append(
                    {
                        "scale": scale[i].item(),
                        "offset": zero_point[i].item(),
                        "min": encoding_min[i].item(),
                        "max": encoding_max[i].item(),
                    }
                )
        return {**base_info, "encodings": encodings}

    def __get_quant_min_max(self, dtype):
        dtype = dtype.lower()
        if dtype == "int4":
            return -8, 7, torch.qint8
        elif dtype == "uint4":
            return 0, 15, torch.quint8
        elif dtype == "int8":
            return -128, 127, torch.qint8
        elif dtype == "uint8":
            return 0, 255, torch.quint8
        elif dtype == "int16":
            return -32768, 32767, torch.qint32
        #     return 0, 65535, torch.qint32
        else:
            raise ValueError(f"Unsupported dtype: {dtype}")

    def __get_encodings_from_model(self, model, encoding_dict=None) -> Dict[str, Dict]:
        """
        Extracts quantization parameters (scale, zero_point, min, max, etc.)
        from a model prepared using torch.ao.quantization.prepare_qat.

        Returns a hierarchical dictionary of encodings per FakeQuantize module.
        """
        encoding_dict = {}

        for name, child in model.named_modules():
            if not name:
                continue
            if "quant" in name:
                continue
            onnx_path = build_onnx_path_from_torch_name(name)
            cls_name = normalize_class_name(child.__class__.__name__)
            output_name = build_clean_onnx_path(f"{onnx_path}/{cls_name}")
            output_name = output_name.replace("Quantized", "")
            if "module_" in output_name:
                output_name.replace("module_", "")
            if isinstance(child, QuantizationMixin):
                data = {}
                for sub_name, sub_module in child.named_modules():
                    if isinstance(sub_module, (CustomFakeQuantize)):
                        encoding_info = self.__extract_encoding(sub_module)
                        data[f"{sub_name.replace('_quantizers','')}"] = encoding_info
                encoding_dict[output_name] = data

        return encoding_dict

    def generate_embeddings(self, attribute):
        """
        Iterates through model modules, collects quantization encodings from QuantizationMixin modules,
        and writes them to a JSON file as a list.
        """
        output_dir = (
            self.config["export"]["output_dir"]
            + "/"
            + get_model_configs(self.config, "name")
        )
        # if attribute is not None:
        #     if not hasattr(model, attribute):
        #         raise ValueError(f"Model has no attribute '{attribute}' for export")
        #     model = getattr(model, attribute)

        all_encodings = self.__get_encodings_from_model(
            self._model
            # if  isinstance(attribute, str) and not hasattr(self._model, attribute)
            # else  isinstance(attribute, str) and getattr(self._model, attribute)
        )

        task_type = (
            "qft"
            if get_model_configs(self.config, "qft")
            else "ptq" if get_model_configs(self.config, "ptq") else "pruning"
        )
        output_path = os.path.join(
            output_dir,
            f'{get_model_configs(self.config, "name")}_{task_type}_quantization_encodings.json',
        )
        os.makedirs(output_dir, exist_ok=True)

        with open(output_path, "w") as f:
            json.dump(all_encodings, f, indent=4)

        print(f"Quantization encodings saved to: {output_path}")
Update all method according to my quantsimparse
ignore aimet
i have sent one quantsimparser class right
some methods that you have used here is wrong
South Goa is a tranquil haven for travelers seeking a blend of luxury, nature, and authentic Goan charm. Unlike the bustling beaches of the North, South Goa offers serene stretches of sand, lush coconut groves, and a more laid-back vibe — making it the ideal destination for those who prefer a peaceful and personalized vacation experience. One of the best ways to enjoy this unique region is by staying in boutique resorts, which offer a mix of exclusivity, style, and local flavor that big hotel chains often lack.

Boutique resorts in South Goa are known for their distinctive charm and personalized hospitality. These resorts are typically smaller in size but big on experience — offering beautifully designed rooms, curated wellness programs, gourmet cuisine, and easy access to quiet beaches. If you're looking for an intimate getaway, a boutique resort like Soul Vacation
 offers the perfect escape. Located near Colva Beach, Soul Vacation blends Mediterranean-inspired design with Goan warmth, making it one of the top boutique choices in the region. Whether you're on a romantic holiday or a solo retreat, this resort offers tranquility and luxury in equal measure.

What sets boutique resorts apart in South Goa is their focus on individuality and attention to detail. Many of these properties are set in heritage homes or artistically crafted villas that reflect the cultural essence of Goa. With fewer rooms and a focus on bespoke service, guests enjoy a more intimate and relaxing experience. You’ll often find yoga sessions by the beach, spa treatments using local ingredients, and home-style meals that highlight traditional Goan recipes.

Beyond the resorts themselves, South Goa offers plenty to explore — from hidden waterfalls and spice plantations to historic churches and quiet fishing villages. Staying in a boutique resort puts you closer to these unique experiences, away from the crowds, while still enjoying all the comforts of a luxury holiday. It’s not just about accommodation; it’s about creating lasting memories in an environment that feels both indulgent and authentic.

Whether you're escaping the hustle of daily life or planning a special celebration, boutique resorts in South Goa offer a soulful, immersive, and unforgettable stay.

Visit: https://soulvacation.in/resort-in-south-goa/
{
	"blocks": [
		{
			"type": "header",
			"text": {
				"type": "plain_text",
				"text": ":star: What's on in Melbourne this week! :star:"
			}
		},
		{
			"type": "section",
			"text": {
				"type": "mrkdwn",
				"text": "\n\n Hey Melbourne, happy Monday and hope you all had a Beautiful weekend! Please see below for what's on this week. "
			}
		},
		{
			"type": "divider"
		},
		{
			"type": "header",
			"text": {
				"type": "plain_text",
				"text": "Xero Café :coffee:",
				"emoji": true
			}
		},
		{
			"type": "section",
			"text": {
				"type": "mrkdwn",
				"text": "\n :new-thing: *This week we are offering:* \n\n :caramel-slice: *Sweet Treats*: Selection of cookies \n\n :coffee: *Weekly Café Special*: English Toffee Coffee"
			}
		},
		{
			"type": "header",
			"text": {
				"type": "plain_text",
				"text": " Wednesday, 27th August :calendar-date-27:",
				"emoji": true
			}
		},
		{
			"type": "section",
			"text": {
				"type": "mrkdwn",
				"text": "\n\n:lunch: :flag-fr: Join us for an French lunch From *12pm* in the Wominjeka breakout space! Menu in the:thread: "
			}
		},
		{
			"type": "header",
			"text": {
				"type": "plain_text",
				"text": "Thursday, 28th August :calendar-date-28:",
				"emoji": true
			}
		},
		{
			"type": "section",
			"text": {
				"type": "mrkdwn",
				"text": ":breakfast: *Breakfast*: Join us for Breakfast from *8:30am - 10:30am* in the Wominjeka Breakout Space. Menu in the :thread: \n\n\n\n *What Else? :green_heart:*"
			}
		},
		{
			"type": "divider"
		},
		{
			"type": "section",
			"text": {
				"type": "mrkdwn",
				"text": " Feedback on our Boost Offerings? We want to hear more. Let us know what you love by filling out our form <https://docs.google.com/forms/d/e/1FAIpQLScGOSeS5zUI8WXEl0K4WGoQUkmpIHzAjLlEKWBob4sMPhDXmA/viewform|here.>  \n\n Stay tuned to this channel, and make sure you're subscribed to the <https://calendar.google.com/calendar/u/0?cid=Y19xczkyMjk5ZGlsODJzMjA4aGt1b3RnM2t1MEBncm91cC5jYWxlbmRhci5nb29nbGUuY29t|*Melbourne Social Calendar*> :party-wx:"
			}
		}
	]
}
C:\Users\Ismingiz>python --version
<style>
	/* menu open */
body{
	  overflow-x: hidden;
}
body.open-menu{
	overflow: hidden;
}
#header-main{
	transition: all 0.5s;
}
.scroll-web #header-main {
	background-color: #1B101D !important;
	top: 20px;
}
#menu-menu-header .menu-item.menu-item-has-children {
    margin-top: 0;
    padding: 0 25px;
}
#menu-menu-header .menu-item.menu-item-has-children > a {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 0;
}
#menu-menu-header .menu-item.menu-item-has-children > a:first-child:after {
    content: "";
    background-image: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iOSIgaGVpZ2h0PSI1IiB2aWV3Qm94PSIwIDAgOSA1IiBmaWxsPSJub25lIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPgo8cGF0aCBkPSJNNC4xNjc5NyA0Ljk0OTIyTDAgMC43ODEyNUwwLjczMDQ2OSAwLjA1MDc4MTJMMS4wOTU3IDAuNDE2MDE2TDQuMTY3OTcgMy41MDk3N0w3LjYwNTQ3IDAuMDcyMjY1Nkw4LjMzNTk0IDAuNzgxMjVMNC4xNjc5NyA0Ljk0OTIyWiIgZmlsbD0id2hpdGUiLz4KPC9zdmc+Cg==);
    width: 8.336px;
    height: 4.898px;
    position: unset;
    display: block;
}
.menu-icon {
	width: 50px;
	height: 50px;
	background: #f15a29; 
	display: flex;
	justify-content: center;
	align-items: center;
	cursor: pointer;
	flex-direction: column;
	transition: background 0.3s ease;
}
 
.menu-icon:hover {
	background: #27b4b2; 
}
 
.menu-icon span {
	display: block;
	width: 30px;
	height: 2px;
	background: white;
	margin: 2.5px 0;
	transition: width 0.3s ease;
}
 
.menu-icon:hover span:nth-child(2) {
	width: 16px;
}
@media(min-width: 981px){
	#menu-open{
		display: none !important;
	}
}
#menu-mobile-section{
	opacity: 0;
	visibility: hidden;
	transition: all 0.5s;
}
#menu-mobile-section.active {
    opacity: 1 !important;
    visibility: visible !important;
    z-index: 1000 !important;
}
#menu-mobile-section #menu-box {
    transition: all 0.5s;
}
#menu-mobile-section.active #menu-box {
    right: 0;
}
.header-menu-custom{
		height: calc(100% - 30px);
    overflow: scroll;
	 scrollbar-width: none;
    -ms-overflow-style: none;
}
.header-menu-custom::-webkit-scrollbar{
	display: none;
}
.header-menu-custom .menu {
    list-style: none;
    padding: 0;
}
#menu-mobile-section .header-menu-custom .sub-menu {
    list-style: none;
    padding: 0 0 0 20px !important;
    display: none;
}
#menu-mobile-section .header-menu-custom .et-show-dropdown ul.sub-menu {
    display: block;
}
#menu-mobile-section .header-menu-custom .menu a {
    color: #323D62;
    font-family: Figtree;
    font-size: 18px;
    font-style: normal;
    font-weight: 500;
    line-height: 1.35;
    letter-spacing: 0.24px;
    padding: 10px;
    width: 100%;
    display: block;
    position: relative;
}
#menu-mobile-section .header-menu-custom .menu .sub-menu a {
    font-size: 16px;
    line-height: 1.2;
    padding: 8px 0;
}
#menu-mobile-section .header-menu-custom .menu .menu-item-has-children >  a:after {
    content: "";
    width: 12px;
    height: 12px;
    position: absolute;
    background-image: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iOSIgaGVpZ2h0PSI1IiB2aWV3Qm94PSIwIDAgOSA1IiBmaWxsPSJub25lIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPgo8cGF0aCBkPSJNNC4xNjc5NyA0Ljk0OTIyTDAgMC43ODEyNUwwLjczMDQ2OSAwLjA1MDc4MTJMMS4wOTU3IDAuNDE2MDE2TDQuMTY3OTcgMy41MDk3N0w3LjYwNTQ3IDAuMDcyMjY1Nkw4LjMzNTk0IDAuNzgxMjVMNC4xNjc5NyA0Ljk0OTIyWiIgZmlsbD0iIzFCMTAxRCIvPgo8L3N2Zz4K);
    background-repeat: no-repeat;
    background-position: center;
    background-size: contain;
    top: 50%;
    transform: translateY(-50%);
    right: 0;
	  transition: all 0.3s;
}
#menu-mobile-section .header-menu-custom .menu .menu-item-has-children.et-hover >  a:after{
	transform: translateY(-50%) rotate(180deg);
}
 
.close-button {
    width: 30px;
    height: 30px;
    padding: 5px;
    position: relative;
    cursor: pointer;
    margin: 0 0 0 auto;
    background: #ED5D2C;
}
.close-button span {
    position: absolute;
    width: 2px;
    height: 60%;
    background-color: #ffffff;
    top: 50%;
    left: 50%;
    transform-origin: center;
    transition: all 0.3s;
}
.close-button:hover{
	background: #32B4B8;
}
.close-button .line1 {
    transform: translate(-50%, -50%) rotate(45deg);
}
.close-button .line2 {
    transform: translate(-50%, -50%) rotate(-45deg);
}
 
@media(min-width: 982px){
	.menu-icon.menu-open {
			display: none;
	}
}
@media(min-width: 767px){
	#menu-mobile-section.active #menu-box {
			width: 80%;
      max-width: 520px;
	}
}
@media(max-width: 767px){
	.menu-icon {
    width: 40px;
    height: 40px;
	}
}
@media(max-width: 520px){
	#menu-mobile-section.active #menu-box {
			width: 100%;
	}
	.menu-icon {
    width: 40px;
    height: 40px;
	}
}
</style>
// Menu
function custom_menu_shortcode( $atts ) {
    $atts = shortcode_atts(
        array(
            'name' => 'Menu Header',
        ),
        $atts
    );
 
    $menu = wp_nav_menu(
        array(
            'menu'            => $atts['name'],
            'container'       => 'nav',
            'container_class' => 'header-menu-custom',
            'echo'            => false,
            'depth'           => 0,
            'fallback_cb'     => false,
        )
    );
 
    return $menu ?: '';
}
add_shortcode( 'show_menu', 'custom_menu_shortcode' );
jQuery(document).ready(function($) {
    $('.menu-icon').click(function() {
        $('#menu-mobile-section').addClass('active');
        $('body').addClass('open-menu');
    });
 
    $('.menu-close').click(function() {
        $('#menu-mobile-section').removeClass('active');
        $('body').removeClass('open-menu');
    });
 
    $(document).click(function(event) {
        if (!$(event.target).closest('#menu-box, .menu-open').length) {
            $('#menu-mobile-section').removeClass('active');
            $('body').removeClass('open-menu');
        }
    });
 
    $('.header-menu-custom .menu .menu-item-has-children > a').on('click', function(e) {
        var $this = $(this);
        var subMenu = $this.siblings('.sub-menu');
        if (subMenu.length) {
            if (!subMenu.is(':visible')) {
                e.preventDefault();
                $('.header-menu-custom .menu .sub-menu').slideUp();
                $('.header-menu-custom .menu .menu-item-has-children').removeClass('active-sub');
                subMenu.slideDown();
                $this.parent().addClass('active-sub');
            }
        }
    });
	
	$(window).scroll(function() {
        if ($(this).scrollTop() > 0) {
            $('body').addClass('scroll-web');
        } else {
            $('body').removeClass('scroll-web');
        }
    });
});
// Utility function for delay
function wait(ms) {
  return new Promise(resolve => setTimeout(resolve, ms));
}

// Function to hit API with retries and delay
function hitApiWithRetry(url, options = {}, retries = 3, delay = 1000, callbacks = {}) {
  return new Promise(async (resolve, reject) => {
    for (let attempt = 1; attempt <= retries; attempt++) {
      try {
        console.log(`Attempt ${attempt}...`);
        let response = await fetch(url, options);

        if (!response.ok) {
          throw new Error(`HTTP error! Status: ${response.status}`);
        }

        let data = await response.json();

        // success callback
        if (callbacks.onSuccess) {
          callbacks.onSuccess(data, attempt);
        }

        return resolve(data); // resolve promise if success
      } catch (err) {
        console.error(`Error on attempt ${attempt}: ${err.message}`);

        // error callback
        if (callbacks.onError) {
          callbacks.onError(err, attempt);
        }

        if (attempt < retries) {
          console.log(`Retrying in ${delay}ms...`);
          await wait(delay);
        } else {
          return reject(`Failed after ${retries} attempts`);
        }
      }
    }
  });
}

// Usage example:
hitApiWithRetry(
  "https://jsonplaceholder.typicode.com/posts/1",
  {},
  3,  // retries
  2000, // delay (ms)
  {
    onSuccess: (data, attempt) => {
      console.log(`✅ Success on attempt ${attempt}`, data);
    },
    onError: (err, attempt) => {
      console.log(`❌ Failed on attempt ${attempt}: ${err.message}`);
    }
  }
)
.then(data => console.log("Final Data:", data))
.catch(err => console.log("Final Error:", err));
star

Tue Aug 26 2025 20:27:16 GMT+0000 (Coordinated Universal Time)

@jrg_300i #docker

star

Tue Aug 26 2025 18:44:04 GMT+0000 (Coordinated Universal Time)

@jrg_300i #docker

star

Tue Aug 26 2025 16:13:26 GMT+0000 (Coordinated Universal Time)

@jrg_300i #docker

star

Tue Aug 26 2025 12:35:26 GMT+0000 (Coordinated Universal Time) https://cryptiecraft.com/coinbase-clone-script/

@RileyQuinn #coinbaseclone #coinbaseclonescript

star

Tue Aug 26 2025 10:10:55 GMT+0000 (Coordinated Universal Time)

@StephenThevar

star

Tue Aug 26 2025 10:08:02 GMT+0000 (Coordinated Universal Time) https://www.newassignmenthelpaus.expert/physics-assignment-help

@henryjones0116

star

Tue Aug 26 2025 09:02:01 GMT+0000 (Coordinated Universal Time) https://maticz.com/blockchain-game-development

@carolinemax #maticz #blockchain #game

star

Mon Aug 25 2025 08:00:28 GMT+0000 (Coordinated Universal Time) https://www.wpbeginner.com/beginners-guide/how-to-add-http-security-headers-in-wordpress/#securityheaders-htaccess

@andersdeleuran #php #wordpress #htaccess #security

star

Sun Aug 24 2025 19:54:36 GMT+0000 (Coordinated Universal Time) https://www.pynerds.com/argparse-module-with-examples-turn-python-scripts-to-commandline-applicatio/

@pynerds #python

star

Sun Aug 24 2025 08:41:39 GMT+0000 (Coordinated Universal Time) https://www.patreon.com/posts/312-park-ave-era-136611522

@gina0993

star

Fri Aug 22 2025 22:55:34 GMT+0000 (Coordinated Universal Time) https://apploidstudios.com/

@edwardcoleman #mobileappdevelopment #crossplatformapp development

star

Fri Aug 22 2025 20:27:35 GMT+0000 (Coordinated Universal Time) https://cn.linux-console.net/?p

@jason11584

star

Fri Aug 22 2025 20:27:10 GMT+0000 (Coordinated Universal Time) https://cn.linux-console.net/?p

@jason11584

star

Fri Aug 22 2025 17:29:48 GMT+0000 (Coordinated Universal Time)

@jrg_300i #docker

star

Fri Aug 22 2025 15:41:50 GMT+0000 (Coordinated Universal Time)

@jrg_300i #docker

star

Fri Aug 22 2025 15:27:46 GMT+0000 (Coordinated Universal Time)

@jrg_300i #docker

star

Fri Aug 22 2025 15:25:19 GMT+0000 (Coordinated Universal Time)

@jrg_300i #docker

star

Fri Aug 22 2025 15:22:18 GMT+0000 (Coordinated Universal Time)

@jrg_300i #docker

star

Fri Aug 22 2025 15:08:13 GMT+0000 (Coordinated Universal Time)

@marcopinero #bash

star

Fri Aug 22 2025 15:06:03 GMT+0000 (Coordinated Universal Time)

@jrg_300i #docker

star

Fri Aug 22 2025 15:02:06 GMT+0000 (Coordinated Universal Time)

@jrg_300i #docker

star

Fri Aug 22 2025 13:51:40 GMT+0000 (Coordinated Universal Time)

@jrg_300i #docker

star

Fri Aug 22 2025 05:39:02 GMT+0000 (Coordinated Universal Time) https://www.thecryptoape.com/coinbase-clone-script

@Davidbrevis

star

Fri Aug 22 2025 05:15:06 GMT+0000 (Coordinated Universal Time)

@FOHWellington

star

Thu Aug 21 2025 09:47:03 GMT+0000 (Coordinated Universal Time) https://www.i95dev.com/dynamics-erp-integration/magento-2-dynamics-365-integration/

@alexpaul

star

Thu Aug 21 2025 09:44:49 GMT+0000 (Coordinated Universal Time) https://www.i95dev.com/dynamics-erp-integration/magento-microsoft-dynamics-365-business-central-connect/

@alexpaul

star

Wed Aug 20 2025 20:26:20 GMT+0000 (Coordinated Universal Time)

@jdeveloper #php

star

Wed Aug 20 2025 17:41:01 GMT+0000 (Coordinated Universal Time)

@jrg_300i #docker

star

Wed Aug 20 2025 17:35:40 GMT+0000 (Coordinated Universal Time)

@jrg_300i #docker

star

Wed Aug 20 2025 17:33:07 GMT+0000 (Coordinated Universal Time)

@jrg_300i #docker

star

Wed Aug 20 2025 17:32:22 GMT+0000 (Coordinated Universal Time)

@jrg_300i #docker

star

Wed Aug 20 2025 17:30:15 GMT+0000 (Coordinated Universal Time)

@jrg_300i #docker

star

Wed Aug 20 2025 17:21:47 GMT+0000 (Coordinated Universal Time)

@jrg_300i #docker

star

Wed Aug 20 2025 15:13:56 GMT+0000 (Coordinated Universal Time) https://codepen.io/rustcode/pen/YPPbxYX

@CallTheCops #undefined

star

Wed Aug 20 2025 15:13:53 GMT+0000 (Coordinated Universal Time) https://codepen.io/rustcode/pen/YPPbxYX

@CallTheCops #undefined

star

Wed Aug 20 2025 15:13:51 GMT+0000 (Coordinated Universal Time) https://codepen.io/rustcode/pen/YPPbxYX

@CallTheCops #undefined

star

Wed Aug 20 2025 15:05:47 GMT+0000 (Coordinated Universal Time) https://codepen.io/rustcode/pen/YPPbxYX

@CallTheCops #undefined

star

Wed Aug 20 2025 14:18:20 GMT+0000 (Coordinated Universal Time)

@jrg_300i #docker

star

Tue Aug 19 2025 18:40:28 GMT+0000 (Coordinated Universal Time)

@reiddd #javascript

star

Tue Aug 19 2025 16:12:49 GMT+0000 (Coordinated Universal Time) https://soulvacation.in/resort-in-south-goa/

@dagapam705

star

Tue Aug 19 2025 05:58:33 GMT+0000 (Coordinated Universal Time)

@FOHWellington

star

Mon Aug 18 2025 11:43:20 GMT+0000 (Coordinated Universal Time) https://maticz.com/rummy-game-development

@austinparker #rummy #rummygame #rummygamedevelopment #rummygameapp

star

Mon Aug 18 2025 05:58:16 GMT+0000 (Coordinated Universal Time)

@Diyorbek21

star

Mon Aug 18 2025 03:31:25 GMT+0000 (Coordinated Universal Time)

@quanganh141220 #wordpress #divi #menumobile

star

Mon Aug 18 2025 03:30:31 GMT+0000 (Coordinated Universal Time)

@quanganh141220 #wordpress #divi #menumobile

star

Mon Aug 18 2025 03:29:48 GMT+0000 (Coordinated Universal Time)

@quanganh141220 #wordpress #divi #menumobile

star

Sun Aug 17 2025 15:00:07 GMT+0000 (Coordinated Universal Time)

@E23CSEU1151

Save snippets that work with our extensions

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