npm package: @phemium-costaisa/cordova-plugin-webview-overlay
Description: Plugin de Cordova para mostrar múltiples WebViews nativos superpuestos que cargan contenido externo sin CORS. Soporta gestión independiente de múltiples instancias.
Author: Phemium-Costaisa
Latest version: 1.2.1

# Cordova WebView Overlay Plugin

Plugin de Cordova que permite mostrar múltiples WebViews nativos superpuestos a la vista principal de la aplicación. Cada WebView puede cargar contenido externo sin restricciones CORS y es gestionado independientemente mediante un identificador único.

## Características

- Soporte para múltiples instancias de WebView simultáneas
- Cada WebView es gestionado independientemente mediante un identificador único
- Muestra WebViews nativos (WKWebView en iOS, WebView en Android) superpuestos a la vista principal
- Permite establecer posición y tamaño individual de cada WebView
- Carga contenido web externo sin problemas de CORS
- Permite establecer cookies de sesión en cada WebView
- Proporciona métodos para ocultar, mostrar y destruir WebViews específicos o todos a la vez
- **Manejo personalizado del botón back de Android**: Intercepta el botón back nativo y ejecuta un callback personalizado en lugar de cerrar la aplicación
- Compatible con iOS y Android

## Requisitos

- Cordova CLI: ≥ 9.0.0
- Cordova Android: ≥ 6.0.0
- Cordova iOS: ≥ 4.0.0


## Instalación

```bash
cordova plugin add @phemium-costaisa/cordova-plugin-webview-overlay
```

## API

El plugin está disponible a través del objeto `cordova.plugins.webviewOverlay`.

### Métodos

#### open(url, x, y, width, height, cookie, successCallback, errorCallback)

Abre un WebView superpuesto con una URL específica y devuelve un identificador único de la instancia.

| Parámetro | Tipo | Descripción |
|-----------|------|-------------|
| url | String | URL a cargar en el WebView |
| x | Number | Posición X del WebView (en píxeles) |
| y | Number | Posición Y del WebView (en píxeles) |
| width | Number | Ancho del WebView (en píxeles) |
| height | Number | Alto del WebView (en píxeles) |
| cookie | Object | (Opcional) Objeto con la información de la cookie a establecer |
| successCallback | Function | (Opcional) Función llamada con el identificador de instancia cuando el WebView se ha cargado exitosamente |
| errorCallback | Function | (Opcional) Función llamada cuando hay un error |

**Objeto cookie**

El parámetro `cookie` es un objeto con las siguientes propiedades:

| Propiedad | Tipo | Descripción |
|-----------|------|-------------|
| name | String | Nombre de la cookie |
| value | String | Valor de la cookie |
| expires | String | (Opcional) Fecha de expiración en formato "expires=Wed, 21 Oct 2025 07:28:00 GMT" |
| path | String | (Opcional) Ruta para la cookie, por defecto es "/" |
| domain | String | (Opcional) Dominio para la cookie, ej: "domain=.ejemplo.com" |
| secure | Boolean | (Opcional) Indica si la cookie debe enviarse solo por HTTPS |
| url | String | (Obligatorio en Android) Indica la url del dominio |

#### hide(instanceId, successCallback, errorCallback)

Oculta el WebView superpuesto especificado sin destruirlo.

| Parámetro | Tipo | Descripción |
|-----------|------|-------------|
| instanceId | String | Identificador único de la instancia del WebView |
| successCallback | Function | (Opcional) Función llamada cuando el WebView se ha ocultado exitosamente |
| errorCallback | Function | (Opcional) Función llamada cuando hay un error |

#### show(instanceId, successCallback, errorCallback)

Muestra el WebView superpuesto especificado si estaba oculto.

| Parámetro | Tipo | Descripción |
|-----------|------|-------------|
| instanceId | String | Identificador único de la instancia del WebView |
| successCallback | Function | (Opcional) Función llamada cuando el WebView se ha mostrado exitosamente |
| errorCallback | Function | (Opcional) Función llamada cuando hay un error |

#### destroy(instanceId, successCallback, errorCallback)

Destruye el WebView superpuesto especificado y libera recursos.

| Parámetro | Tipo | Descripción |
|-----------|------|-------------|
| instanceId | String | Identificador único de la instancia del WebView |
| successCallback | Function | (Opcional) Función llamada cuando el WebView se ha destruido exitosamente |
| errorCallback | Function | (Opcional) Función llamada cuando hay un error |

#### destroyAll(successCallback, errorCallback)

Destruye todas las instancias de WebView y libera recursos.

| Parámetro | Tipo | Descripción |
|-----------|------|-------------|
| successCallback | Function | (Opcional) Función llamada cuando todas las instancias se han destruido exitosamente |
| errorCallback | Function | (Opcional) Función llamada cuando hay un error |

#### getActiveInstances(successCallback, errorCallback)

Obtiene la lista de identificadores de instancias activas.

| Parámetro | Tipo | Descripción |
|-----------|------|-------------|
| successCallback | Function | (Opcional) Función llamada con un array de identificadores de instancia |
| errorCallback | Function | (Opcional) Función llamada cuando hay un error |

#### setBackButtonCallback(instanceId, callback, errorCallback)

**[Solo Android]** Registra un callback para manejar el evento del botón back nativo de Android cuando el WebView está visible.

| Parámetro | Tipo | Descripción |
|-----------|------|-------------|
| instanceId | String | Identificador único de la instancia del WebView |
| callback | Function | Función llamada cuando se presiona el botón back mientras el WebView está visible |
| errorCallback | Function | (Opcional) Función llamada cuando hay un error |

**Objeto de respuesta del callback**

El callback recibirá un objeto con las siguientes propiedades:

| Propiedad | Tipo | Descripción |
|-----------|------|-------------|
| instanceId | String | El identificador de la instancia del WebView |
| action | String | Siempre será 'backButton' |

**Nota importante**: Este callback intercepta el comportamiento por defecto del botón back de Android. Cuando un WebView está visible y tiene un callback registrado, presionar el botón back **NO** cerrará la aplicación, sino que ejecutará el callback permitiéndote decidir qué acción tomar (ocultar el WebView, destruirlo, etc.).

## Ejemplos de Uso

### Abrir un WebView y gestionar la instancia

```javascript
// Abrir un WebView en la posición (10, 50) con tamaño 300x400 píxeles
cordova.plugins.webviewOverlay.open(
    'https://ejemplo.com', 
    10, 
    50, 
    300, 
    400, 
    null, // Sin cookie
    function(instanceId) {
        console.log('WebView cargado correctamente con ID:', instanceId);
        // Guardar el ID para usar más tarde
        window.myWebViewId = instanceId;
    },
    function(error) {
        console.error('Error al cargar WebView:', error);
    }
);
```

### Abrir múltiples WebViews

```javascript
// Variables para almacenar los IDs de las instancias
var webViewIds = [];

// Abrir primer WebView
cordova.plugins.webviewOverlay.open(
    'https://ejemplo1.com', 
    10, 10, 200, 300, 
    null,
    function(instanceId) {
        console.log('Primer WebView cargado:', instanceId);
        webViewIds.push(instanceId);
    }
);

// Abrir segundo WebView en diferente posición
cordova.plugins.webviewOverlay.open(
    'https://ejemplo2.com', 
    250, 10, 200, 300, 
    null,
    function(instanceId) {
        console.log('Segundo WebView cargado:', instanceId);
        webViewIds.push(instanceId);
    }
);
```

### Abrir un WebView con una cookie de sesión

```javascript
// Crear objeto cookie
var cookie = {
    name: 'sessionId',
    value: '12345abcde',
    expires: 'expires=Wed, 21 Oct 2025 07:28:00 GMT',
    path: '/',
    domain: 'domain=.ejemplo.com',
    url: 'https://www.ejemplo.com',
    secure: true
};

// Abrir un WebView con una cookie
cordova.plugins.webviewOverlay.open(
    'https://ejemplo.com', 
    10, 
    50, 
    300, 
    400, 
    cookie,
    function(instanceId) {
        console.log('WebView cargado correctamente con cookie, ID:', instanceId);
        window.myWebViewId = instanceId;
    },
    function(error) {
        console.error('Error al cargar WebView:', error);
    }
);
```

### Gestionar instancias específicas

```javascript
// Ocultar un WebView específico
cordova.plugins.webviewOverlay.hide(
    window.myWebViewId,
    function() {
        console.log('WebView ocultado correctamente');
    },
    function(error) {
        console.error('Error al ocultar WebView:', error);
    }
);

// Mostrar un WebView específico
cordova.plugins.webviewOverlay.show(
    window.myWebViewId,
    function() {
        console.log('WebView mostrado correctamente');
    },
    function(error) {
        console.error('Error al mostrar WebView:', error);
    }
);

// Destruir un WebView específico
cordova.plugins.webviewOverlay.destroy(
    window.myWebViewId,
    function() {
        console.log('WebView destruido correctamente');
        window.myWebViewId = null;
    },
    function(error) {
        console.error('Error al destruir WebView:', error);
    }
);
```

### Gestionar todas las instancias

```javascript
// Obtener lista de instancias activas
cordova.plugins.webviewOverlay.getActiveInstances(
    function(instanceIds) {
        console.log('Instancias activas:', instanceIds);
        console.log('Número de WebViews activos:', instanceIds.length);
    },
    function(error) {
        console.error('Error al obtener instancias activas:', error);
    }
);

// Destruir todas las instancias
cordova.plugins.webviewOverlay.destroyAll(
    function() {
        console.log('Todas las instancias destruidas correctamente');
    },
    function(error) {
        console.error('Error al destruir todas las instancias:', error);
    }
);
```

### Manejar el botón back de Android

```javascript
// Abrir un WebView y configurar el callback del botón back
cordova.plugins.webviewOverlay.open(
    'https://ejemplo.com', 
    10, 50, 300, 400, 
    null,
    function(instanceId) {
        console.log('WebView cargado:', instanceId);
        
        // Registrar el callback del botón back
        cordova.plugins.webviewOverlay.setBackButtonCallback(
            instanceId,
            function(data) {
                console.log('Botón back presionado en instancia:', data.instanceId);
                console.log('Acción:', data.action);
                
                // Decidir qué hacer: ocultar el WebView
                cordova.plugins.webviewOverlay.hide(
                    data.instanceId,
                    function() {
                        console.log('WebView ocultado por botón back');
                    }
                );
                
                // O destruirlo completamente:
                // cordova.plugins.webviewOverlay.destroy(data.instanceId);
            },
            function(error) {
                console.error('Error al registrar callback:', error);
            }
        );
    }
);

```

## Caso de uso práctico

Este plugin es ideal para situaciones donde necesitas:

- Mostrar múltiples contenidos de terceros simultáneamente sin restricciones CORS
- Implementar un dashboard con múltiples widgets web externos
- Crear una experiencia multi-ventana en tu aplicación móvil
- Gestionar contenido dinámico con diferentes sesiones o cookies

Ejemplo de flujo completo con múltiples instancias:

```javascript
document.addEventListener('deviceready', function() {
    // Array para gestionar múltiples WebViews
    var activeWebViews = [];
    
    // Función para abrir un nuevo WebView
    function openNewWebView(url, x, y, width, height) {
        cordova.plugins.webviewOverlay.open(
            url, x, y, width, height, null,
            function(instanceId) {
                console.log('Nuevo WebView abierto:', instanceId);
                activeWebViews.push({
                    id: instanceId,
                    url: url,
                    visible: true
                });
                updateWebViewCounter();
            },
            function(error) {
                console.error('Error al abrir WebView:', error);
            }
        );
    }
    
    // Función para cerrar un WebView específico
    function closeWebView(instanceId) {
        cordova.plugins.webviewOverlay.destroy(
            instanceId,
            function() {
                console.log('WebView cerrado:', instanceId);
                // Eliminar del array de activos
                activeWebViews = activeWebViews.filter(wv => wv.id !== instanceId);
                updateWebViewCounter();
            },
            function(error) {
                console.error('Error al cerrar WebView:', error);
            }
        );
    }
    
    // Función para actualizar contador en la UI
    function updateWebViewCounter() {
        document.getElementById('webview-counter').textContent = 
            'WebViews activos: ' + activeWebViews.length;
    }
    
    // Botones de la interfaz
    document.getElementById('open-webview1').addEventListener('click', function() {
        openNewWebView('https://ejemplo1.com', 10, 60, 150, 200);
    });
    
    document.getElementById('open-webview2').addEventListener('click', function() {
        openNewWebView('https://ejemplo2.com', 170, 60, 150, 200);
    });
    
    document.getElementById('close-all').addEventListener('click', function() {
        cordova.plugins.webviewOverlay.destroyAll(
            function() {
                console.log('Todos los WebViews cerrados');
                activeWebViews = [];
                updateWebViewCounter();
            }
        );
    });
    
    // Inicializar contador
    updateWebViewCounter();
    
}, false);
```

## Notas

- En iOS, el plugin utiliza WKWebView que tiene mejor rendimiento y menor consumo de memoria.
- En Android, el plugin configura el WebView para permitir el acceso sin restricciones CORS.
- Cada instancia de WebView consume memoria, por lo que es recomendable destruir las instancias que no se usen.
- Al salir de la aplicación, todas las instancias se destruyen automáticamente.
- Los identificadores de instancia son únicos y secuenciales (webview_1, webview_2, etc.).
- **Botón back de Android**: 
  - El método `setBackButtonCallback` solo funciona en Android.
  - Cuando un WebView está visible y tiene un callback registrado, el botón back ejecutará el callback en lugar del comportamiento por defecto.
  - Si hay múltiples WebViews visibles, se ejecutará el callback del WebView más reciente.
  - Si no hay callback registrado o no hay WebViews visibles, el botón back tendrá su comportamiento normal.
  - Es importante registrar el callback inmediatamente después de abrir el WebView para asegurar que funcione correctamente.