├── .gitattributes ├── LICENSE ├── README.md ├── archivo-a-base64 └── index.html ├── autocompletado-awesomplete ├── awesomplete.base.css ├── awesomplete.min.js ├── awesomplete.theme.css └── index.html ├── autocompletado-vue-bootstrap ├── con-api │ ├── index.html │ ├── script.js │ └── style.css └── estatico │ ├── index.html │ ├── script.js │ └── style.css ├── bot-transporte-telegram └── index.html ├── comandos-voz-annyang ├── annyang.min.js ├── index.html └── script.js ├── comunicacion-ventanas ├── hija.js ├── index.html ├── otra_ventana.html └── padre.js ├── consumir-proxy-android └── index.html ├── convertir-tabla-a-excel ├── estilos.css ├── index.html └── js │ ├── FileSaver.min.js │ ├── script.js │ ├── tableexport.min.js │ └── xlsx.full.min.js ├── debounce-vue └── index.html ├── detectar-bom └── index.html ├── encriptacion-js ├── bulma.min.css ├── index.html └── script.js ├── enviar-imagen-telegram └── index.html ├── enviar-mensaje-telegram └── index.html ├── envoltura-fetch └── HTTP.js ├── esteganografia ├── .gitignore ├── README.md ├── estilos.css ├── estilos_base.css ├── index.html ├── package-lock.json ├── package.json ├── postcss.config.js ├── script.js └── tailwind.config.js ├── firma-js ├── documento.html ├── estilo.css ├── index.html └── script.js ├── foto-camara-telegram ├── index.html └── script.js ├── generar-numeros-aleatorios ├── estilos.css └── index.html ├── generar-qr ├── index.html └── qrcode.min.js ├── html-a-pdf ├── html2pdf.bundle.min.js ├── index.html ├── lake-5534341_1280.jpg ├── script.js └── style.css ├── leer-codigo-barras ├── index.html └── script.js ├── llenar-select-arreglo ├── cadenas.html └── objetos.html ├── paint-js ├── estilo.css ├── index.html └── script.js ├── pixeles-imagen └── index.html ├── plugin-impresora-v1 ├── conector.js ├── index.html └── script.js ├── prevenir-enter-textarea ├── index.html └── script.js ├── proxy-android-plugin-v1 ├── conector.js └── index.html ├── proxy-android-plugin-v3 ├── detectar-e-imprimir.html ├── detectar-e-imprimir.js ├── index.html └── script.js ├── quaggajs ├── basico │ ├── index.html │ ├── script.js │ └── style.css ├── con-dibujo │ ├── index.html │ ├── script.js │ └── style.css └── lector-en-ventana │ ├── index.html │ ├── leer.html │ ├── quagga.min.js │ ├── script.js │ ├── scriptLeer.js │ └── style.css ├── recortar-imagen ├── css │ ├── cropper.min.css │ └── estilo.css ├── index.html ├── js │ ├── cropper.min.js │ └── script.js └── video-game-2234745_1920.jpg ├── reducir-tamaño-imagen └── index.html ├── reproducir-sonido ├── README.md ├── index.html ├── script.js └── sonido.flac ├── resolucion-imagen ├── code.png └── index.html ├── rotar-imagen ├── captura_codigo.png └── index.html ├── sweet-alert-2 ├── index.html ├── script.js └── sweetalert2.all.min.js ├── tasa-cambio ├── index.html ├── registrar_producto.html └── tasa.html ├── unir-archivos └── index.html ├── validacion-formularios └── index.html ├── webapp-telegram └── index.html └── wysiwyg ├── font ├── summernote.eot ├── summernote.ttf ├── summernote.woff └── summernote.woff2 ├── index.html ├── summernote-bs4.min.css └── summernote-bs4.min.js /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Luis Cabrera Benito 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | # ejemplos-javascript 3 | 4 | Múltiples ejemplos de JavaScript que no necesitan un repositorio aislado 5 | 6 | # Lista de ejemplos 7 | - Autocompletado con awesomplete | [Tutorial](https://parzibyte.me/blog/2019/12/03/autocompletado-javascript-html-awesomplete/) | [Demo](https://parzibyte.github.io/ejemplos-javascript/autocompletado-awesomplete/) 8 | 9 | - Exportar HTML a Excel | [Tutorial](https://parzibyte.me/blog/2019/12/04/exportar-tabla-html-excel-javascript/) | [Demo](https://parzibyte.github.io/ejemplos-javascript/convertir-tabla-a-excel/) 10 | 11 | - Comandos de voz en la web | [Tutorial](https://parzibyte.me/blog/2019/12/09/comandos-voz-web-javascript-annyang/) | [Demo](https://parzibyte.github.io/ejemplos-javascript/comandos-voz-annyang) 12 | 13 | - Sweet Alert 2 | [Tutorial](https://parzibyte.me/blog/2019/12/16/sweet-alert-2-tutorial-ejemplos/) | [Demo](https://parzibyte.github.io/ejemplos-javascript/sweet-alert-2) 14 | 15 | - Librería HTTP, envoltura de fetch | [Tutorial](https://parzibyte.me/blog/2020/01/09/creando-libreria-http-javascript/) 16 | 17 | - Comunicar ventanas en JavaScript | [Tutorial](https://parzibyte.me/blog/2020/06/12/comunicacion-ventanas-javascript/) | [Demo](https://parzibyte.github.io/ejemplos-javascript/comunicacion-ventanas/) 18 | 19 | - Autocompletado con Vue, Bootstrap y API (con api) | [Tutorial](https://parzibyte.me/blog/2020/06/18/autocompletado-bootstrap-vue-api/) | [Demo](https://parzibyte.github.io/ejemplos-javascript/autocompletado-vue-bootstrap/con-api/) 20 | 21 | - Autocompletado con Vue, Bootstrap y API (estatico) | [Tutorial](https://parzibyte.me/blog/2020/06/18/autocompletado-bootstrap-vue-api/) | [Demo](https://parzibyte.github.io/ejemplos-javascript/autocompletado-vue-bootstrap/estatico/) 22 | 23 | - Leer código de barras con JavaScript | [Tutorial](https://parzibyte.me/blog/2020/06/22/leer-codigo-barras-javascript-camara/) | [Demo](https://parzibyte.github.io/ejemplos-javascript/quaggajs/con-dibujo/) 24 | 25 | - Generar PDF a partir de HTML con html2pdf | [Tutorial](https://parzibyte.me/blog/2020/09/05/html-pdf-javascript/) | [Demo](https://parzibyte.github.io/ejemplos-javascript/html-a-pdf/) 26 | 27 | - Reproducir sonidos con JavaScript | [Tutorial](https://parzibyte.me/blog/2020/09/28/reproducir-sonidos-javascript/) | [Demo](https://parzibyte.github.io/ejemplos-javascript/reproducir-sonido/) 28 | 29 | - Validar formularios con JavaScript | [Tutorial](https://parzibyte.me/blog/2021/04/12/validar-formularios-javascript/) | [Demo](https://parzibyte.github.io/ejemplos-javascript/validacion-formularios/) 30 | 31 | - Generar códigos QR con JavaScript | [Tutorial](https://parzibyte.me/blog/2021/06/26/generar-codigos-qr-javascript/) | [Demo](https://parzibyte.github.io/ejemplos-javascript/generar-qr/) 32 | 33 | - Dibujar sobre un canvas usando JavaScript y el mouse | [Tutorial](https://parzibyte.me/blog/2021/09/08/dibujar-canvas-mouse-javascript/) | [Demo](https://parzibyte.github.io/ejemplos-javascript/paint-js/) 34 | 35 | - Solicitar firma de usuario con JavaScript y canvas | [Tutorial](https://parzibyte.me/blog/2021/09/09/solicitar-firma-manuscrita-javascript/) | [Demo](https://parzibyte.github.io/ejemplos-javascript/firma-js/) 36 | 37 | - Debounce con Vue.js | [Tutorial](https://parzibyte.me/blog/2021/10/01/debounce-con-vue/) | [Demo](https://parzibyte.github.io/ejemplos-javascript/debounce-vue/) 38 | 39 | - Reducir tamaño de imagen con JavaScript | [Tutorial](https://parzibyte.me/blog/2022/01/22/reducir-tamano-imagen-javascript/) | [Demo](https://parzibyte.github.io/ejemplos-javascript/reducir-tamaño-imagen/) 40 | 41 | - Encriptar y desencriptar información con JS y la Web Crypto API usando AES| [Tutorial](https://parzibyte.me/blog/2022/02/14/encriptacion-javascript-lado-cliente-usando-web-crypto-api/) | [Demo](https://parzibyte.github.io/ejemplos-javascript/encriptacion-js/) 42 | 43 | 44 | - Rotar una imagen con JavaScript y Canvas | [Tutorial](https://parzibyte.me/blog/2023/03/06/rotar-imagen-navegador-web-javascript-canvas/) | [Demo](https://parzibyte.github.io/ejemplos-javascript/rotar-imagen/) 45 | 46 | 47 | - Obtener resolución de imágenes con JavaScript | [Tutorial](https://parzibyte.me/blog/2023/10/19/javascript-obtener-resolucion-imagen/) | [Demo](https://parzibyte.github.io/ejemplos-javascript/resolucion-imagen/) 48 | 49 | - Convertir archivo a base64 | [Tutorial](https://parzibyte.me/blog/2023/10/19/javascript-convertir-archivo-base64/) | [Demo](https://parzibyte.github.io/ejemplos-javascript/archivo-a-base64/) 50 | 51 | - Editor WYSIWYG| [Tutorial](https://parzibyte.me/blog/2024/04/09/editor-wysiwyg-javascript/) | [Demo](https://parzibyte.github.io/ejemplos-javascript/wysiwyg/) 52 | 53 | 54 | - Llenar select con JavaScript | [Tutorial](https://parzibyte.me/blog/2024/04/16/javascript-llenar-select-arreglo/) | [Demo](https://parzibyte.github.io/ejemplos-javascript/llenar-select-arreglo/) 55 | 56 | - Leer pixeles de imagen (RGBA) | [Tutorial](https://parzibyte.me/blog/2024/04/23/javascript-lado-cliente-leer-pixeles-imagen/) | [Demo](https://parzibyte.github.io/ejemplos-javascript/pixeles-imagen/) 57 | 58 | - Consumir API de bots de Telegram | [Tutorial](https://parzibyte.me/blog/2024/05/01/enviar-mensaje-bot-telegram-usando-javascript-lado-cliente/) | [Demo](https://parzibyte.github.io/ejemplos-javascript/enviar-mensaje-telegram/) 59 | 60 | - Enviar imagen de JavaScript a Telegram | [Tutorial](https://parzibyte.me/blog/2024/05/21/enviar-foto-javascript-telegram-bot/) | [Demo](https://parzibyte.github.io/ejemplos-javascript/enviar-imagen-telegram/) 61 | 62 | - Enviar fotos periódicas de la cámara web con JavaScript a Telegram | [Tutorial](https://parzibyte.me/blog/2024/05/22/monitorear-camara-web-telegram-javascript/) | [Demo](https://parzibyte.github.io/ejemplos-javascript/foto-camara-telegram/?token=TU_TOKEN_VA_EN_ESTE_LUGAR&idChat=AQUI_VA_EL_ID_DE_CHAT) 63 | 64 | 65 | 66 | # Más sobre JavaScript 67 | 68 | Explora el contenido que tengo en mi blog sobre [JavaScript](https://parzibyte.me/blog/category/javascript) -------------------------------------------------------------------------------- /archivo-a-base64/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Convertir archivo a base64 8 | 9 | 10 | 11 | 12 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /autocompletado-awesomplete/awesomplete.base.css: -------------------------------------------------------------------------------- 1 | .awesomplete [hidden] { 2 | display: none; 3 | } 4 | 5 | .awesomplete .visually-hidden { 6 | position: absolute; 7 | clip: rect(0, 0, 0, 0); 8 | } 9 | 10 | .awesomplete { 11 | display: inline-block; 12 | position: relative; 13 | } 14 | 15 | .awesomplete > input { 16 | display: block; 17 | } 18 | 19 | .awesomplete > ul { 20 | position: absolute; 21 | left: 0; 22 | z-index: 1; 23 | min-width: 100%; 24 | box-sizing: border-box; 25 | list-style: none; 26 | padding: 0; 27 | margin: 0; 28 | background: #fff; 29 | } 30 | 31 | .awesomplete > ul:empty { 32 | display: none; 33 | } 34 | -------------------------------------------------------------------------------- /autocompletado-awesomplete/awesomplete.min.js: -------------------------------------------------------------------------------- 1 | // Awesomplete - Lea Verou - MIT license 2 | !function(){function t(t){var e=Array.isArray(t)?{label:t[0],value:t[1]}:"object"==typeof t&&"label"in t&&"value"in t?t:{label:t,value:t};this.label=e.label||e.value,this.value=e.value}function e(t,e,i){for(var n in e){var s=e[n],r=t.input.getAttribute("data-"+n.toLowerCase());"number"==typeof s?t[n]=parseInt(r):!1===s?t[n]=null!==r:s instanceof Function?t[n]=null:t[n]=r,t[n]||0===t[n]||(t[n]=n in i?i[n]:s)}}function i(t,e){return"string"==typeof t?(e||document).querySelector(t):t||null}function n(t,e){return o.call((e||document).querySelectorAll(t))}function s(){n("input.awesomplete").forEach(function(t){new r(t)})}var r=function(t,n){var s=this;r.count=(r.count||0)+1,this.count=r.count,this.isOpened=!1,this.input=i(t),this.input.setAttribute("autocomplete","off"),this.input.setAttribute("aria-expanded","false"),this.input.setAttribute("aria-owns","awesomplete_list_"+this.count),this.input.setAttribute("role","combobox"),this.options=n=n||{},e(this,{minChars:2,maxItems:10,autoFirst:!1,data:r.DATA,filter:r.FILTER_CONTAINS,sort:!1!==n.sort&&r.SORT_BYLENGTH,container:r.CONTAINER,item:r.ITEM,replace:r.REPLACE,tabSelect:!1},n),this.index=-1,this.container=this.container(t),this.ul=i.create("ul",{hidden:"hidden",role:"listbox",id:"awesomplete_list_"+this.count,inside:this.container}),this.status=i.create("span",{className:"visually-hidden",role:"status","aria-live":"assertive","aria-atomic":!0,inside:this.container,textContent:0!=this.minChars?"Type "+this.minChars+" or more characters for results.":"Begin typing for results."}),this._events={input:{input:this.evaluate.bind(this),blur:this.close.bind(this,{reason:"blur"}),keydown:function(t){var e=t.keyCode;s.opened&&(13===e&&s.selected?(t.preventDefault(),s.select()):9===e&&s.selected&&s.tabSelect?s.select():27===e?s.close({reason:"esc"}):38!==e&&40!==e||(t.preventDefault(),s[38===e?"previous":"next"]()))}},form:{submit:this.close.bind(this,{reason:"submit"})},ul:{mousedown:function(t){t.preventDefault()},click:function(t){var e=t.target;if(e!==this){for(;e&&!/li/i.test(e.nodeName);)e=e.parentNode;e&&0===t.button&&(t.preventDefault(),s.select(e,t.target))}}}},i.bind(this.input,this._events.input),i.bind(this.input.form,this._events.form),i.bind(this.ul,this._events.ul),this.input.hasAttribute("list")?(this.list="#"+this.input.getAttribute("list"),this.input.removeAttribute("list")):this.list=this.input.getAttribute("data-list")||n.list||[],r.all.push(this)};r.prototype={set list(t){if(Array.isArray(t))this._list=t;else if("string"==typeof t&&t.indexOf(",")>-1)this._list=t.split(/\s*,\s*/);else if((t=i(t))&&t.children){var e=[];o.apply(t.children).forEach(function(t){if(!t.disabled){var i=t.textContent.trim(),n=t.value||i,s=t.label||i;""!==n&&e.push({label:s,value:n})}}),this._list=e}document.activeElement===this.input&&this.evaluate()},get selected(){return this.index>-1},get opened(){return this.isOpened},close:function(t){this.opened&&(this.input.setAttribute("aria-expanded","false"),this.ul.setAttribute("hidden",""),this.isOpened=!1,this.index=-1,this.status.setAttribute("hidden",""),i.fire(this.input,"awesomplete-close",t||{}))},open:function(){this.input.setAttribute("aria-expanded","true"),this.ul.removeAttribute("hidden"),this.isOpened=!0,this.status.removeAttribute("hidden"),this.autoFirst&&-1===this.index&&this.goto(0),i.fire(this.input,"awesomplete-open")},destroy:function(){if(i.unbind(this.input,this._events.input),i.unbind(this.input.form,this._events.form),!this.options.container){var t=this.container.parentNode;t.insertBefore(this.input,this.container),t.removeChild(this.container)}this.input.removeAttribute("autocomplete"),this.input.removeAttribute("aria-autocomplete");var e=r.all.indexOf(this);-1!==e&&r.all.splice(e,1)},next:function(){var t=this.ul.children.length;this.goto(this.index-1&&e.length>0&&(e[t].setAttribute("aria-selected","true"),this.status.textContent=e[t].textContent+", list item "+(t+1)+" of "+e.length,this.input.setAttribute("aria-activedescendant",this.ul.id+"_item_"+this.index),this.ul.scrollTop=e[t].offsetTop-this.ul.clientHeight+e[t].clientHeight,i.fire(this.input,"awesomplete-highlight",{text:this.suggestions[this.index]}))},select:function(t,e){if(t?this.index=i.siblingIndex(t):t=this.ul.children[this.index],t){var n=this.suggestions[this.index];i.fire(this.input,"awesomplete-select",{text:n,origin:e||t})&&(this.replace(n),this.close({reason:"select"}),i.fire(this.input,"awesomplete-selectcomplete",{text:n}))}},evaluate:function(){var e=this,i=this.input.value;i.length>=this.minChars&&this._list&&this._list.length>0?(this.index=-1,this.ul.innerHTML="",this.suggestions=this._list.map(function(n){return new t(e.data(n,i))}).filter(function(t){return e.filter(t,i)}),!1!==this.sort&&(this.suggestions=this.suggestions.sort(this.sort)),this.suggestions=this.suggestions.slice(0,this.maxItems),this.suggestions.forEach(function(t,n){e.ul.appendChild(e.item(t,i,n))}),0===this.ul.children.length?(this.status.textContent="No results found",this.close({reason:"nomatches"})):(this.open(),this.status.textContent=this.ul.children.length+" results found")):(this.close({reason:"nomatches"}),this.status.textContent="No results found")}},r.all=[],r.FILTER_CONTAINS=function(t,e){return RegExp(i.regExpEscape(e.trim()),"i").test(t)},r.FILTER_STARTSWITH=function(t,e){return RegExp("^"+i.regExpEscape(e.trim()),"i").test(t)},r.SORT_BYLENGTH=function(t,e){return t.length!==e.length?t.length-e.length:t$&"),role:"option","aria-selected":"false",id:"awesomplete_list_"+this.count+"_item_"+n})},r.REPLACE=function(t){this.input.value=t.value},r.DATA=function(t){return t},Object.defineProperty(t.prototype=Object.create(String.prototype),"length",{get:function(){return this.label.length}}),t.prototype.toString=t.prototype.valueOf=function(){return""+this.label};var o=Array.prototype.slice;i.create=function(t,e){var n=document.createElement(t);for(var s in e){var r=e[s];if("inside"===s)i(r).appendChild(n);else if("around"===s){var o=i(r);o.parentNode.insertBefore(n,o),n.appendChild(o),null!=o.getAttribute("autofocus")&&o.focus()}else s in n?n[s]=r:n.setAttribute(s,r)}return n},i.bind=function(t,e){if(t)for(var i in e){var n=e[i];i.split(/\s+/).forEach(function(e){t.addEventListener(e,n)})}},i.unbind=function(t,e){if(t)for(var i in e){var n=e[i];i.split(/\s+/).forEach(function(e){t.removeEventListener(e,n)})}},i.fire=function(t,e,i){var n=document.createEvent("HTMLEvents");n.initEvent(e,!0,!0);for(var s in i)n[s]=i[s];return t.dispatchEvent(n)},i.regExpEscape=function(t){return t.replace(/[-\\^$*+?.()|[\]{}]/g,"\\$&")},i.siblingIndex=function(t){for(var e=0;t=t.previousElementSibling;e++);return e},"undefined"!=typeof self&&(self.Awesomplete=r),"undefined"!=typeof Document&&("loading"!==document.readyState?s():document.addEventListener("DOMContentLoaded",s)),r.$=i,r.$$=n,"object"==typeof module&&module.exports&&(module.exports=r)}(); 3 | //# sourceMappingURL=awesomplete.min.js.map 4 | -------------------------------------------------------------------------------- /autocompletado-awesomplete/awesomplete.theme.css: -------------------------------------------------------------------------------- 1 | .awesomplete > ul { 2 | border-radius: .3em; 3 | margin: .2em 0 0; 4 | background: hsla(0,0%,100%,.9); 5 | background: linear-gradient(to bottom right, white, hsla(0,0%,100%,.8)); 6 | border: 1px solid rgba(0,0,0,.3); 7 | box-shadow: .05em .2em .6em rgba(0,0,0,.2); 8 | text-shadow: none; 9 | } 10 | 11 | @supports (transform: scale(0)) { 12 | .awesomplete > ul { 13 | transition: .3s cubic-bezier(.4,.2,.5,1.4); 14 | transform-origin: 1.43em -.43em; 15 | } 16 | 17 | .awesomplete > ul[hidden], 18 | .awesomplete > ul:empty { 19 | opacity: 0; 20 | transform: scale(0); 21 | display: block; 22 | transition-timing-function: ease; 23 | } 24 | } 25 | 26 | /* Pointer */ 27 | .awesomplete > ul:before { 28 | content: ""; 29 | position: absolute; 30 | top: -.43em; 31 | left: 1em; 32 | width: 0; height: 0; 33 | padding: .4em; 34 | background: white; 35 | border: inherit; 36 | border-right: 0; 37 | border-bottom: 0; 38 | -webkit-transform: rotate(45deg); 39 | transform: rotate(45deg); 40 | } 41 | 42 | .awesomplete > ul > li { 43 | position: relative; 44 | padding: .2em .5em; 45 | cursor: pointer; 46 | } 47 | 48 | .awesomplete > ul > li:hover { 49 | background: hsl(200, 40%, 80%); 50 | color: black; 51 | } 52 | 53 | .awesomplete > ul > li[aria-selected="true"] { 54 | background: hsl(205, 40%, 40%); 55 | color: white; 56 | } 57 | 58 | .awesomplete mark { 59 | background: hsl(65, 100%, 50%); 60 | } 61 | 62 | .awesomplete li:hover mark { 63 | background: hsl(68, 100%, 41%); 64 | } 65 | 66 | .awesomplete li[aria-selected="true"] mark { 67 | background: hsl(86, 100%, 21%); 68 | color: inherit; 69 | } -------------------------------------------------------------------------------- /autocompletado-awesomplete/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Autocompletado con JavaScript 8 | 11 | 12 | 13 | 14 | 15 |

Probando Autocompletado

16 | By parzibyte 17 |
18 | 19 |
20 | 28 | 29 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /autocompletado-vue-bootstrap/con-api/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Autocompletado 8 | 9 | 10 | 11 | 14 | 15 | 16 | 17 | 18 |
19 |
20 |
21 |

Autocompletado

22 | By Parzibyte 23 |
24 | 26 |
27 |
28 | Artículo seleccionado es: 29 | {{articuloSeleccionado}} 30 |
31 |
32 |
33 |
34 | 35 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /autocompletado-vue-bootstrap/con-api/script.js: -------------------------------------------------------------------------------- 1 | Vue.component('vue-bootstrap-typeahead', VueBootstrapTypeahead) 2 | new Vue({ 3 | el: "#app", 4 | data: () => ({ 5 | busqueda: "", 6 | articulos: [], 7 | articuloSeleccionado: {}, 8 | }), 9 | methods: { 10 | // Función que se invoca cuando el texto de búsqueda cambia 11 | async buscarArticulos() { 12 | // La búsqueda como string. La tomamos del campo de texto 13 | const busqueda = this.busqueda; 14 | // Hacemos la petición a nuestra API pasándole la búsqueda. En este caso consulto la wikipedia 15 | const respuesta = await fetch(`https://es.wikipedia.org/w/api.php?action=query&list=search&srprop=snippet&format=json&origin=*&utf8=&srsearch=${busqueda}`); 16 | /** 17 | * Pequeña gran nota: En este caso primero decodifico como texto, luego quito el HTML 18 | * y después hago un JSON.parse así: 19 | let datos = await respuesta.text(); 20 | datos = this.quitarHTML(datos); 21 | datos = JSON.parse(datos); 22 | * En tu caso, si tu API no devuelve HTML (como debería ser) simplemente haz un: 23 | const datos = await respuesta.json(); 24 | */ 25 | let articulosWikipedia = await respuesta.text(); 26 | articulosWikipedia = this.quitarHTML(articulosWikipedia); 27 | articulosWikipedia = JSON.parse(articulosWikipedia); 28 | // Aquí todo depende de la respuesta de tu servidor. Si el mismo solo te da un arreglo, entonces asigna con: 29 | // this.articulos = datos; 30 | //Pero en este caso la API nos regresa un objeto que tiene la propiedad "query" y dentro de la misma tiene la propiedad search 31 | this.articulos = articulosWikipedia.query.search; 32 | }, 33 | quitarHTML(html) { 34 | return html.replace(/<\/?[^>]+>/gi, ''); 35 | }, 36 | // Función que convierte el objeto a cadena. Es llamado para mostrarse en la lista 37 | serializarValor(articulo) { 38 | return articulo.title + " - " + articulo.snippet; 39 | }, 40 | onArticuloSeleccionado(articulo) { 41 | this.articuloSeleccionado = articulo; 42 | } 43 | } 44 | }); -------------------------------------------------------------------------------- /autocompletado-vue-bootstrap/con-api/style.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/parzibyte/ejemplos-javascript/31d317bcce8f9075d2c7a76b8e969a895f800212/autocompletado-vue-bootstrap/con-api/style.css -------------------------------------------------------------------------------- /autocompletado-vue-bootstrap/estatico/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Autocompletado 8 | 9 | 10 | 11 | 14 | 15 | 16 | 17 | 18 |
19 |
20 |
21 |

Autocompletado

22 |
23 | 25 |
26 |
27 | El nombre seleccionado es: 28 | {{nombreSeleccionado}} 29 |
30 |
31 |
32 |
33 | 34 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /autocompletado-vue-bootstrap/estatico/script.js: -------------------------------------------------------------------------------- 1 | Vue.component('vue-bootstrap-typeahead', VueBootstrapTypeahead) 2 | new Vue({ 3 | el: "#app", 4 | data: () => ({ 5 | busqueda: "", 6 | nombres: ["Luis Cabrera", "Leon Scott Kennedy", "Claire Redfield", "Noble 6", "Chris Redfield", "Madeline", "Cuphead", "Mugman", "Ori"], 7 | nombreSeleccionado: null, 8 | }), 9 | methods: { 10 | onNombreSeleccionado(nombre) { 11 | this.nombreSeleccionado = nombre; 12 | } 13 | } 14 | }); -------------------------------------------------------------------------------- /autocompletado-vue-bootstrap/estatico/style.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/parzibyte/ejemplos-javascript/31d317bcce8f9075d2c7a76b8e969a895f800212/autocompletado-vue-bootstrap/estatico/style.css -------------------------------------------------------------------------------- /bot-transporte-telegram/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Telegram Web App 9 | 10 | 11 | 12 |

Elige tus opciones

13 | 14 | 16 |

17 | 18 | 20 |

21 |

Inicio

22 | 23 |

Fin

24 | 25 |

Radio en metros

26 | 27 |
28 | Seleccione los días de la semana 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 |
51 | 52 | 53 | 116 | 117 | 118 | -------------------------------------------------------------------------------- /comandos-voz-annyang/annyang.min.js: -------------------------------------------------------------------------------- 1 | "use strict";var _typeof="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e}; 2 | //! annyang 3 | //! version : 2.6.1 4 | //! author : Tal Ater @TalAter 5 | //! license : MIT 6 | //! https://www.TalAter.com/annyang/ 7 | //! annyang 8 | //! version : 2.6.1 9 | //! author : Tal Ater @TalAter 10 | //! license : MIT 11 | //! https://www.TalAter.com/annyang/ 12 | !function(e,n){"function"==typeof define&&define.amd?define([],function(){return e.annyang=n(e)}):"object"===("undefined"==typeof module?"undefined":_typeof(module))&&module.exports?module.exports=n(e):e.annyang=n(e)}("undefined"!=typeof window?window:void 0,function(r,i){var t,o=r.SpeechRecognition||r.webkitSpeechRecognition||r.mozSpeechRecognition||r.msSpeechRecognition||r.oSpeechRecognition;if(!o)return null;var a,c,s=[],u={start:[],error:[],end:[],soundstart:[],result:[],resultMatch:[],resultNoMatch:[],errorNetwork:[],errorPermissionBlocked:[],errorPermissionDenied:[]},f=0,l=0,d=!1,p="font-weight: bold; color: #00f;",g=!1,m=!1,h=/\s*\((.*?)\)\s*/g,y=/(\(\?:[^)]+\))\?/g,b=/(\(\?)?:\w+/g,v=/\*\w+/g,w=/[\-{}\[\]+?.,\\\^$|#]/g,S=function(e){for(var n=arguments.length,t=Array(1 2 | 3 | 8 | 9 | 10 | 12 | 13 | 14 | Comados de voz en la web con Annyang 15 | 20 | 21 | 22 | 23 | 24 | 25 |
26 |
27 |
28 |

Comandos de voz en la web con JavaScript y Annyang

29 | By Parzibyte 30 |

31 | Intenta con: 32 |
Hola 33 |
Reporte de ventas de {mes} 34 |
Enviar correo a {dirección} 35 |
Mi nombre es {Nombre} y tengo {años} años 36 |

37 |
38 |
39 |

Comandos reconocidos

40 |
41 | 42 |

43 |
44 |
45 |
46 |

Voz escuchada, sin reconocer

47 |
48 |

49 |

50 |
51 |
52 | 53 |
54 |
55 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /comandos-voz-annyang/script.js: -------------------------------------------------------------------------------- 1 | document.addEventListener("DOMContentLoaded", function() { 2 | if (!annyang) { 3 | return alert("Lo siento, tu navegador no soporta el reconocimiento de voz :("); 4 | } 5 | const $comandosReconocidos = document.querySelector("#comandosReconocidos"), 6 | $vozDetectada = document.querySelector("#vozDetectada"); 7 | 8 | const loguearComandoReconocido = contenido => { 9 | $comandosReconocidos.innerHTML += contenido + "
"; 10 | }; 11 | 12 | const loguearVozDetectada = contenido => { 13 | $vozDetectada.innerHTML += contenido + "
"; 14 | }; 15 | 16 | annyang.setLanguage("es-MX"); 17 | let comandos = { 18 | "hola": () => { 19 | loguearComandoReconocido(`Hola mundo!`); 20 | 21 | }, 22 | "reporte de ventas de *mes": mes => { 23 | if ("enero,febrero,marzo,abril,mayo,junio,julio,agosto,septiembre,octubre,noviembre,diciembre".split(",").indexOf(mes.toLowerCase()) === -1) { 24 | return; 25 | } 26 | loguearComandoReconocido(`Ok te muestro el reporte de ventas de ${mes}`); 27 | }, 28 | "enviar correo a *usuario": usuario => { 29 | let usuarioCorregido = usuario.replace(/\ /g, "").replace(/arroba/g, "@").toLowerCase(); 30 | loguearComandoReconocido(`Originalmente es ${usuario} pero creo que el correcto es ${usuarioCorregido}`); 31 | }, 32 | "mi nombre es *nombre y tengo *anios años": (nombre, anios) => { 33 | loguearComandoReconocido(`Hola ${nombre} es genial que tu edad sea ${anios} :)`); 34 | } 35 | }; 36 | 37 | annyang.addCommands(comandos); 38 | 39 | annyang.addCallback("result", frases => { 40 | loguearVozDetectada(`Probablemente has dicho:
${frases.join("
")}`); 41 | }); 42 | 43 | annyang.start(); 44 | }); -------------------------------------------------------------------------------- /comunicacion-ventanas/hija.js: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Programado por Luis Cabrera Benito 4 | ____ _____ _ _ _ 5 | | _ \ | __ \ (_) | | | 6 | | |_) |_ _ | |__) |_ _ _ __ _____| |__ _ _| |_ ___ 7 | | _ <| | | | | ___/ _` | '__|_ / | '_ \| | | | __/ _ \ 8 | | |_) | |_| | | | | (_| | | / /| | |_) | |_| | || __/ 9 | |____/ \__, | |_| \__,_|_| /___|_|_.__/ \__, |\__\___| 10 | __/ | __/ | 11 | |___/ |___/ 12 | 13 | 14 | Blog: https://parzibyte.me/blog 15 | Ayuda: https://parzibyte.me/blog/contrataciones-ayuda/ 16 | Contacto: https://parzibyte.me/blog/contacto/ 17 | */ 18 | 19 | 20 | // Este script es incluido en la ventana que abre la principal 21 | const $btnEnviar = document.querySelector("#btnEnviar"), 22 | $mensaje = document.querySelector("#mensaje"), 23 | $mensajeRecibido = document.querySelector("#mensajeRecibido"); 24 | $btnEnviar.addEventListener("click", () => { 25 | const mensaje = $mensaje.value; 26 | if (!mensaje) return alert("Escribe un mensaje"); 27 | if (window.opener) { 28 | window.opener.establecerMensaje(mensaje); 29 | } 30 | }); 31 | 32 | 33 | // Definición de función desde la que nos llama el padre 34 | window.establecerMensaje = function (mensaje) { 35 | $mensajeRecibido.textContent = mensaje; 36 | } -------------------------------------------------------------------------------- /comunicacion-ventanas/index.html: -------------------------------------------------------------------------------- 1 | 18 | 19 | 20 | 21 | 22 | 23 | Ventana padre 24 | 25 | 26 | 27 |
28 | 29 |

30 |

Si la ventana está abierta...

31 | 32 | 33 |

Aquí aparecerá el mensaje recibido

34 | 35 | 36 | -------------------------------------------------------------------------------- /comunicacion-ventanas/otra_ventana.html: -------------------------------------------------------------------------------- 1 | 18 | 19 | 20 | 21 | 22 | 23 | Ventana hija 24 | 25 | 26 | 27 |   28 | 29 |
30 |

Aquí aparece el mensaje recibido

31 | 32 | 33 | -------------------------------------------------------------------------------- /comunicacion-ventanas/padre.js: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Programado por Luis Cabrera Benito 4 | ____ _____ _ _ _ 5 | | _ \ | __ \ (_) | | | 6 | | |_) |_ _ | |__) |_ _ _ __ _____| |__ _ _| |_ ___ 7 | | _ <| | | | | ___/ _` | '__|_ / | '_ \| | | | __/ _ \ 8 | | |_) | |_| | | | | (_| | | / /| | |_) | |_| | || __/ 9 | |____/ \__, | |_| \__,_|_| /___|_|_.__/ \__, |\__\___| 10 | __/ | __/ | 11 | |___/ |___/ 12 | 13 | 14 | Blog: https://parzibyte.me/blog 15 | Ayuda: https://parzibyte.me/blog/contrataciones-ayuda/ 16 | Contacto: https://parzibyte.me/blog/contacto/ 17 | */ 18 | const $btnAbrir = document.querySelector("#btnAbrir"), 19 | $mensaje = document.querySelector("#mensaje"), 20 | $btnEnviarMensaje = document.querySelector("#btnEnviarMensaje"), 21 | $mensajeRecibido = document.querySelector("#mensajeRecibido"); 22 | 23 | let ventana; 24 | $btnAbrir.addEventListener("click", () => { 25 | ventana = window.open("otra_ventana.html"); 26 | }); 27 | 28 | $btnEnviarMensaje.addEventListener("click", () => { 29 | let mensaje = $mensaje.value; 30 | if (!mensaje) { 31 | return; 32 | } 33 | if (ventana) { 34 | ventana.establecerMensaje(mensaje); 35 | } 36 | }); 37 | 38 | // Llamada desde la hija 39 | function establecerMensaje(mensaje) { 40 | $mensajeRecibido.textContent = mensaje; 41 | } -------------------------------------------------------------------------------- /consumir-proxy-android/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Consumir proxy de Android 8 | 9 | 10 | 11 | 12 |
13 | 14 |
15 | 16 | 20 |
21 | 22 |
23 | 24 |
25 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /convertir-tabla-a-excel/estilos.css: -------------------------------------------------------------------------------- 1 | table { 2 | border-collapse: collapse; 3 | } 4 | 5 | table, 6 | th, 7 | td { 8 | border: 1px solid black; 9 | } 10 | 11 | th, 12 | td { 13 | padding: 5px; 14 | } -------------------------------------------------------------------------------- /convertir-tabla-a-excel/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Exportar tabla HTML a Excel 8 | 9 | 10 | 11 | 12 | 13 | 14 |

Tabla HTML a Excel

15 |

16 | Exportar los datos de una tabla de una página web a una hoja de 17 | cálculo 18 | de Excel 19 |
20 | By Parzibyte 21 |

22 | 23 |
24 |
25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 |
LenguajeSitio webAlgunos usos
PHPphp.netAplicaciones web
Pythonpython.orgAplicaciones web y de escritorio. Machine learning
Gogolang.orgAplicaciones web y de escritorio
51 | 52 | 53 | -------------------------------------------------------------------------------- /convertir-tabla-a-excel/js/FileSaver.min.js: -------------------------------------------------------------------------------- 1 | /*! FileSaver.js v1.3.6 2 | * 3 | * A saveAs() FileSaver implementation. 4 | * 5 | * By Travis Clarke, https://travismclarke.com 6 | * By Eli Grey, http://eligrey.com 7 | * 8 | * License: MIT (https://github.com/clarketm/FileSaver.js/blob/master/LICENSE.md) 9 | */ 10 | (function(e,t){if(typeof exports==="object"&&typeof exports.nodeName!=="string"){module.exports=e.document?t(e,true):function(e){if(!e.document){throw new Error("FileSaver requires a window with a document")}return t(e)}}else{t(e)}})(window||this,function(e,t){"use strict";if(typeof e==="undefined"||typeof navigator!=="undefined"&&/MSIE [1-9]\./.test(navigator.userAgent)){return}var n=e.document,r=function(){return e.URL||e.webkitURL||e},o=n.createElementNS("http://www.w3.org/1999/xhtml","a"),i="download"in o,a=function(e){var t=new MouseEvent("click");e.dispatchEvent(t)},f=/constructor/i.test(e.HTMLElement)||e.safari,u=/CriOS\/[\d]+/.test(navigator.userAgent),c=function(t){(e.setImmediate||e.setTimeout)(function(){throw t},0)},d="application/octet-stream",s=1e3*40,l=function(e){var t=function(){if(typeof e==="string"){r().revokeObjectURL(e)}else{e.remove()}};setTimeout(t,s)},p=function(e,t,n){t=[].concat(t);var r=t.length;while(r--){var o=e["on"+t[r]];if(typeof o==="function"){try{o.call(e,n||e)}catch(e){c(e)}}}},w=function(e){if(/^\s*(?:text\/\S*|application\/xml|\S*\/\S*\+xml)\s*;.*charset\s*=\s*utf-8/i.test(e.type)){return new Blob([String.fromCharCode(65279),e],{type:e.type})}return e},m=function(t,n,c){if(!c){t=w(t)}var s=this,m=t.type,v=m===d,y,h=function(){p(s,"writestart progress write writeend".split(" "))},S=function(){if((u||v&&f)&&e.FileReader){var n=new FileReader;n.onloadend=function(){var t=u?n.result:n.result.replace(/^data:[^;]*;/,"data:attachment/file;");var r=e.open(t,"_blank");if(!r)e.location.href=t;t=undefined;s.readyState=s.DONE;h()};n.readAsDataURL(t);s.readyState=s.INIT;return}if(!y){y=r().createObjectURL(t)}if(v){e.location.href=y}else{var o=e.open(y,"_blank");if(!o){e.location.href=y}}s.readyState=s.DONE;h();l(y)};s.readyState=s.INIT;if(i){y=r().createObjectURL(t);setTimeout(function(){o.href=y;o.download=n;a(o);h();l(y);s.readyState=s.DONE});return}S()},v=m.prototype,y=function(e,t,n){return new m(e,t||e.name||"download",n)};if(typeof navigator!=="undefined"&&navigator.msSaveOrOpenBlob){y=function(e,t,n){t=t||e.name||"download";if(!n){e=w(e)}return navigator.msSaveOrOpenBlob(e,t)}}v.abort=function(){};v.readyState=v.INIT=0;v.WRITING=1;v.DONE=2;v.error=v.onwritestart=v.onprogress=v.onwrite=v.onabort=v.onerror=v.onwriteend=null;if(typeof define==="function"&&define.amd){define("file-saverjs",[],function(){return y})}if(typeof t==="undefined"){e.saveAs=y}return y}); 11 | -------------------------------------------------------------------------------- /convertir-tabla-a-excel/js/script.js: -------------------------------------------------------------------------------- 1 | const $btnExportar = document.querySelector("#btnExportar"), 2 | $tabla = document.querySelector("#tabla"); 3 | 4 | $btnExportar.addEventListener("click", function() { 5 | let tableExport = new TableExport($tabla, { 6 | exportButtons: false, // No queremos botones 7 | filename: "Mi tabla de Excel", //Nombre del archivo de Excel 8 | sheetname: "Mi tabla de Excel", //Título de la hoja 9 | }); 10 | let datos = tableExport.getExportData(); 11 | let preferenciasDocumento = datos.tabla.xlsx; 12 | tableExport.export2file(preferenciasDocumento.data, preferenciasDocumento.mimeType, preferenciasDocumento.filename, preferenciasDocumento.fileExtension, preferenciasDocumento.merges, preferenciasDocumento.RTL, preferenciasDocumento.sheetname); 13 | }); -------------------------------------------------------------------------------- /debounce-vue/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Debounce con Vue - By Parzibyte 9 | 10 | 11 | 12 | 13 |
14 | 15 |
16 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /detectar-bom/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | BOM detector 8 | 9 | 10 | 11 | 12 |
13 | 83 | 84 | 85 | -------------------------------------------------------------------------------- /encriptacion-js/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Encriptar y desencriptar información con JavaScript - By Parzibyte 8 | 9 | 10 | 16 | 17 | 18 | 19 | 49 | 59 |
60 |
61 |
62 |

Encriptar

63 |
64 | 65 |
66 | 68 |
69 |
70 |
71 | 72 |
73 | 75 |
76 |
77 | 78 |
79 | 80 |
81 | 82 |
83 |
84 |
85 |
86 |

Desencriptar

87 |
88 | 89 |
90 | 92 |
93 |
94 |
95 | 96 |
97 | 99 |
100 |
101 | 102 |
103 | 104 |
105 | 106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |

114 | Cifrar y descifrar información con JavaScript usando la Web Crypto API | By Parzibyte 116 |

117 |
118 |
119 | 120 | 121 | -------------------------------------------------------------------------------- /encriptacion-js/script.js: -------------------------------------------------------------------------------- 1 | document.addEventListener("DOMContentLoaded", async () => { 2 | const $contraseñaEncriptar = document.querySelector("#contraseñaEncriptar"), 3 | $informacionEncriptar = document.querySelector("#informacionEncriptar"), 4 | $resultadoEncriptacion = document.querySelector("#resultadoEncriptacion"), 5 | $botonEncriptar = document.querySelector("#botonEncriptar"), 6 | $contraseñaDesencriptar = document.querySelector("#contraseñaDesencriptar"), 7 | $informacionDesencriptar = document.querySelector("#informacionDesencriptar"), 8 | $resultadoDesencriptacion = document.querySelector("#resultadoDesencriptacion"), 9 | $botonDesencriptar = document.querySelector("#botonDesencriptar"); 10 | 11 | const bufferABase64 = buffer => btoa(String.fromCharCode(...new Uint8Array(buffer))); 12 | const base64ABuffer = buffer => Uint8Array.from(atob(buffer), c => c.charCodeAt(0)); 13 | const LONGITUD_SAL = 16; 14 | const LONGITUD_VECTOR_INICIALIZACION = LONGITUD_SAL; 15 | const derivacionDeClaveBasadaEnContraseña = async (contraseña, sal, iteraciones, longitud, hash, algoritmo = 'AES-CBC') => { 16 | const encoder = new TextEncoder(); 17 | let keyMaterial = await window.crypto.subtle.importKey( 18 | 'raw', 19 | encoder.encode(contraseña), 20 | { name: 'PBKDF2' }, 21 | false, 22 | ['deriveKey'] 23 | ); 24 | return await window.crypto.subtle.deriveKey( 25 | { 26 | name: 'PBKDF2', 27 | salt: encoder.encode(sal), 28 | iterations: iteraciones, 29 | hash 30 | }, 31 | keyMaterial, 32 | { name: algoritmo, length: longitud }, 33 | false, 34 | ['encrypt', 'decrypt'] 35 | ); 36 | } 37 | const encriptar = async (contraseña, textoPlano) => { 38 | const encoder = new TextEncoder(); 39 | const sal = window.crypto.getRandomValues(new Uint8Array(LONGITUD_SAL)); 40 | const vectorInicializacion = window.crypto.getRandomValues(new Uint8Array(LONGITUD_VECTOR_INICIALIZACION)); 41 | const bufferTextoPlano = encoder.encode(textoPlano); 42 | const clave = await derivacionDeClaveBasadaEnContraseña(contraseña, sal, 100000, 256, 'SHA-256'); 43 | const encrypted = await window.crypto.subtle.encrypt( 44 | { name: "AES-CBC", iv: vectorInicializacion }, 45 | clave, 46 | bufferTextoPlano 47 | ); 48 | return bufferABase64([ 49 | ...sal, 50 | ...vectorInicializacion, 51 | ...new Uint8Array(encrypted) 52 | ]); 53 | }; 54 | 55 | const desencriptar = async (contraseña, encriptadoEnBase64) => { 56 | const decoder = new TextDecoder(); 57 | const datosEncriptados = base64ABuffer(encriptadoEnBase64); 58 | const sal = datosEncriptados.slice(0, LONGITUD_SAL); 59 | const vectorInicializacion = datosEncriptados.slice(0 + LONGITUD_SAL, LONGITUD_SAL + LONGITUD_VECTOR_INICIALIZACION); 60 | const clave = await derivacionDeClaveBasadaEnContraseña(contraseña, sal, 100000, 256, 'SHA-256'); 61 | const datosDesencriptadosComoBuffer = await window.crypto.subtle.decrypt( 62 | { name: "AES-CBC", iv: vectorInicializacion }, 63 | clave, 64 | datosEncriptados.slice(LONGITUD_SAL + LONGITUD_VECTOR_INICIALIZACION) 65 | ); 66 | return decoder.decode(datosDesencriptadosComoBuffer); 67 | } 68 | $botonEncriptar.onclick = async () => { 69 | const contraseña = $contraseñaEncriptar.value; 70 | if (!contraseña) { 71 | return alert("No hay contraseña"); 72 | } 73 | const textoPlano = $informacionEncriptar.value; 74 | if (!textoPlano) { 75 | return alert("No hay texto para encriptar"); 76 | } 77 | const encriptado = await encriptar(contraseña, textoPlano); 78 | $resultadoEncriptacion.value = encriptado; 79 | }; 80 | $botonDesencriptar.onclick = async () => { 81 | const contraseña = $contraseñaDesencriptar.value; 82 | if (!contraseña) { 83 | return alert("No hay contraseña"); 84 | } 85 | const textoCifradoBase64 = $informacionDesencriptar.value; 86 | if (!textoCifradoBase64) { 87 | return alert("No hay texto en base64"); 88 | } 89 | try { 90 | const desencriptado = await desencriptar(contraseña, textoCifradoBase64); 91 | $resultadoDesencriptacion.value = desencriptado; 92 | } catch (e) { 93 | $resultadoDesencriptacion.value = "Error desencriptando: " + e.message + ". ¿La contraseña es la correcta y la información está en base64?"; 94 | } 95 | }; 96 | 97 | }); 98 | -------------------------------------------------------------------------------- /enviar-imagen-telegram/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Enviar imagen en nombre de Bot de Telegram 8 | 9 | 10 | 11 |

Enviar imagen a Telegram desde JavaScript

12 | 13 |

14 | 15 |

16 | 17 |

18 | 19 | 20 | 55 | 56 | 57 | -------------------------------------------------------------------------------- /enviar-mensaje-telegram/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Enviar mensaje en nombre de Bot de Telegram 8 | 9 | 10 | 11 |

Enviar mensaje a Telegram desde JavaScript

12 | 13 |

14 | 15 |

16 | 17 |

18 | 19 | 20 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /envoltura-fetch/HTTP.js: -------------------------------------------------------------------------------- 1 | /* 2 | Programado por Luis Cabrera Benito 3 | ____ _____ _ _ _ 4 | | _ \ | __ \ (_) | | | 5 | | |_) |_ _ | |__) |_ _ _ __ _____| |__ _ _| |_ ___ 6 | | _ <| | | | | ___/ _` | '__|_ / | '_ \| | | | __/ _ \ 7 | | |_) | |_| | | | | (_| | | / /| | |_) | |_| | || __/ 8 | |____/ \__, | |_| \__,_|_| /___|_|_.__/ \__, |\__\___| 9 | __/ | __/ | 10 | |___/ |___/ 11 | 12 | 13 | Blog: https://parzibyte.me/blog 14 | Ayuda: https://parzibyte.me/blog/contrataciones-ayuda/ 15 | Contacto: https://parzibyte.me/blog/contacto/ 16 | */ 17 | const RUTA_SERVIDOR = "./webapi"; 18 | const HTTP = { 19 | "post": (ruta, datos) => 20 | fetch(RUTA_SERVIDOR + ruta, { 21 | credentials: 'include', 22 | method: "POST", 23 | body: JSON.stringify(datos) 24 | }) 25 | .then(r => r.json()) 26 | , 27 | "put": (ruta, datos) => 28 | fetch(RUTA_SERVIDOR + ruta, { 29 | credentials: 'include', 30 | method: "PUT", 31 | body: JSON.stringify(datos) 32 | }) 33 | .then(r => r.json()) 34 | , 35 | "get": (ruta) => 36 | fetch(RUTA_SERVIDOR + ruta, { 37 | credentials: 'include', 38 | }) 39 | .then(r => r.json()) 40 | , 41 | "delete": (ruta) => 42 | fetch(RUTA_SERVIDOR + ruta, { 43 | credentials: 'include', 44 | method: "DELETE", 45 | }) 46 | .then(r => r.json()) 47 | }; -------------------------------------------------------------------------------- /esteganografia/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ -------------------------------------------------------------------------------- /esteganografia/README.md: -------------------------------------------------------------------------------- 1 | npx tailwindcss -i ./estilos_base.css -o ./estilos.css --watch 2 | 3 | 4 | npx tailwindcss -i ./estilos_base.css -o ./estilos.css --minify -------------------------------------------------------------------------------- /esteganografia/estilos.css: -------------------------------------------------------------------------------- 1 | /*! tailwindcss v3.4.4 | MIT License | https://tailwindcss.com*/*,:after,:before{box-sizing:border-box;border:0 solid #e5e7eb}:after,:before{--tw-content:""}:host,html{line-height:1.5;-webkit-text-size-adjust:100%;-moz-tab-size:4;-o-tab-size:4;tab-size:4;font-family:ui-sans-serif,system-ui,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji;font-feature-settings:normal;font-variation-settings:normal;-webkit-tap-highlight-color:transparent}body{margin:0;line-height:inherit}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,pre,samp{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-feature-settings:normal;font-variation-settings:normal;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:initial}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}button,input,optgroup,select,textarea{font-family:inherit;font-feature-settings:inherit;font-variation-settings:inherit;font-size:100%;font-weight:inherit;line-height:inherit;letter-spacing:inherit;color:inherit;margin:0;padding:0}button,select{text-transform:none}button,input:where([type=button]),input:where([type=reset]),input:where([type=submit]){-webkit-appearance:button;background-color:initial;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:initial}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dd,dl,figure,h1,h2,h3,h4,h5,h6,hr,p,pre{margin:0}fieldset{margin:0}fieldset,legend{padding:0}menu,ol,ul{list-style:none;margin:0;padding:0}dialog{padding:0}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{opacity:1;color:#9ca3af}input::placeholder,textarea::placeholder{opacity:1;color:#9ca3af}[role=button],button{cursor:pointer}:disabled{cursor:default}audio,canvas,embed,iframe,img,object,svg,video{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}[hidden]{display:none}*,::backdrop,:after,:before{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:#3b82f680;--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }.m-2{margin:.5rem}.mb-2{margin-bottom:.5rem}.flex{display:flex}.max-w-max{max-width:-moz-max-content;max-width:max-content}.flex-row{flex-direction:row}.flex-col{flex-direction:column}.rounded-md{border-radius:.375rem}.border{border-width:1px}.border-2{border-width:2px}.border-blue-500{--tw-border-opacity:1;border-color:rgb(59 130 246/var(--tw-border-opacity))}.border-gray-200{--tw-border-opacity:1;border-color:rgb(229 231 235/var(--tw-border-opacity))}.bg-green-500{--tw-bg-opacity:1;background-color:rgb(34 197 94/var(--tw-bg-opacity))}.p-1{padding:.25rem}.p-2{padding:.5rem}.text-center{text-align:center}.text-2xl{font-size:1.5rem;line-height:2rem}.font-semibold{font-weight:600}.text-white{--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity))} -------------------------------------------------------------------------------- /esteganografia/estilos_base.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; -------------------------------------------------------------------------------- /esteganografia/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Esteganografía JavaScript 8 | 9 | 10 | 11 | 12 |
13 |

Esteganografía con JavaScript

14 |
15 |

Por favor, elige una imagen:

16 | 17 |
18 |
19 |
20 |

Ocultar

21 |

Escribe el mensaje:

22 | 24 | 26 |
27 |
28 |

Leer

29 |

30 | 32 |
33 | 34 |
35 |
36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /esteganografia/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "devDependencies": { 3 | "autoprefixer": "^10.4.19", 4 | "postcss": "^8.4.38", 5 | "tailwindcss": "^3.4.4" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /esteganografia/postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | } 7 | -------------------------------------------------------------------------------- /esteganografia/script.js: -------------------------------------------------------------------------------- 1 | let canvasFueraDePantalla = null; 2 | let contextoCanvas = null; 3 | const CODIGO_CARACTER_TERMINACION = 4; 4 | const $imagen = document.querySelector("#imagen"); 5 | const $ocultar = document.querySelector("#ocultar"); 6 | const $leer = document.querySelector("#leer"); 7 | const $mensaje = document.querySelector("#mensaje"); 8 | const $resultado = document.querySelector("#resultado"); 9 | let nombreImagenSinExtension = ""; 10 | let extensionImagen = ""; 11 | 12 | const extraerNombreSinExtension = (nombre) => { 13 | const indice = nombre.lastIndexOf("."); 14 | if (indice === -1) { 15 | return ""; 16 | } 17 | return nombre.substring(0, indice); 18 | } 19 | const extraerExtensionDeArchivo = (nombre) => { 20 | const indice = nombre.lastIndexOf("."); 21 | if (indice === -1) { 22 | return ""; 23 | } 24 | return nombre.substring(indice + 1); 25 | } 26 | const mimeTypeAPartirDeExtension = (extension) => { 27 | if (extension === "png") { 28 | return "image/png"; 29 | } else if (extension === "jpg") { 30 | return "image/jpeg"; 31 | } else if (extension === "webp") { 32 | return "image/png"; 33 | } 34 | } 35 | /* 36 | Si especificas el indiceBit en 0 te estarás refiriendo al LSB 37 | y si especificas el indiceBit en 7 te refieres al MSB. 38 | Dicho de otra manera, los índices comienzan a contarse de derecha a izquierda 39 | */ 40 | const establecerBitEnNumero = (numero, indiceBit, bit) => { 41 | if (bit === 1) { 42 | numero |= (1 << indiceBit); 43 | } else { 44 | numero &= ~(1 << indiceBit); 45 | } 46 | return numero; 47 | } 48 | 49 | // Establece el LSB de un número según el bit y devuelve el número 50 | const colocarLsbDeNumero = (numero, bit) => { 51 | // Solo lo he probado con números entre 0 y 255 52 | if (bit === 1) { 53 | return numero | 1; 54 | } else { 55 | return numero & 254; 56 | } 57 | } 58 | 59 | // Devuelve el LSB de un número 60 | const obtenerLsb = (numero) => { 61 | return numero & 1; 62 | } 63 | 64 | const obtenerBitsDeMensaje = (mensaje) => { 65 | const bits = []; 66 | for (let indiceByte = 0; indiceByte < mensaje.length; indiceByte++) { 67 | // Obtener entero ASCII de la letra (byte) actual... 68 | const charCode = mensaje.charCodeAt(indiceByte); 69 | // Recorrer cada bit 70 | for (let indiceBit = 7; indiceBit >= 0; indiceBit--) { 71 | const bit = (charCode >> indiceBit) & 1; 72 | //console.log(`Charcode ${charCode} con índice bit ${indiceBit} y bit valor ${bit}`); 73 | bits.push(bit); 74 | } 75 | } 76 | return bits; 77 | } 78 | 79 | 80 | const dibujarImagenEnCanvasGlobal = async (imagen) => { 81 | nombreImagenSinExtension = extraerNombreSinExtension(imagen.name); 82 | extensionImagen = extraerExtensionDeArchivo(imagen.name); 83 | const imagenComoBitmap = await createImageBitmap(imagen); 84 | canvasFueraDePantalla = new OffscreenCanvas(imagenComoBitmap.width, imagenComoBitmap.height); 85 | contextoCanvas = canvasFueraDePantalla.getContext("2d"); 86 | contextoCanvas.drawImage(imagenComoBitmap, 0, 0, imagenComoBitmap.width, imagenComoBitmap.height); 87 | } 88 | 89 | const obtenerImageData = () => { 90 | return contextoCanvas.getImageData(0, 0, canvasFueraDePantalla.width, canvasFueraDePantalla.height); 91 | } 92 | 93 | const colocarImageData = (datosDeImagen) => { 94 | contextoCanvas.putImageData(datosDeImagen, 0, 0); 95 | } 96 | 97 | const descargarCanvas = async () => { 98 | let fotoComoBlob = await canvasFueraDePantalla.convertToBlob(); 99 | const a = document.createElement("a"); 100 | const archivo = new Blob([fotoComoBlob], { type: mimeTypeAPartirDeExtension(extensionImagen) }); 101 | const url = URL.createObjectURL(archivo); 102 | a.href = url; 103 | a.download = `${nombreImagenSinExtension}_con_mensaje.${extensionImagen}`; 104 | a.click(); 105 | URL.revokeObjectURL(url); 106 | } 107 | 108 | const leer = async () => { 109 | const imagenes = $imagen.files; 110 | if (imagenes.length <= 0) { 111 | return; 112 | } 113 | const primerArchivoDeImagen = imagenes[0]; 114 | await dibujarImagenEnCanvasGlobal(primerArchivoDeImagen); 115 | const datosDeImagen = obtenerImageData(); 116 | const pixeles = datosDeImagen.data; 117 | let mensaje = ""; 118 | let codigoDelCaracterActual = 0; 119 | let indiceBitEnCaracterActual = 7; 120 | for (let indice = 0; indice < pixeles.length; indice++) { 121 | // Omitir canal alfa por ahora 122 | // Por ejemplo 3, 7, 11 son el alfa. Si le sumamos 1 son 123 | // 4,8,12 que ya se puede comparar para saber si es múltiplo 124 | // de 4 125 | //console.log({ indice }); 126 | if ((indice + 1) % 4 === 0) { 127 | continue; 128 | } 129 | const lsbDelNivelDelColor = obtenerLsb(pixeles[indice]); 130 | codigoDelCaracterActual = establecerBitEnNumero(codigoDelCaracterActual, indiceBitEnCaracterActual, lsbDelNivelDelColor); 131 | //console.log(`Agregando LSB ${lsbDelNivelDelColor} del nivel ${pixeles[indice]} al número que hasta ahora es ${codigoDelCaracterActual} en el índice ${indiceBitEnCaracterActual}`); 132 | if (indiceBitEnCaracterActual === 0) { 133 | if (codigoDelCaracterActual === CODIGO_CARACTER_TERMINACION) { 134 | break; 135 | } 136 | const letra = String.fromCodePoint(codigoDelCaracterActual); 137 | //console.log(`Agregando número ${codigoDelCaracterActual} que es ${letra}`); 138 | mensaje += letra; 139 | codigoDelCaracterActual = 0; 140 | indiceBitEnCaracterActual = 8; 141 | } 142 | indiceBitEnCaracterActual--; 143 | } 144 | $resultado.textContent = mensaje; 145 | } 146 | 147 | const bitsMensajeTerminacion = obtenerBitsDeMensaje(String.fromCharCode(CODIGO_CARACTER_TERMINACION)); 148 | const ocultar = async () => { 149 | const imagenes = $imagen.files; 150 | if (imagenes.length <= 0) { 151 | return; 152 | } 153 | const mensaje = $mensaje.value; 154 | if (!mensaje) { 155 | return alert("Escribe un mensaje"); 156 | } 157 | const primerArchivoDeImagen = imagenes[0]; 158 | await dibujarImagenEnCanvasGlobal(primerArchivoDeImagen); 159 | 160 | const datosDeImagen = obtenerImageData(); 161 | const pixeles = datosDeImagen.data; 162 | const bitsMensaje = obtenerBitsDeMensaje(mensaje).concat(bitsMensajeTerminacion); 163 | let indiceDeNivelDeColorDelPixel = 0; 164 | for (let indiceBit = 0; indiceBit < bitsMensaje.length; indiceBit++) { 165 | const bitDelMensaje = bitsMensaje[indiceBit]; 166 | //console.log(`Ocultando ${bitDelMensaje} en el nivel con valor ${pixeles[indicePixel]} (posición ${indicePixel}) que será convertido a ${colocarLsbDeNumero(pixeles[indicePixel], bitDelMensaje)}`) 167 | pixeles[indiceDeNivelDeColorDelPixel] = colocarLsbDeNumero(pixeles[indiceDeNivelDeColorDelPixel], bitDelMensaje); 168 | indiceDeNivelDeColorDelPixel++; 169 | // Omitir canal alfa por ahora 170 | // Por ejemplo 3, 7, 11 son el alfa. Si le sumamos 1 son 171 | // 4,8,12 que ya se puede comparar para saber si es múltiplo 172 | // de 4 173 | if ((indiceDeNivelDeColorDelPixel + 1) % 4 === 0) { 174 | indiceDeNivelDeColorDelPixel++; 175 | } 176 | } 177 | colocarImageData(datosDeImagen); 178 | await descargarCanvas(); 179 | } 180 | 181 | document.addEventListener("DOMContentLoaded", () => { 182 | $ocultar.addEventListener("click", ocultar); 183 | $leer.addEventListener("click", leer); 184 | }); -------------------------------------------------------------------------------- /esteganografia/tailwind.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('tailwindcss').Config} */ 2 | module.exports = { 3 | content: ["./**/*.html"], 4 | theme: { 5 | extend: {}, 6 | }, 7 | plugins: [], 8 | } 9 | 10 | -------------------------------------------------------------------------------- /firma-js/documento.html: -------------------------------------------------------------------------------- 1 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | Documento con firma - By Parzibyte 42 | 49 | 50 | 51 | 52 |

Título del documento

53 | Simple documento para demostrar cómo se puede colocar una firma del usuario 54 |

Lorem ipsum dolor sit amet consectetur adipisicing elit. Et magnam eius reprehenderit repudiandae, veritatis 55 | aliquid a iste! Eos necessitatibus omnis maiores doloremque? Ipsam rem omnis saepe architecto quam molestias 56 | asperiores.

57 |

Lorem ipsum dolor sit amet consectetur adipisicing elit. Quam unde veritatis, aut exercitationem in voluptatum 58 | aliquid rem deleniti non quas dignissimos asperiores laborum omnis similique esse, neque autem sit possimus.

59 |

Quos veniam incidunt animi distinctio, itaque voluptate laudantium voluptates doloribus ipsa praesentium qui 60 | veritatis perferendis rerum dicta a, non esse cupiditate nemo mollitia exercitationem nesciunt explicabo, 61 | debitis dolores. Mollitia, similique.

62 |

Deleniti sapiente rem beatae officia libero similique iste, vitae aut? Voluptatum aperiam fugit placeat adipisci, 63 | consequatur reiciendis voluptatem eius dolore qui. Cumque delectus iste earum, explicabo error quas rerum nam! 64 |

65 |

Porro tempore ipsa enim a dolore explicabo totam. Quos veniam repellendus quo excepturi voluptatibus eum 66 | provident corrupti debitis nesciunt neque ipsa, consequatur qui illo perferendis mollitia omnis sit cum sunt. 67 |

68 |

Aliquid saepe quod recusandae at adipisci veniam quasi delectus maiores magni fuga accusamus ex, facere, vero 69 | voluptatem temporibus odit maxime. Fuga assumenda suscipit repellat sapiente, porro sit repudiandae doloremque 70 | officiis.

71 |

A continuación la firma

72 | Firma del usuario 73 |
74 | By Parzibyte 75 | 82 | 83 | 84 | -------------------------------------------------------------------------------- /firma-js/estilo.css: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | ____ _____ _ _ _ 4 | | _ \ | __ \ (_) | | | 5 | | |_) |_ _ | |__) |_ _ _ __ _____| |__ _ _| |_ ___ 6 | | _ <| | | | | ___/ _` | '__|_ / | '_ \| | | | __/ _ \ 7 | | |_) | |_| | | | | (_| | | / /| | |_) | |_| | || __/ 8 | |____/ \__, | |_| \__,_|_| /___|_|_.__/ \__, |\__\___| 9 | __/ | __/ | 10 | |___/ |___/ 11 | 12 | ____________________________________ 13 | / Si necesitas ayuda, contáctame en \ 14 | \ https://parzibyte.me / 15 | ------------------------------------ 16 | \ ^__^ 17 | \ (oo)\_______ 18 | (__)\ )\/\ 19 | ||----w | 20 | || || 21 | Creado por Parzibyte (https://parzibyte.me). 22 | ------------------------------------------------------------------------------------------------ 23 | | IMPORTANTE | 24 | Si vas a borrar este encabezado, considera: 25 | Seguirme: https://parzibyte.me/blog/sigueme/ 26 | Y compartir mi blog con tus amigos 27 | También tengo canal de YouTube: https://www.youtube.com/channel/UCroP4BTWjfM0CkGB6AFUoBg?sub_confirmation=1 28 | Twitter: https://twitter.com/parzibyte 29 | Facebook: https://facebook.com/parzibyte.fanpage 30 | Instagram: https://instagram.com/parzibyte 31 | Hacer una donación vía PayPal: https://paypal.me/LuisCabreraBenito 32 | ------------------------------------------------------------------------------------------------ 33 | */ 34 | 35 | #canvas { 36 | border: 1px solid black; 37 | } 38 | html, body { 39 | overscroll-behavior: none; 40 | } -------------------------------------------------------------------------------- /firma-js/index.html: -------------------------------------------------------------------------------- 1 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | Solicitar firma de usuario - By Parzibyte 42 | 43 | 44 | 45 | 46 |

Soy un párrafo solo para agregar contenido y demostrar que el scroll no afecta la firma

47 |

Lorem ipsum dolor sit amet consectetur adipisicing elit. Blanditiis minus voluptatibus facere architecto ipsam 48 | beatae nostrum neque. Architecto voluptatibus amet, sed molestiae, unde pariatur qui mollitia excepturi 49 | provident vel numquam.

50 |

Firmar a continuación:

51 | 52 |

Lorem ipsum dolor sit amet consectetur adipisicing elit. Blanditiis minus voluptatibus facere architecto ipsam 53 | beatae nostrum neque. Architecto voluptatibus amet, sed molestiae, unde pariatur qui mollitia excepturi 54 | provident vel numquam.

55 |

Lorem ipsum dolor sit amet consectetur adipisicing elit. Harum velit atque, dignissimos assumenda architecto 56 | expedita molestias. Velit quis tempore quos consequuntur? Voluptatum labore voluptas sint itaque rerum, 57 | blanditiis officiis a.

58 |

Lorem, ipsum dolor sit amet consectetur adipisicing elit. Maxime quo accusantium doloribus expedita beatae odit 59 | corporis possimus aspernatur explicabo id tempora, voluptatem eligendi ea, libero, asperiores dignissimos 60 | consequatur repellendus dolorum!

61 |
62 | 63 | 64 | 65 |
66 | By Parzibyte 67 | 68 | 69 | 70 | -------------------------------------------------------------------------------- /firma-js/script.js: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | ____ _____ _ _ _ 4 | | _ \ | __ \ (_) | | | 5 | | |_) |_ _ | |__) |_ _ _ __ _____| |__ _ _| |_ ___ 6 | | _ <| | | | | ___/ _` | '__|_ / | '_ \| | | | __/ _ \ 7 | | |_) | |_| | | | | (_| | | / /| | |_) | |_| | || __/ 8 | |____/ \__, | |_| \__,_|_| /___|_|_.__/ \__, |\__\___| 9 | __/ | __/ | 10 | |___/ |___/ 11 | 12 | ____________________________________ 13 | / Si necesitas ayuda, contáctame en \ 14 | \ https://parzibyte.me / 15 | ------------------------------------ 16 | \ ^__^ 17 | \ (oo)\_______ 18 | (__)\ )\/\ 19 | ||----w | 20 | || || 21 | Creado por Parzibyte (https://parzibyte.me). 22 | ------------------------------------------------------------------------------------------------ 23 | | IMPORTANTE | 24 | Si vas a borrar este encabezado, considera: 25 | Seguirme: https://parzibyte.me/blog/sigueme/ 26 | Y compartir mi blog con tus amigos 27 | También tengo canal de YouTube: https://www.youtube.com/channel/UCroP4BTWjfM0CkGB6AFUoBg?sub_confirmation=1 28 | Twitter: https://twitter.com/parzibyte 29 | Facebook: https://facebook.com/parzibyte.fanpage 30 | Instagram: https://instagram.com/parzibyte 31 | Hacer una donación vía PayPal: https://paypal.me/LuisCabreraBenito 32 | ------------------------------------------------------------------------------------------------ 33 | */ 34 | const $canvas = document.querySelector("#canvas"), 35 | $btnDescargar = document.querySelector("#btnDescargar"), 36 | $btnLimpiar = document.querySelector("#btnLimpiar"), 37 | $btnGenerarDocumento = document.querySelector("#btnGenerarDocumento"); 38 | const contexto = $canvas.getContext("2d"); 39 | const COLOR_PINCEL = "black"; 40 | const COLOR_FONDO = "white"; 41 | const GROSOR = 2; 42 | let xAnterior = 0, yAnterior = 0, xActual = 0, yActual = 0; 43 | const obtenerXReal = (clientX) => clientX - $canvas.getBoundingClientRect().left; 44 | const obtenerYReal = (clientY) => clientY - $canvas.getBoundingClientRect().top; 45 | let haComenzadoDibujo = false; // Bandera que indica si el usuario está presionando el botón del mouse sin soltarlo 46 | 47 | 48 | const limpiarCanvas = () => { 49 | // Colocar color blanco en fondo de canvas 50 | contexto.fillStyle = COLOR_FONDO; 51 | contexto.fillRect(0, 0, $canvas.width, $canvas.height); 52 | }; 53 | limpiarCanvas(); 54 | $btnLimpiar.onclick = limpiarCanvas; 55 | // Escuchar clic del botón para descargar el canvas 56 | $btnDescargar.onclick = () => { 57 | const enlace = document.createElement('a'); 58 | // El título 59 | enlace.download = "Firma.png"; 60 | // Convertir la imagen a Base64 y ponerlo en el enlace 61 | enlace.href = $canvas.toDataURL(); 62 | // Hacer click en él 63 | enlace.click(); 64 | }; 65 | 66 | window.obtenerImagen = () => { 67 | return $canvas.toDataURL(); 68 | }; 69 | 70 | $btnGenerarDocumento.onclick = () => { 71 | window.open("documento.html"); 72 | }; 73 | const onClicOToqueIniciado = evento => { 74 | // En este evento solo se ha iniciado el clic, así que dibujamos un punto 75 | xAnterior = xActual; 76 | yAnterior = yActual; 77 | xActual = obtenerXReal(evento.clientX); 78 | yActual = obtenerYReal(evento.clientY); 79 | contexto.beginPath(); 80 | contexto.fillStyle = COLOR_PINCEL; 81 | contexto.fillRect(xActual, yActual, GROSOR, GROSOR); 82 | contexto.closePath(); 83 | // Y establecemos la bandera 84 | haComenzadoDibujo = true; 85 | } 86 | 87 | const onMouseODedoMovido = evento => { 88 | evento.preventDefault(); // Prevenir scroll en móviles 89 | if (!haComenzadoDibujo) { 90 | return; 91 | } 92 | // El mouse se está moviendo y el usuario está presionando el botón, así que dibujamos todo 93 | let target = evento; 94 | if (evento.type.includes("touch")) { 95 | target = evento.touches[0]; 96 | } 97 | xAnterior = xActual; 98 | yAnterior = yActual; 99 | xActual = obtenerXReal(target.clientX); 100 | yActual = obtenerYReal(target.clientY); 101 | contexto.beginPath(); 102 | contexto.moveTo(xAnterior, yAnterior); 103 | contexto.lineTo(xActual, yActual); 104 | contexto.strokeStyle = COLOR_PINCEL; 105 | contexto.lineWidth = GROSOR; 106 | contexto.stroke(); 107 | contexto.closePath(); 108 | } 109 | const onMouseODedoLevantado = () => { 110 | haComenzadoDibujo = false; 111 | }; 112 | 113 | // Lo demás tiene que ver con pintar sobre el canvas en los eventos del mouse 114 | ["mousedown", "touchstart"].forEach(nombreDeEvento => { 115 | $canvas.addEventListener(nombreDeEvento, onClicOToqueIniciado); 116 | }); 117 | 118 | ["mousemove", "touchmove"].forEach(nombreDeEvento => { 119 | $canvas.addEventListener(nombreDeEvento, onMouseODedoMovido); 120 | }); 121 | ["mouseup", "touchend"].forEach(nombreDeEvento => { 122 | $canvas.addEventListener(nombreDeEvento, onMouseODedoLevantado); 123 | }); -------------------------------------------------------------------------------- /foto-camara-telegram/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 11 | 12 | 14 | Foto JS + Telegram 15 | 22 | 23 | 24 | 25 |

Tomar foto con JavaScript y enviarla a Telegram

26 |

Selecciona un dispositivo

27 |
28 | 29 |

30 |
31 |
32 | 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /foto-camara-telegram/script.js: -------------------------------------------------------------------------------- 1 | /* 2 | Tomar una fotografía y enviarla a Telegram periódicamente 3 | @date 2024-05-14 4 | @author parzibyte 5 | @web parzibyte.me/blog 6 | */ 7 | const tieneSoporteUserMedia = () => 8 | !!(navigator.getUserMedia || (navigator.mozGetUserMedia || navigator.mediaDevices.getUserMedia) || navigator.webkitGetUserMedia || navigator.msGetUserMedia) 9 | const _getUserMedia = (...arguments) => 10 | (navigator.getUserMedia || (navigator.mozGetUserMedia || navigator.mediaDevices.getUserMedia) || navigator.webkitGetUserMedia || navigator.msGetUserMedia).apply(navigator, arguments); 11 | 12 | const sleep = ms => new Promise(r => setTimeout(r, ms)); 13 | let yaEstaEnviandoFotos = false; 14 | const TIEMPO_ESPERA_ENTRE_FOTOS_EN_MILISEGUNDOS = 2000; 15 | const tomarFotoPeriodicamente = async () => { 16 | while (true) { 17 | await tomarFoto(); 18 | await sleep(TIEMPO_ESPERA_ENTRE_FOTOS_EN_MILISEGUNDOS); 19 | } 20 | } 21 | const $video = document.querySelector("#video"), 22 | $estado = document.querySelector("#estado"), 23 | $listaDeDispositivos = document.querySelector("#listaDeDispositivos"); 24 | const urlSearchParams = new URLSearchParams(window.location.search); 25 | const TOKEN = urlSearchParams.get("token"), 26 | ID_CHAT = urlSearchParams.get("idChat"); 27 | let canvasFueraDePantalla = null; 28 | let contextoCanvas = null; 29 | const formateador = new Intl.DateTimeFormat('es-MX', { dateStyle: 'medium', timeStyle: 'medium' }); 30 | 31 | const limpiarSelect = () => { 32 | for (let x = $listaDeDispositivos.options.length - 1; x >= 0; x--) 33 | $listaDeDispositivos.remove(x); 34 | }; 35 | const obtenerDispositivos = () => navigator 36 | .mediaDevices 37 | .enumerateDevices(); 38 | 39 | // La función que es llamada después de que ya se dieron los permisos 40 | // Lo que hace es llenar el select con los dispositivos obtenidos 41 | const llenarSelectConDispositivosDisponibles = async () => { 42 | limpiarSelect(); 43 | const dispositivos = await obtenerDispositivos(); 44 | const dispositivosDeVideo = []; 45 | dispositivos.forEach(dispositivo => { 46 | const tipo = dispositivo.kind; 47 | if (tipo === "videoinput") { 48 | dispositivosDeVideo.push(dispositivo); 49 | } 50 | }); 51 | 52 | // Vemos si encontramos algún dispositivo, y en caso de que si, entonces llamamos a la función 53 | if (dispositivosDeVideo.length > 0) { 54 | // Llenar el select 55 | dispositivosDeVideo.forEach(dispositivo => { 56 | const option = document.createElement('option'); 57 | option.value = dispositivo.deviceId; 58 | option.text = dispositivo.label; 59 | $listaDeDispositivos.appendChild(option); 60 | }); 61 | } 62 | } 63 | 64 | const enviarImagen = async (idChat, token, imagen) => { 65 | const url = `https://api.telegram.org/bot${token}/sendPhoto`; 66 | const fd = new FormData(); 67 | fd.append("chat_id", idChat) 68 | const caption = `${$listaDeDispositivos.options[$listaDeDispositivos.selectedIndex].text} ${formateador.format(new Date())}`; 69 | fd.append("caption", caption); 70 | fd.append("photo", imagen); 71 | const respuestaHttp = await fetch(url, { 72 | method: 'POST', 73 | body: fd, 74 | }); 75 | return { 76 | respuesta: await respuestaHttp.json(), 77 | codigo: respuestaHttp.status, 78 | }; 79 | } 80 | 81 | const tomarFoto = async () => { 82 | //Pausar reproducción 83 | $video.pause(); 84 | canvasFueraDePantalla.width = $video.videoWidth; 85 | canvasFueraDePantalla.height = $video.videoHeight; 86 | //Obtener contexto del canvas y dibujar sobre él 87 | contextoCanvas.drawImage($video, 0, 0, canvasFueraDePantalla.width, canvasFueraDePantalla.height); 88 | //Reanudar reproducción 89 | await $video.play(); 90 | let foto = await canvasFueraDePantalla.convertToBlob(); //Esta es la foto como bytes 91 | await enviarImagen(ID_CHAT, TOKEN, foto); 92 | } 93 | 94 | (async function () { 95 | // Comenzamos viendo si tiene soporte, si no, nos detenemos 96 | if (!tieneSoporteUserMedia()) { 97 | alert("Lo siento. Tu navegador no soporta esta característica"); 98 | $estado.innerHTML = "Parece que tu navegador no soporta esta característica. Intenta actualizarlo."; 99 | return; 100 | } 101 | //Aquí guardaremos el stream globalmente 102 | let stream; 103 | 104 | const mostrarStream = idDeDispositivo => { 105 | _getUserMedia({ 106 | video: { 107 | // Justo aquí indicamos cuál dispositivo usar 108 | deviceId: idDeDispositivo, 109 | } 110 | }, 111 | async (streamObtenido) => { 112 | // Aquí ya tenemos permisos, ahora sí llenamos el select, 113 | // pues si no, no nos daría el nombre de los dispositivos 114 | if ($listaDeDispositivos.length <= 0) { 115 | llenarSelectConDispositivosDisponibles(); 116 | } 117 | 118 | // Escuchar cuando seleccionen otra opción y entonces llamar a esta función 119 | $listaDeDispositivos.onchange = () => { 120 | // Detener el stream 121 | if (stream) { 122 | stream.getTracks().forEach(function (track) { 123 | track.stop(); 124 | }); 125 | } 126 | // Mostrar el nuevo stream con el dispositivo seleccionado 127 | mostrarStream($listaDeDispositivos.value); 128 | } 129 | 130 | // Simple asignación 131 | stream = streamObtenido; 132 | 133 | // Mandamos el stream de la cámara al elemento de vídeo 134 | $video.srcObject = stream; 135 | // Refrescar el canvas 136 | await $video.play(); 137 | canvasFueraDePantalla = new OffscreenCanvas($video.videoWidth, $video.videoHeight); 138 | contextoCanvas = canvasFueraDePantalla.getContext("2d"); 139 | // Prevenir enviar dos fotos cuando se cambia el dispositivo 140 | if (!yaEstaEnviandoFotos) { 141 | tomarFotoPeriodicamente(); 142 | yaEstaEnviandoFotos = true; 143 | } 144 | }, (error) => { 145 | console.log("Permiso denegado o error: ", error); 146 | $estado.innerHTML = "No se puede acceder a la cámara, o no diste permiso."; 147 | }); 148 | } 149 | 150 | // Comenzamos pidiendo los dispositivos 151 | const dispositivos = await obtenerDispositivos() 152 | // Vamos a filtrarlos y guardar aquí los de vídeo 153 | const dispositivosDeVideo = []; 154 | 155 | // Recorrer y filtrar 156 | dispositivos.forEach(function (dispositivo) { 157 | const tipo = dispositivo.kind; 158 | if (tipo === "videoinput") { 159 | dispositivosDeVideo.push(dispositivo); 160 | } 161 | }); 162 | 163 | // Vemos si encontramos algún dispositivo, y en caso de que si, entonces llamamos a la función 164 | // y le pasamos el id de dispositivo 165 | if (dispositivosDeVideo.length > 0) { 166 | // Mostrar stream con el ID del primer dispositivo, luego el usuario puede cambiar 167 | mostrarStream(dispositivosDeVideo[0].deviceId); 168 | } 169 | })(); -------------------------------------------------------------------------------- /generar-numeros-aleatorios/estilos.css: -------------------------------------------------------------------------------- 1 | body { 2 | font-family: Arial, sans-serif; 3 | background-color: #f5f5f5; 4 | color: #333; 5 | margin: 0; 6 | padding: 20px; 7 | } 8 | 9 | h1 { 10 | color: #4A90E2; 11 | text-align: center; 12 | } 13 | 14 | .form-group { 15 | margin-bottom: 15px; 16 | } 17 | 18 | label { 19 | display: block; 20 | margin-bottom: 5px; 21 | color: #666; 22 | } 23 | 24 | input[type="number"], 25 | textarea { 26 | width: 100%; 27 | padding: 10px; 28 | border: 1px solid #ddd; 29 | border-radius: 5px; 30 | font-size: 16px; 31 | color: #333; 32 | box-sizing: border-box; 33 | } 34 | 35 | input[type="number"]:focus, 36 | textarea:focus { 37 | border-color: #4A90E2; 38 | outline: none; 39 | } 40 | 41 | textarea { 42 | height: 80px; 43 | resize: none; 44 | } 45 | 46 | button { 47 | background-color: #4A90E2; 48 | color: #fff; 49 | border: none; 50 | border-radius: 5px; 51 | padding: 10px 20px; 52 | font-size: 16px; 53 | cursor: pointer; 54 | transition: background-color 0.3s ease; 55 | margin-right: 10px; 56 | box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); 57 | } 58 | 59 | button:hover { 60 | background-color: #357ABD; 61 | } 62 | 63 | button:focus { 64 | outline: none; 65 | box-shadow: 0 0 0 3px rgba(74, 144, 226, 0.5); 66 | } 67 | 68 | button:active { 69 | background-color: #2E5A8C; 70 | } 71 | 72 | pre#contenedor { 73 | background-color: #f9f9f9; 74 | border: 1px solid #ddd; 75 | border-radius: 5px; 76 | padding: 15px; 77 | font-size: 16px; 78 | color: #333; 79 | overflow-x: auto; 80 | max-height: 200px; 81 | margin-top: 20px; 82 | } -------------------------------------------------------------------------------- /generar-numeros-aleatorios/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Generar números aleatorios 8 | 9 | 10 | 11 | 12 |

Generar números aleatorios

13 | 14 |
15 | 16 | 17 |
18 | 19 |
20 | 21 | 22 |
23 | 24 |
25 | 26 | 27 |
28 | 29 |
30 | 31 | 32 |
33 | 34 | 35 |

 36 |     
 99 | 
100 | 
101 | 


--------------------------------------------------------------------------------
/generar-qr/index.html:
--------------------------------------------------------------------------------
 1 | 
 2 | 
 3 | 
 4 | 
 5 | 	
 6 | 	
 7 | 	
 8 | 	
 9 | 	
10 | 	Generar códigos QR - By Parzibyte
11 | 
12 | 
13 | 
14 | 	

Generando códigos QR desde parzibyte.me

15 | By Parzibyte 16 |
17 | Código QR 18 |
19 | 20 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /html-a-pdf/index.html: -------------------------------------------------------------------------------- 1 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | Convertir HTML a PDF 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 |

Mi página web

41 | Presiona el siguiente botón para crear un PDF: 42 | 43 |
Estamos probando la creación de un PDF desde HTML usando JavaScript
44 | Creado por Parzibyte 45 |

Lorem ipsum dolor sit amet consectetur, adipisicing elit. Voluptas dignissimos laudantium laboriosam asperiores? 46 | Doloremque fuga quia odit quas. Obcaecati aliquam suscipit molestias? Architecto nisi blanditiis maxime beatae 47 | temporibus assumenda ex.

48 | Una imagen 49 |

Otro encabezado

50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 |
Nombre
Luis Cabrera Benito
62 | 63 | 64 | -------------------------------------------------------------------------------- /html-a-pdf/lake-5534341_1280.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/parzibyte/ejemplos-javascript/31d317bcce8f9075d2c7a76b8e969a895f800212/html-a-pdf/lake-5534341_1280.jpg -------------------------------------------------------------------------------- /html-a-pdf/script.js: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | ____ _____ _ _ _ 4 | | _ \ | __ \ (_) | | | 5 | | |_) |_ _ | |__) |_ _ _ __ _____| |__ _ _| |_ ___ 6 | | _ <| | | | | ___/ _` | '__|_ / | '_ \| | | | __/ _ \ 7 | | |_) | |_| | | | | (_| | | / /| | |_) | |_| | || __/ 8 | |____/ \__, | |_| \__,_|_| /___|_|_.__/ \__, |\__\___| 9 | __/ | __/ | 10 | |___/ |___/ 11 | 12 | ____________________________________ 13 | / Si necesitas ayuda, contáctame en \ 14 | \ https://parzibyte.me / 15 | ------------------------------------ 16 | \ ^__^ 17 | \ (oo)\_______ 18 | (__)\ )\/\ 19 | ||----w | 20 | || || 21 | Creado por Parzibyte (https://parzibyte.me). Este encabezado debe mantenerse intacto, 22 | excepto si este es un proyecto de un estudiante. 23 | */ 24 | document.addEventListener("DOMContentLoaded", () => { 25 | // Escuchamos el click del botón 26 | const $boton = document.querySelector("#btnCrearPdf"); 27 | $boton.addEventListener("click", () => { 28 | const $elementoParaConvertir = document.body; // <-- Aquí puedes elegir cualquier elemento del DOM 29 | html2pdf() 30 | .set({ 31 | margin: 1, 32 | filename: 'documento.pdf', 33 | image: { 34 | type: 'jpeg', 35 | quality: 0.98 36 | }, 37 | html2canvas: { 38 | scale: 3, // A mayor escala, mejores gráficos, pero más peso 39 | letterRendering: true, 40 | }, 41 | jsPDF: { 42 | unit: "in", 43 | format: "a3", 44 | orientation: 'portrait' // landscape o portrait 45 | } 46 | }) 47 | .from($elementoParaConvertir) 48 | .save() 49 | .catch(err => console.log(err)); 50 | }); 51 | }); -------------------------------------------------------------------------------- /html-a-pdf/style.css: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | ____ _____ _ _ _ 4 | | _ \ | __ \ (_) | | | 5 | | |_) |_ _ | |__) |_ _ _ __ _____| |__ _ _| |_ ___ 6 | | _ <| | | | | ___/ _` | '__|_ / | '_ \| | | | __/ _ \ 7 | | |_) | |_| | | | | (_| | | / /| | |_) | |_| | || __/ 8 | |____/ \__, | |_| \__,_|_| /___|_|_.__/ \__, |\__\___| 9 | __/ | __/ | 10 | |___/ |___/ 11 | 12 | ____________________________________ 13 | / Si necesitas ayuda, contáctame en \ 14 | \ https://parzibyte.me / 15 | ------------------------------------ 16 | \ ^__^ 17 | \ (oo)\_______ 18 | (__)\ )\/\ 19 | ||----w | 20 | || || 21 | Creado por Parzibyte (https://parzibyte.me). Este encabezado debe mantenerse intacto, 22 | excepto si este es un proyecto de un estudiante. 23 | */ 24 | /* 25 | Podemos agregar estilos 26 | */ 27 | table { 28 | border-collapse: collapse; 29 | } 30 | 31 | table, 32 | th, 33 | td { 34 | border: 1px solid black; 35 | } 36 | 37 | th, 38 | td { 39 | padding: 5px; 40 | } 41 | a{ 42 | color: #ffaa00; 43 | font-size: 2rem; 44 | } -------------------------------------------------------------------------------- /leer-codigo-barras/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Leer código de barras con JS 9 | 10 | 11 | 12 |

Leer código de barras con JS y lector físico

13 |

Enfoca el input (es decir, dale un clic) y lee un código con el lector. No olvides abrir la consola con F12

14 | 15 |
16 |

By Parzibyte

17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /leer-codigo-barras/script.js: -------------------------------------------------------------------------------- 1 | document.addEventListener("DOMContentLoaded", () => { 2 | const $codigo = document.querySelector("#codigo"); 3 | $codigo.addEventListener("keydown", evento => { 4 | if (evento.keyCode === 13) { 5 | // El lector ya terminó de leer 6 | const codigoDeBarras = $codigo.value; 7 | // Aquí ya podemos hacer algo con el código. Yo solo lo imprimiré 8 | console.log("Tenemos un código de barras:"); 9 | console.log(codigoDeBarras); 10 | // Limpiar el campo 11 | $codigo.value = ""; 12 | } 13 | }); 14 | }); -------------------------------------------------------------------------------- /llenar-select-arreglo/cadenas.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Llenar select con arreglo de cadenas 8 | 9 | 10 | 11 | 12 | 13 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /llenar-select-arreglo/objetos.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Llenar select con arreglo de objetos 8 | 9 | 10 | 11 | 12 | 13 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /paint-js/estilo.css: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | ____ _____ _ _ _ 4 | | _ \ | __ \ (_) | | | 5 | | |_) |_ _ | |__) |_ _ _ __ _____| |__ _ _| |_ ___ 6 | | _ <| | | | | ___/ _` | '__|_ / | '_ \| | | | __/ _ \ 7 | | |_) | |_| | | | | (_| | | / /| | |_) | |_| | || __/ 8 | |____/ \__, | |_| \__,_|_| /___|_|_.__/ \__, |\__\___| 9 | __/ | __/ | 10 | |___/ |___/ 11 | 12 | ____________________________________ 13 | / Si necesitas ayuda, contáctame en \ 14 | \ https://parzibyte.me / 15 | ------------------------------------ 16 | \ ^__^ 17 | \ (oo)\_______ 18 | (__)\ )\/\ 19 | ||----w | 20 | || || 21 | Creado por Parzibyte (https://parzibyte.me). 22 | ------------------------------------------------------------------------------------------------ 23 | | IMPORTANTE | 24 | Si vas a borrar este encabezado, considera: 25 | Seguirme: https://parzibyte.me/blog/sigueme/ 26 | Y compartir mi blog con tus amigos 27 | También tengo canal de YouTube: https://www.youtube.com/channel/UCroP4BTWjfM0CkGB6AFUoBg?sub_confirmation=1 28 | Twitter: https://twitter.com/parzibyte 29 | Facebook: https://facebook.com/parzibyte.fanpage 30 | Instagram: https://instagram.com/parzibyte 31 | Hacer una donación vía PayPal: https://paypal.me/LuisCabreraBenito 32 | ------------------------------------------------------------------------------------------------ 33 | */ 34 | #canvas{ 35 | border: 1px solid black; 36 | } -------------------------------------------------------------------------------- /paint-js/index.html: -------------------------------------------------------------------------------- 1 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | Dibujar en canvas con JS - By Parzibyte 42 | 43 | 44 | 45 | 46 | 47 |
48 | By Parzibyte 49 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /paint-js/script.js: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | ____ _____ _ _ _ 4 | | _ \ | __ \ (_) | | | 5 | | |_) |_ _ | |__) |_ _ _ __ _____| |__ _ _| |_ ___ 6 | | _ <| | | | | ___/ _` | '__|_ / | '_ \| | | | __/ _ \ 7 | | |_) | |_| | | | | (_| | | / /| | |_) | |_| | || __/ 8 | |____/ \__, | |_| \__,_|_| /___|_|_.__/ \__, |\__\___| 9 | __/ | __/ | 10 | |___/ |___/ 11 | 12 | ____________________________________ 13 | / Si necesitas ayuda, contáctame en \ 14 | \ https://parzibyte.me / 15 | ------------------------------------ 16 | \ ^__^ 17 | \ (oo)\_______ 18 | (__)\ )\/\ 19 | ||----w | 20 | || || 21 | Creado por Parzibyte (https://parzibyte.me). 22 | ------------------------------------------------------------------------------------------------ 23 | | IMPORTANTE | 24 | Si vas a borrar este encabezado, considera: 25 | Seguirme: https://parzibyte.me/blog/sigueme/ 26 | Y compartir mi blog con tus amigos 27 | También tengo canal de YouTube: https://www.youtube.com/channel/UCroP4BTWjfM0CkGB6AFUoBg?sub_confirmation=1 28 | Twitter: https://twitter.com/parzibyte 29 | Facebook: https://facebook.com/parzibyte.fanpage 30 | Instagram: https://instagram.com/parzibyte 31 | Hacer una donación vía PayPal: https://paypal.me/LuisCabreraBenito 32 | ------------------------------------------------------------------------------------------------ 33 | */ 34 | const $canvas = document.querySelector("#canvas"); 35 | const contexto = $canvas.getContext("2d"); 36 | const COLOR = "black"; 37 | const GROSOR = 2; 38 | let xAnterior = 0, yAnterior = 0, xActual = 0, yActual = 0; 39 | const obtenerXReal = (clientX) => clientX - $canvas.getBoundingClientRect().left; 40 | const obtenerYReal = (clientY) => clientY - $canvas.getBoundingClientRect().top; 41 | let haComenzadoDibujo = false; // Bandera que indica si el usuario está presionando el botón del mouse sin soltarlo 42 | $canvas.addEventListener("mousedown", evento => { 43 | // En este evento solo se ha iniciado el clic, así que dibujamos un punto 44 | xAnterior = xActual; 45 | yAnterior = yActual; 46 | xActual = obtenerXReal(evento.clientX); 47 | yActual = obtenerYReal(evento.clientY); 48 | contexto.beginPath(); 49 | contexto.fillStyle = COLOR; 50 | contexto.fillRect(xActual, yActual, GROSOR, GROSOR); 51 | contexto.closePath(); 52 | // Y establecemos la bandera 53 | haComenzadoDibujo = true; 54 | }); 55 | 56 | $canvas.addEventListener("mousemove", (evento) => { 57 | if (!haComenzadoDibujo) { 58 | return; 59 | } 60 | // El mouse se está moviendo y el usuario está presionando el botón, así que dibujamos todo 61 | 62 | xAnterior = xActual; 63 | yAnterior = yActual; 64 | xActual = obtenerXReal(evento.clientX); 65 | yActual = obtenerYReal(evento.clientY); 66 | contexto.beginPath(); 67 | contexto.moveTo(xAnterior, yAnterior); 68 | contexto.lineTo(xActual, yActual); 69 | contexto.strokeStyle = COLOR; 70 | contexto.lineWidth = GROSOR; 71 | contexto.stroke(); 72 | contexto.closePath(); 73 | }); 74 | ["mouseup", "mouseout"].forEach(nombreDeEvento => { 75 | $canvas.addEventListener(nombreDeEvento, () => { 76 | haComenzadoDibujo = false; 77 | }); 78 | }); -------------------------------------------------------------------------------- /pixeles-imagen/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Pixeles de imagen 8 | 9 | 10 | 11 |

Selecciona una imagen:

12 | 13 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /plugin-impresora-v1/conector.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Una clase para interactuar con el plugin 3 | * 4 | * @author parzibyte 5 | * @see https://parzibyte.me/blog 6 | */ 7 | const C = { 8 | AccionWrite: "write", 9 | AccionCut: "cut", 10 | AccionCash: "cash", 11 | AccionCutPartial: "cutpartial", 12 | AccionAlign: "align", 13 | AccionFontSize: "fontsize", 14 | AccionFont: "font", 15 | AccionEmphasize: "emphasize", 16 | AccionFeed: "feed", 17 | AccionQr: "qr", 18 | AlineacionCentro: "center", 19 | AlineacionDerecha: "right", 20 | AlineacionIzquierda: "left", 21 | FuenteA: "A", 22 | FuenteB: "B", 23 | AccionBarcode128: "barcode128", 24 | AccionBarcode39: "barcode39", 25 | AccionBarcode93: "barcode93", 26 | AccionBarcodeEAN: "barcodeEAN", 27 | AccionBarcodeTwoOfFiveSinInterleaved: "barcodeTwoOfFive", 28 | AccionBarcodeTwoOfFiveInterleaved: "barcodeTwoOfFiveInterleaved", 29 | AccionBarcodeCodabar: "barcodeCodabar", 30 | AccionBarcodeUPCA: "barcodeUPCA", 31 | AccionBarcodeUPCE: "barcodeUPCE", 32 | Medida80: 80, 33 | Medida100: 100, 34 | Medida156: 156, 35 | Medida200: 200, 36 | Medida300: 300, 37 | Medida350: 350, 38 | }; 39 | 40 | const URL_PLUGIN = "http://localhost:8000"; 41 | 42 | class OperacionTicket { 43 | constructor(accion, datos) { 44 | this.accion = accion + ""; 45 | this.datos = datos + ""; 46 | } 47 | } 48 | class Impresora { 49 | constructor(ruta) { 50 | if (!ruta) ruta = URL_PLUGIN; 51 | this.ruta = ruta; 52 | this.operaciones = []; 53 | } 54 | 55 | static setImpresora(nombreImpresora, ruta) { 56 | if (ruta) URL_PLUGIN = ruta; 57 | return fetch(URL_PLUGIN + "/impresora", { 58 | method: "PUT", 59 | body: JSON.stringify(nombreImpresora), 60 | }) 61 | .then(r => r.json()) 62 | .then(respuestaDecodificada => respuestaDecodificada === nombreImpresora); 63 | } 64 | static setImpresoraSilencioso(nombreImpresora, ruta) { 65 | if (ruta) URL_PLUGIN = ruta; 66 | return fetch(URL_PLUGIN + "/impresora_silencioso", { 67 | method: "PUT", 68 | body: JSON.stringify(nombreImpresora), 69 | }) 70 | .then(r => r.json()) 71 | .then(respuestaDecodificada => respuestaDecodificada === nombreImpresora); 72 | } 73 | 74 | 75 | static getImpresora(ruta) { 76 | if (ruta) URL_PLUGIN = ruta; 77 | return fetch(URL_PLUGIN + "/impresora") 78 | .then(r => r.json()); 79 | } 80 | 81 | static getImpresoras(ruta) { 82 | if (ruta) URL_PLUGIN = ruta; 83 | return fetch(URL_PLUGIN + "/impresoras") 84 | .then(r => r.json()); 85 | } 86 | 87 | static getImpresorasRemotas(ip) { 88 | return fetch(URL_PLUGIN + "/impresoras_remotas?ip=" + ip) 89 | .then(r => r.json()); 90 | } 91 | 92 | cut() { 93 | this.operaciones.push(new OperacionTicket(C.AccionCut, "")); 94 | } 95 | 96 | cash() { 97 | this.operaciones.push(new OperacionTicket(C.AccionCash, "")); 98 | } 99 | 100 | cutPartial() { 101 | this.operaciones.push(new OperacionTicket(C.AccionCutPartial, "")); 102 | } 103 | 104 | setFontSize(a, b) { 105 | this.operaciones.push(new OperacionTicket(C.AccionFontSize, `${a},${b}`)); 106 | } 107 | 108 | setFont(font) { 109 | if (font !== C.FuenteA && font !== C.FuenteB) throw Error("Fuente inválida"); 110 | this.operaciones.push(new OperacionTicket(C.AccionFont, font)); 111 | } 112 | setEmphasize(val) { 113 | if (isNaN(parseInt(val)) || parseInt(val) < 0) throw Error("Valor inválido"); 114 | this.operaciones.push(new OperacionTicket(C.AccionEmphasize, val)); 115 | } 116 | setAlign(align) { 117 | if (align !== C.AlineacionCentro && align !== C.AlineacionDerecha && align !== C.AlineacionIzquierda) { 118 | throw Error(`Alineación ${align} inválida`); 119 | } 120 | this.operaciones.push(new OperacionTicket(C.AccionAlign, align)); 121 | } 122 | 123 | write(text) { 124 | this.operaciones.push(new OperacionTicket(C.AccionWrite, text)); 125 | } 126 | 127 | feed(n) { 128 | if (!parseInt(n) || parseInt(n) < 0) { 129 | throw Error("Valor para feed inválido"); 130 | } 131 | this.operaciones.push(new OperacionTicket(C.AccionFeed, n)); 132 | } 133 | 134 | end() { 135 | return fetch(this.ruta + "/imprimir", { 136 | method: "POST", 137 | body: JSON.stringify(this.operaciones), 138 | }) 139 | .then(r => r.json()); 140 | } 141 | 142 | imprimirEnImpresora(nombreImpresora) { 143 | const payload = { 144 | operaciones: this.operaciones, 145 | impresora: nombreImpresora, 146 | }; 147 | return fetch(this.ruta + "/imprimir_en", { 148 | method: "POST", 149 | body: JSON.stringify(payload), 150 | }) 151 | .then(r => r.json()); 152 | } 153 | 154 | qr(contenido) { 155 | this.operaciones.push(new OperacionTicket(C.AccionQr, contenido)); 156 | } 157 | 158 | validarMedida(medida) { 159 | medida = parseInt(medida); 160 | if (medida !== C.Medida80 && 161 | medida !== C.Medida100 && 162 | medida !== C.Medida156 && 163 | medida !== C.Medida200 && 164 | medida !== C.Medida300 && 165 | medida !== C.Medida350) { 166 | throw Error("Valor para medida del barcode inválido"); 167 | } 168 | } 169 | 170 | validarTipo(tipo) { 171 | if ( 172 | [C.AccionBarcode128, 173 | C.AccionBarcode39, 174 | C.AccionBarcode93, 175 | C.AccionBarcodeEAN, 176 | C.AccionBarcodeTwoOfFiveInterleaved, 177 | C.AccionBarcodeTwoOfFiveSinInterleaved, 178 | C.AccionBarcodeCodabar, 179 | C.AccionBarcodeUPCA, 180 | C.AccionBarcodeUPCE, 181 | ] 182 | .indexOf(tipo) === -1 183 | ) throw Error("Tipo de código de barras no soportado"); 184 | } 185 | 186 | barcode(contenido, medida, tipo) { 187 | this.validarMedida(medida); 188 | this.validarTipo(tipo); 189 | let payload = contenido.concat(",").concat(medida.toString()); 190 | this.operaciones.push(new OperacionTicket(tipo, payload)); 191 | } 192 | imprimirEnImpresoraConNombreEIp(nombreImpresora, ip) { 193 | const payload = { 194 | operaciones: this.operaciones, 195 | impresora: nombreImpresora, 196 | ip: ip, 197 | }; 198 | return fetch(this.ruta + "/imprimir_y_reenviar", { 199 | method: "POST", 200 | body: JSON.stringify(payload), 201 | }) 202 | .then(r => r.json()); 203 | } 204 | 205 | } -------------------------------------------------------------------------------- /plugin-impresora-v1/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Plugin versión 1 7 | 8 | 9 | 10 | 11 |

Si la lista está vacía asegúrese de que el plugin se está ejecutando y de que ha compartido la impresora

12 | 13 |
14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /plugin-impresora-v1/script.js: -------------------------------------------------------------------------------- 1 | document.addEventListener("DOMContentLoaded", async () => { 2 | const $impresoras = document.querySelector("#impresoras"), 3 | $mensaje = document.querySelector("#mensaje"), 4 | $imprimir = document.querySelector("#imprimir"); 5 | if (typeof Impresora === "undefined") { 6 | alert("No has importado el conector") 7 | return; 8 | } 9 | 10 | const refrescarListaDeImpresoras = async () => { 11 | try { 12 | const respuestaHttpImpresoras = await fetch("http://localhost:8000/impresoras"); 13 | const impresoras = await respuestaHttpImpresoras.json(); 14 | if (impresoras.length <= 0) { 15 | alert("No hay impresoras. Asegúrese de contar con una y compartirla"); 16 | } 17 | for (const impresora of impresoras) { 18 | const $option = Object.assign(document.createElement("option"), { 19 | value: impresora, 20 | text: impresora, 21 | }) 22 | $impresoras.append($option); 23 | } 24 | } catch (e) { 25 | alert("Error cargando impresoras. Asegúrese de que el plugin se está ejecutando " + e.message); 26 | } 27 | 28 | } 29 | 30 | await refrescarListaDeImpresoras(); 31 | 32 | const imprimir = async () => { 33 | const impresora = new Impresora(); 34 | impresora.write("Imprimiendo...\n"); 35 | impresora.setEmphasize(1); 36 | impresora.write("Texto enfatizado\n"); 37 | impresora.setEmphasize(0); 38 | impresora.write("Tu mensaje es: \n"); 39 | impresora.write($mensaje.value); 40 | impresora.write("\nAvanzando papel 5 lineas\n"); 41 | impresora.feed(5); 42 | impresora.setAlign(C.AlineacionCentro) 43 | impresora.write("Texto centrado\nTerminando trabajo..."); 44 | const respuesta = await impresora.imprimirEnImpresora($impresoras.value); 45 | console.log({respuesta}) 46 | } 47 | 48 | 49 | $imprimir.addEventListener("click", () => { 50 | imprimir(); 51 | }); 52 | 53 | 54 | }) -------------------------------------------------------------------------------- /prevenir-enter-textarea/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Prevenir nueva línea en textarea 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /prevenir-enter-textarea/script.js: -------------------------------------------------------------------------------- 1 | document.addEventListener("DOMContentLoaded", () => { 2 | const $textarea = document.querySelector("#miTextarea"); 3 | $textarea.addEventListener("keydown", (evento) => { 4 | if (evento.keyCode === 13) { 5 | evento.preventDefault(); 6 | } 7 | }); 8 | }); 9 | -------------------------------------------------------------------------------- /proxy-android-plugin-v1/conector.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Una clase para interactuar con el plugin 3 | * 4 | * @author parzibyte 5 | * @see https://parzibyte.me/blog 6 | */ 7 | const C = { 8 | AccionWrite: "write", 9 | AccionCut: "cut", 10 | AccionCash: "cash", 11 | AccionCutPartial: "cutpartial", 12 | AccionAlign: "align", 13 | AccionFontSize: "fontsize", 14 | AccionFont: "font", 15 | AccionEmphasize: "emphasize", 16 | AccionFeed: "feed", 17 | AccionQr: "qr", 18 | AlineacionCentro: "center", 19 | AlineacionDerecha: "right", 20 | AlineacionIzquierda: "left", 21 | FuenteA: "A", 22 | FuenteB: "B", 23 | AccionBarcode128: "barcode128", 24 | AccionBarcode39: "barcode39", 25 | AccionBarcode93: "barcode93", 26 | AccionBarcodeEAN: "barcodeEAN", 27 | AccionBarcodeTwoOfFiveSinInterleaved: "barcodeTwoOfFive", 28 | AccionBarcodeTwoOfFiveInterleaved: "barcodeTwoOfFiveInterleaved", 29 | AccionBarcodeCodabar: "barcodeCodabar", 30 | AccionBarcodeUPCA: "barcodeUPCA", 31 | AccionBarcodeUPCE: "barcodeUPCE", 32 | Medida80: 80, 33 | Medida100: 100, 34 | Medida156: 156, 35 | Medida200: 200, 36 | Medida300: 300, 37 | Medida350: 350, 38 | }; 39 | 40 | const URL_PLUGIN = "http://localhost:8000"; 41 | 42 | class OperacionTicket { 43 | constructor(accion, datos) { 44 | this.accion = accion + ""; 45 | this.datos = datos + ""; 46 | } 47 | } 48 | class Impresora { 49 | constructor(ruta) { 50 | if (!ruta) ruta = URL_PLUGIN; 51 | this.ruta = ruta; 52 | this.operaciones = []; 53 | } 54 | 55 | static setImpresora(nombreImpresora, ruta) { 56 | if (ruta) URL_PLUGIN = ruta; 57 | return fetch(URL_PLUGIN + "/impresora", { 58 | method: "PUT", 59 | body: JSON.stringify(nombreImpresora), 60 | }) 61 | .then(r => r.json()) 62 | .then(respuestaDecodificada => respuestaDecodificada === nombreImpresora); 63 | } 64 | static setImpresoraSilencioso(nombreImpresora, ruta) { 65 | if (ruta) URL_PLUGIN = ruta; 66 | return fetch(URL_PLUGIN + "/impresora_silencioso", { 67 | method: "PUT", 68 | body: JSON.stringify(nombreImpresora), 69 | }) 70 | .then(r => r.json()) 71 | .then(respuestaDecodificada => respuestaDecodificada === nombreImpresora); 72 | } 73 | 74 | 75 | static getImpresora(ruta) { 76 | if (ruta) URL_PLUGIN = ruta; 77 | return fetch(URL_PLUGIN + "/impresora") 78 | .then(r => r.json()); 79 | } 80 | 81 | static getImpresoras(ruta) { 82 | if (ruta) URL_PLUGIN = ruta; 83 | return fetch(URL_PLUGIN + "/impresoras") 84 | .then(r => r.json()); 85 | } 86 | 87 | static getImpresorasRemotas(ip) { 88 | return fetch(URL_PLUGIN + "/impresoras_remotas?ip=" + ip) 89 | .then(r => r.json()); 90 | } 91 | 92 | cut() { 93 | this.operaciones.push(new OperacionTicket(C.AccionCut, "")); 94 | } 95 | 96 | cash() { 97 | this.operaciones.push(new OperacionTicket(C.AccionCash, "")); 98 | } 99 | 100 | cutPartial() { 101 | this.operaciones.push(new OperacionTicket(C.AccionCutPartial, "")); 102 | } 103 | 104 | setFontSize(a, b) { 105 | this.operaciones.push(new OperacionTicket(C.AccionFontSize, `${a},${b}`)); 106 | } 107 | 108 | setFont(font) { 109 | if (font !== C.FuenteA && font !== C.FuenteB) throw Error("Fuente inválida"); 110 | this.operaciones.push(new OperacionTicket(C.AccionFont, font)); 111 | } 112 | setEmphasize(val) { 113 | if (isNaN(parseInt(val)) || parseInt(val) < 0) throw Error("Valor inválido"); 114 | this.operaciones.push(new OperacionTicket(C.AccionEmphasize, val)); 115 | } 116 | setAlign(align) { 117 | if (align !== C.AlineacionCentro && align !== C.AlineacionDerecha && align !== C.AlineacionIzquierda) { 118 | throw Error(`Alineación ${align} inválida`); 119 | } 120 | this.operaciones.push(new OperacionTicket(C.AccionAlign, align)); 121 | } 122 | 123 | write(text) { 124 | this.operaciones.push(new OperacionTicket(C.AccionWrite, text)); 125 | } 126 | 127 | feed(n) { 128 | if (!parseInt(n) || parseInt(n) < 0) { 129 | throw Error("Valor para feed inválido"); 130 | } 131 | this.operaciones.push(new OperacionTicket(C.AccionFeed, n)); 132 | } 133 | 134 | end() { 135 | return fetch(this.ruta + "/imprimir", { 136 | method: "POST", 137 | body: JSON.stringify(this.operaciones), 138 | }) 139 | .then(r => r.json()); 140 | } 141 | 142 | imprimirEnImpresora(nombreImpresora) { 143 | const payload = { 144 | operaciones: this.operaciones, 145 | impresora: nombreImpresora, 146 | }; 147 | return fetch(this.ruta + "/imprimir_en", { 148 | method: "POST", 149 | body: JSON.stringify(payload), 150 | }) 151 | .then(r => r.json()); 152 | } 153 | 154 | qr(contenido) { 155 | this.operaciones.push(new OperacionTicket(C.AccionQr, contenido)); 156 | } 157 | 158 | validarMedida(medida) { 159 | medida = parseInt(medida); 160 | if (medida !== C.Medida80 && 161 | medida !== C.Medida100 && 162 | medida !== C.Medida156 && 163 | medida !== C.Medida200 && 164 | medida !== C.Medida300 && 165 | medida !== C.Medida350) { 166 | throw Error("Valor para medida del barcode inválido"); 167 | } 168 | } 169 | 170 | validarTipo(tipo) { 171 | if ( 172 | [C.AccionBarcode128, 173 | C.AccionBarcode39, 174 | C.AccionBarcode93, 175 | C.AccionBarcodeEAN, 176 | C.AccionBarcodeTwoOfFiveInterleaved, 177 | C.AccionBarcodeTwoOfFiveSinInterleaved, 178 | C.AccionBarcodeCodabar, 179 | C.AccionBarcodeUPCA, 180 | C.AccionBarcodeUPCE, 181 | ] 182 | .indexOf(tipo) === -1 183 | ) throw Error("Tipo de código de barras no soportado"); 184 | } 185 | 186 | barcode(contenido, medida, tipo) { 187 | this.validarMedida(medida); 188 | this.validarTipo(tipo); 189 | let payload = contenido.concat(",").concat(medida.toString()); 190 | this.operaciones.push(new OperacionTicket(tipo, payload)); 191 | } 192 | imprimirEnImpresoraConNombreEIp(nombreImpresora, ip) { 193 | const payload = { 194 | operaciones: this.operaciones, 195 | impresora: nombreImpresora, 196 | ip: ip, 197 | }; 198 | return fetch(this.ruta + "/imprimir_y_reenviar", { 199 | method: "POST", 200 | body: JSON.stringify(payload), 201 | }) 202 | .then(r => r.json()); 203 | } 204 | 205 | } -------------------------------------------------------------------------------- /proxy-android-plugin-v1/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Consumir proxy de Android 8 | 9 | 10 | 11 | 12 | 15 |
16 | 18 |
19 | 20 |
21 | 22 |
23 |
24 | 25 |
26 | 104 | 105 | 106 | -------------------------------------------------------------------------------- /proxy-android-plugin-v3/detectar-e-imprimir.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Detectar e imprimir según puente 8 | 9 | 10 | 11 | 12 |
13 | 16 |
17 | 19 |
20 |
21 | 22 |
23 | 24 |
25 |
26 | 27 |
28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /proxy-android-plugin-v3/detectar-e-imprimir.js: -------------------------------------------------------------------------------- 1 | document.addEventListener("DOMContentLoaded", async () => { 2 | const $direccionPluginDesktop = document.querySelector("#direccionServidor"), 3 | $hacerPeticion = document.querySelector("#hacerPeticion"), 4 | $resultado = document.querySelector("#resultado"), 5 | $impresora = document.querySelector("#impresora"), 6 | $informacionRemota = document.querySelector("#contenedorInformacionRemota"); 7 | let estamosEnAndroid = false; 8 | try { 9 | // Puente y plugin devuelven la versión en la misma ruta 10 | // Recordar que para usar await hay que estar dentro de una función async 11 | const respuestaHttp = await fetch("http://localhost:8000/version"); 12 | const version = await respuestaHttp.json() 13 | estamosEnAndroid = version.plataforma === "Puente"; 14 | } catch (e) { 15 | $resultado.textContent = ("Error consultando versión. Asegúrese de que el plugin o el puente se están ejecutando: " + e.message); 16 | return; 17 | } 18 | // Si llegamos hasta aquí es porque se ha detectado la plataforma correctamente 19 | if (estamosEnAndroid) { 20 | $resultado.textContent = ("Estamos en Android con el puente"); 21 | } else { 22 | $resultado.textContent = ("Estamos en Desktop con el plugin") 23 | } 24 | 25 | 26 | 27 | 28 | if (!estamosEnAndroid) { 29 | $informacionRemota.style.display = "none"; 30 | } 31 | 32 | $hacerPeticion.addEventListener("click", async () => { 33 | /* 34 | Nombre de la impresora compartida en la computadora 35 | donde se ejecuta el plugin Desktop (NO ANDROID) 36 | */ 37 | 38 | let nombreImpresora = $impresora.value; 39 | // La carga útil es la misma tanto para el puente como para el 40 | // plugin 41 | const operaciones = [ 42 | { 43 | "nombre": "EscribirTexto", 44 | "argumentos": [ 45 | "Hola\nimpresora\ndesde puente\ncon JavaScript\nUna ñ: soy ñ\n" 46 | ] 47 | } 48 | ]; 49 | const payload = { 50 | serial: "", 51 | nombreImpresora: nombreImpresora, 52 | operaciones: operaciones 53 | }; 54 | 55 | 56 | // Y aquí comprobamos si se imprime con el puente o sin él 57 | 58 | if (estamosEnAndroid) { 59 | const direccionRemota = $direccionPluginDesktop.value; 60 | const respuestaHttp = await fetch("http://localhost:8000", { 61 | body: JSON.stringify(payload), 62 | method: "POST", 63 | headers: { 64 | "x-reenviar-a": direccionRemota, 65 | } 66 | }); 67 | const respuesta = await respuestaHttp.json(); 68 | if (respuesta.ok) { 69 | $resultado.textContent = ("Impreso correctamente"); 70 | } else { 71 | $resultado.textContent = ("Error: " + respuesta.message); 72 | } 73 | } else { 74 | const respuestaHttp = await fetch("http://localhost:8000/imprimir", { 75 | body: JSON.stringify(payload), 76 | method: "POST", 77 | }); 78 | const respuesta = await respuestaHttp.json(); 79 | if (respuesta.ok) { 80 | $resultado.textContent = ("Impreso correctamente"); 81 | } else { 82 | $resultado.textContent = ("Error: " + respuesta.message); 83 | } 84 | } 85 | }) 86 | 87 | }); -------------------------------------------------------------------------------- /proxy-android-plugin-v3/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Consumir proxy de Android con plugin versión 3 8 | 9 | 10 | 11 | 14 |
15 | 17 |
18 | 19 |
20 | 21 |
22 |
23 | 24 |
25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /proxy-android-plugin-v3/script.js: -------------------------------------------------------------------------------- 1 | document.addEventListener("DOMContentLoaded", () => { 2 | const $direccionPluginDesktop = document.querySelector("#direccionServidor"), 3 | $hacerPeticion = document.querySelector("#hacerPeticion"), 4 | $resultado = document.querySelector("#resultado"), 5 | $impresora = document.querySelector("#impresora"); 6 | $hacerPeticion.addEventListener("click", async () => { 7 | $resultado.textContent = "cargando..."; 8 | try { 9 | /* 10 | La lista de operaciones se crea como siempre 11 | según la API: https://parzibyte.me/http-esc-pos-desktop-docs/es/ 12 | 13 | Aquí es un simple "Hola mundo" pero 14 | puede ser cualquier otro 15 | */ 16 | const operaciones = [ 17 | { 18 | "nombre": "EscribirTexto", 19 | "argumentos": [ 20 | "Hola\nimpresora\ndesde puente\ncon JavaScript\nUna ñ: soy ñ\n" 21 | ] 22 | } 23 | ]; 24 | 25 | /* 26 | Nombre de la impresora compartida en la computadora 27 | donde se ejecuta el plugin Desktop (NO ANDROID) 28 | */ 29 | 30 | let nombreImpresora = $impresora.value; 31 | 32 | 33 | const payload = { 34 | serial: "", 35 | nombreImpresora: nombreImpresora, 36 | operaciones: operaciones 37 | }; 38 | 39 | /* 40 | Dirección para comunicarnos con "Puente para plugin". 41 | 42 | Recuerda: este código se ejecutará en un navegador web, 43 | y ese navegador web se debe ejecutar en el mismo dispositivo 44 | Android donde está el Puente para el plugin minimizado 45 | */ 46 | const direccionPuenteLocal = "http://localhost:8000"; 47 | 48 | 49 | /* 50 | 51 | Dirección del plugin Desktop versión 1 (NO ANDROID). Es la 52 | IP de la computadora que se encuentra en la misma LAN 53 | que el dispositivo Android, incluyendo el puerto 54 | 55 | Si la IP de la computadora que ejecuta el plugin es 56 | 192.168.0.6 y el plugin Desktop versión 1 (NO ANDROID) se 57 | ejecuta en el puerto 8000 entonces podemos decir que la 58 | dirección remota del plugin desktop es http://192.168.0.6:8000 59 | 60 | Recuerda que esa es la raíz, pero para imprimir debemos invocar 61 | a la ruta /imprimir_en así que queda en 62 | http://192.168.0.6:8000/imprimir_en 63 | 64 | En este caso 192.168.0.6 es una IP de ejemplo. En tu caso debes 65 | averiguar la IP de la computadora que ejecuta el plugin Desktop 66 | y de ser posible hacerla estática 67 | */ 68 | const direccionRemota = $direccionPluginDesktop.value; 69 | 70 | const respuestaHttp = await fetch(direccionPuenteLocal, { 71 | method: "POST", 72 | body: JSON.stringify(payload), 73 | headers: { 74 | "x-reenviar-a": direccionRemota, 75 | } 76 | }) 77 | const respuestaComoTexto = await respuestaHttp.text(); 78 | $resultado.textContent = respuestaComoTexto; 79 | } catch (e) { 80 | $resultado.textContent = "Error: " + e.message; 81 | } 82 | 83 | }) 84 | }) 85 | -------------------------------------------------------------------------------- /quaggajs/basico/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Leer código de barras con JavaScript by parzibyte 7 | 8 | 9 | 10 | By Parzibyte 11 |

Aquí aparecerá el código

12 |

A continuación, el contenedor:

13 |
14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /quaggajs/basico/script.js: -------------------------------------------------------------------------------- 1 | document.addEventListener("DOMContentLoaded", () => { 2 | const $resultados = document.querySelector("#resultado"); 3 | Quagga.init({ 4 | inputStream: { 5 | constraints: { 6 | width: 1920, 7 | height: 1080, 8 | }, 9 | name: "Live", 10 | type: "LiveStream", 11 | target: document.querySelector('#contenedor'), // Pasar el elemento del DOM 12 | }, 13 | decoder: { 14 | readers: ["ean_reader"] 15 | } 16 | }, function (err) { 17 | if (err) { 18 | console.log(err); 19 | return 20 | } 21 | console.log("Iniciado correctamente"); 22 | Quagga.start(); 23 | }); 24 | 25 | Quagga.onDetected((data) => { 26 | $resultados.textContent = data.codeResult.code; 27 | // Imprimimos todo el data para que puedas depurar 28 | console.log(data); 29 | }); 30 | }); -------------------------------------------------------------------------------- /quaggajs/basico/style.css: -------------------------------------------------------------------------------- 1 | #contenedor video{ 2 | max-width: 100%; 3 | width: 100%; 4 | } 5 | #contenedor{ 6 | max-width: 100%; 7 | position:relative; 8 | } 9 | canvas{ 10 | max-width: 100%; 11 | } 12 | canvas.drawingBuffer{ 13 | position:absolute; 14 | top:0; 15 | left:0; 16 | } -------------------------------------------------------------------------------- /quaggajs/con-dibujo/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Leer código de barras con JavaScript by parzibyte 7 | 8 | 9 | 10 | By Parzibyte 11 |

Aquí aparecerá el código

12 |

A continuación, el contenedor:

13 |
14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /quaggajs/con-dibujo/script.js: -------------------------------------------------------------------------------- 1 | document.addEventListener("DOMContentLoaded", () => { 2 | const $resultados = document.querySelector("#resultado"); 3 | Quagga.init({ 4 | inputStream: { 5 | constraints: { 6 | width: 1920, 7 | height: 1080, 8 | }, 9 | name: "Live", 10 | type: "LiveStream", 11 | target: document.querySelector('#contenedor'), // Pasar el elemento del DOM 12 | }, 13 | decoder: { 14 | readers: ["ean_reader"] 15 | } 16 | }, function (err) { 17 | if (err) { 18 | console.log(err); 19 | return 20 | } 21 | console.log("Iniciado correctamente"); 22 | Quagga.start(); 23 | }); 24 | 25 | Quagga.onDetected((data) => { 26 | $resultados.textContent = data.codeResult.code; 27 | // Imprimimos todo el data para que puedas depurar 28 | console.log(data); 29 | }); 30 | 31 | Quagga.onProcessed(function (result) { 32 | var drawingCtx = Quagga.canvas.ctx.overlay, 33 | drawingCanvas = Quagga.canvas.dom.overlay; 34 | 35 | if (result) { 36 | if (result.boxes) { 37 | drawingCtx.clearRect(0, 0, parseInt(drawingCanvas.getAttribute("width")), parseInt(drawingCanvas.getAttribute("height"))); 38 | result.boxes.filter(function (box) { 39 | return box !== result.box; 40 | }).forEach(function (box) { 41 | Quagga.ImageDebug.drawPath(box, { x: 0, y: 1 }, drawingCtx, { color: "green", lineWidth: 2 }); 42 | }); 43 | } 44 | 45 | if (result.box) { 46 | Quagga.ImageDebug.drawPath(result.box, { x: 0, y: 1 }, drawingCtx, { color: "#00F", lineWidth: 2 }); 47 | } 48 | 49 | if (result.codeResult && result.codeResult.code) { 50 | Quagga.ImageDebug.drawPath(result.line, { x: 'x', y: 'y' }, drawingCtx, { color: 'red', lineWidth: 3 }); 51 | } 52 | } 53 | }); 54 | }); -------------------------------------------------------------------------------- /quaggajs/con-dibujo/style.css: -------------------------------------------------------------------------------- 1 | #contenedor video{ 2 | max-width: 100%; 3 | width: 100%; 4 | } 5 | #contenedor{ 6 | max-width: 100%; 7 | position:relative; 8 | } 9 | canvas{ 10 | max-width: 100%; 11 | } 12 | canvas.drawingBuffer{ 13 | position:absolute; 14 | top:0; 15 | left:0; 16 | } -------------------------------------------------------------------------------- /quaggajs/lector-en-ventana/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Lectura de código de barras 7 | 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /quaggajs/lector-en-ventana/leer.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Lectura de código de barras 7 | 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /quaggajs/lector-en-ventana/script.js: -------------------------------------------------------------------------------- 1 | document.addEventListener("DOMContentLoaded", () => { 2 | const $btnEscanear = document.querySelector("#btnEscanear"), 3 | $input = document.querySelector("#codigo"); 4 | $btnEscanear.addEventListener("click", ()=>{ 5 | window.open("leer.html"); 6 | }); 7 | 8 | window.onCodigoLeido = datosCodigo => { 9 | console.log("Oh sí, código leído: ") 10 | console.log(datosCodigo) 11 | $input.value = datosCodigo.codeResult.code; 12 | } 13 | }); -------------------------------------------------------------------------------- /quaggajs/lector-en-ventana/scriptLeer.js: -------------------------------------------------------------------------------- 1 | document.addEventListener("DOMContentLoaded", () => { 2 | const $resultados = document.querySelector("#resultados"); 3 | Quagga.init({ 4 | inputStream: { 5 | constraints: { 6 | width: 1920, 7 | height: 1080, 8 | }, 9 | name: "Live", 10 | type: "LiveStream", 11 | target: document.querySelector('#contenedor') // Or '#yourElement' (optional) 12 | }, 13 | decoder: { 14 | readers: ["ean_reader"] 15 | } 16 | }, function (err) { 17 | if (err) { 18 | console.log(err); 19 | return 20 | } 21 | console.log("Initialization finished. Ready to start"); 22 | Quagga.start(); 23 | }); 24 | 25 | Quagga.onDetected((data) => { 26 | if(window.opener){ 27 | window.opener.onCodigoLeido(data); 28 | window.close(); 29 | } 30 | }); 31 | 32 | Quagga.onProcessed(function (result) { 33 | var drawingCtx = Quagga.canvas.ctx.overlay, 34 | drawingCanvas = Quagga.canvas.dom.overlay; 35 | 36 | if (result) { 37 | if (result.boxes) { 38 | drawingCtx.clearRect(0, 0, parseInt(drawingCanvas.getAttribute("width")), parseInt(drawingCanvas.getAttribute("height"))); 39 | result.boxes.filter(function (box) { 40 | return box !== result.box; 41 | }).forEach(function (box) { 42 | Quagga.ImageDebug.drawPath(box, { x: 0, y: 1 }, drawingCtx, { color: "green", lineWidth: 2 }); 43 | }); 44 | } 45 | 46 | if (result.box) { 47 | Quagga.ImageDebug.drawPath(result.box, { x: 0, y: 1 }, drawingCtx, { color: "#00F", lineWidth: 2 }); 48 | } 49 | 50 | if (result.codeResult && result.codeResult.code) { 51 | Quagga.ImageDebug.drawPath(result.line, { x: 'x', y: 'y' }, drawingCtx, { color: 'red', lineWidth: 3 }); 52 | } 53 | } 54 | }); 55 | }); -------------------------------------------------------------------------------- /quaggajs/lector-en-ventana/style.css: -------------------------------------------------------------------------------- 1 | #contenedor video{ 2 | max-width: 100%; 3 | width: 100%; 4 | } 5 | #contenedor{ 6 | max-width: 100%; 7 | position:relative; 8 | } 9 | canvas{ 10 | max-width: 100%; 11 | } 12 | canvas.drawingBuffer{ 13 | position:absolute; 14 | top:0; 15 | left:0; 16 | } -------------------------------------------------------------------------------- /recortar-imagen/css/cropper.min.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * Cropper.js v1.5.12 3 | * https://fengyuanchen.github.io/cropperjs 4 | * 5 | * Copyright 2015-present Chen Fengyuan 6 | * Released under the MIT license 7 | * 8 | * Date: 2021-06-12T08:00:11.623Z 9 | */.cropper-container{direction:ltr;font-size:0;line-height:0;position:relative;-ms-touch-action:none;touch-action:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.cropper-container img{image-orientation:0deg;display:block;height:100%;max-height:none!important;max-width:none!important;min-height:0!important;min-width:0!important;width:100%}.cropper-canvas,.cropper-crop-box,.cropper-drag-box,.cropper-modal,.cropper-wrap-box{bottom:0;left:0;position:absolute;right:0;top:0}.cropper-canvas,.cropper-wrap-box{overflow:hidden}.cropper-drag-box{background-color:#fff;opacity:0}.cropper-modal{background-color:#000;opacity:.5}.cropper-view-box{display:block;height:100%;outline:1px solid #39f;outline-color:rgba(51,153,255,.75);overflow:hidden;width:100%}.cropper-dashed{border:0 dashed #eee;display:block;opacity:.5;position:absolute}.cropper-dashed.dashed-h{border-bottom-width:1px;border-top-width:1px;height:33.33333%;left:0;top:33.33333%;width:100%}.cropper-dashed.dashed-v{border-left-width:1px;border-right-width:1px;height:100%;left:33.33333%;top:0;width:33.33333%}.cropper-center{display:block;height:0;left:50%;opacity:.75;position:absolute;top:50%;width:0}.cropper-center:after,.cropper-center:before{background-color:#eee;content:" ";display:block;position:absolute}.cropper-center:before{height:1px;left:-3px;top:0;width:7px}.cropper-center:after{height:7px;left:0;top:-3px;width:1px}.cropper-face,.cropper-line,.cropper-point{display:block;height:100%;opacity:.1;position:absolute;width:100%}.cropper-face{background-color:#fff;left:0;top:0}.cropper-line{background-color:#39f}.cropper-line.line-e{cursor:ew-resize;right:-3px;top:0;width:5px}.cropper-line.line-n{cursor:ns-resize;height:5px;left:0;top:-3px}.cropper-line.line-w{cursor:ew-resize;left:-3px;top:0;width:5px}.cropper-line.line-s{bottom:-3px;cursor:ns-resize;height:5px;left:0}.cropper-point{background-color:#39f;height:5px;opacity:.75;width:5px}.cropper-point.point-e{cursor:ew-resize;margin-top:-3px;right:-3px;top:50%}.cropper-point.point-n{cursor:ns-resize;left:50%;margin-left:-3px;top:-3px}.cropper-point.point-w{cursor:ew-resize;left:-3px;margin-top:-3px;top:50%}.cropper-point.point-s{bottom:-3px;cursor:s-resize;left:50%;margin-left:-3px}.cropper-point.point-ne{cursor:nesw-resize;right:-3px;top:-3px}.cropper-point.point-nw{cursor:nwse-resize;left:-3px;top:-3px}.cropper-point.point-sw{bottom:-3px;cursor:nesw-resize;left:-3px}.cropper-point.point-se{bottom:-3px;cursor:nwse-resize;height:20px;opacity:1;right:-3px;width:20px}@media (min-width:768px){.cropper-point.point-se{height:15px;width:15px}}@media (min-width:992px){.cropper-point.point-se{height:10px;width:10px}}@media (min-width:1200px){.cropper-point.point-se{height:5px;opacity:.75;width:5px}}.cropper-point.point-se:before{background-color:#39f;bottom:-50%;content:" ";display:block;height:200%;opacity:0;position:absolute;right:-50%;width:200%}.cropper-invisible{opacity:0}.cropper-bg{background-image:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQAQMAAAAlPW0iAAAAA3NCSVQICAjb4U/gAAAABlBMVEXMzMz////TjRV2AAAACXBIWXMAAArrAAAK6wGCiw1aAAAAHHRFWHRTb2Z0d2FyZQBBZG9iZSBGaXJld29ya3MgQ1M26LyyjAAAABFJREFUCJlj+M/AgBVhF/0PAH6/D/HkDxOGAAAAAElFTkSuQmCC")}.cropper-hide{display:block;height:0;position:absolute;width:0}.cropper-hidden{display:none!important}.cropper-move{cursor:move}.cropper-crop{cursor:crosshair}.cropper-disabled .cropper-drag-box,.cropper-disabled .cropper-face,.cropper-disabled .cropper-line,.cropper-disabled .cropper-point{cursor:not-allowed} -------------------------------------------------------------------------------- /recortar-imagen/css/estilo.css: -------------------------------------------------------------------------------- 1 | /* Ensure the size of the image fit the container perfectly */ 2 | img { 3 | display: block; 4 | /* This rule is very important, please don't ignore this */ 5 | max-width: 100%; 6 | } 7 | -------------------------------------------------------------------------------- /recortar-imagen/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Recortar imagen con JavaScript - By Parzibyte 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 |

Recortar imagen con JavaScript

18 |
19 | 20 |
21 | By Parzibyte 22 |
23 | 24 |
25 | 26 | 27 | -------------------------------------------------------------------------------- /recortar-imagen/js/script.js: -------------------------------------------------------------------------------- 1 | document.addEventListener("DOMContentLoaded", () => { 2 | const MIME_TYPE_IMAGEN_DESCARGADA = "image/jpeg", 3 | EXTENSION_IMAGEN_DESCARGADA = "jpg", 4 | CALIDAD_JPG = 1; 5 | 6 | const $btnDescargar = document.querySelector("#descargar"); 7 | const $imagen = document.querySelector("#imagen"); 8 | 9 | let cropper = new Cropper($imagen, { 10 | responsive: false, // <-- Si no se pone en false, la imagen se mueve cuando cambia el tamaño de la ventana 11 | }); 12 | 13 | $btnDescargar.onclick = () => { 14 | if (!cropper) { 15 | return; 16 | } 17 | // Obtener el canvas recortado 18 | const canvas = cropper.getCroppedCanvas(); 19 | // Aquí ya podemos hacer cualquier cosa con el canvas 20 | let enlace = document.createElement('a'); 21 | // Nombre de la imagen que se descarga 22 | enlace.download = "imagen_recortada." + EXTENSION_IMAGEN_DESCARGADA; 23 | enlace.href = canvas.toDataURL(MIME_TYPE_IMAGEN_DESCARGADA, CALIDAD_JPG); 24 | enlace.click(); 25 | }; 26 | 27 | }); -------------------------------------------------------------------------------- /recortar-imagen/video-game-2234745_1920.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/parzibyte/ejemplos-javascript/31d317bcce8f9075d2c7a76b8e969a895f800212/recortar-imagen/video-game-2234745_1920.jpg -------------------------------------------------------------------------------- /reducir-tamaño-imagen/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Comprimir imagen - By Parzibyte 9 | 10 | 11 | 12 |

Comprimir imagen - By Parzibyte

13 | By Parzibyte 14 |
15 | 16 |
17 | 18 |
19 | 20 | 21 |
22 |
23 |
24 | 25 |
26 |
27 | 28 | 29 |
30 |
31 | 32 | 33 | 34 | 98 | 99 | -------------------------------------------------------------------------------- /reproducir-sonido/README.md: -------------------------------------------------------------------------------- 1 | ## Créditos 2 | Sonido tomado de https://freesound.org/people/Timbre/sounds/188387/ 3 | ¡Muchas gracias! -------------------------------------------------------------------------------- /reproducir-sonido/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Reproducir sonido en JavaScript 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /reproducir-sonido/script.js: -------------------------------------------------------------------------------- 1 | 2 | // Carga un sonido a través de su fuente y lo inyecta de manera oculta 3 | const cargarSonido = function (fuente) { 4 | const sonido = document.createElement("audio"); 5 | sonido.src = fuente; 6 | sonido.setAttribute("preload", "auto"); 7 | sonido.setAttribute("controls", "none"); 8 | sonido.style.display = "none"; // <-- oculto 9 | document.body.appendChild(sonido); 10 | return sonido; 11 | }; 12 | const $botonReproducir = document.querySelector("#btnReproducir"), 13 | $botonPausar = document.querySelector("#btnPausar"), 14 | $botonReiniciar = document.querySelector("#btnReiniciar"); 15 | // El sonido que podemos reproducir o pausar 16 | const sonido = cargarSonido("sonido.flac"); 17 | $botonReproducir.onclick = () => { 18 | sonido.play(); 19 | }; 20 | $botonPausar.onclick = () => { 21 | sonido.pause(); 22 | }; 23 | $botonReiniciar.onclick = () => { 24 | sonido.currentTime = 0; 25 | }; -------------------------------------------------------------------------------- /reproducir-sonido/sonido.flac: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/parzibyte/ejemplos-javascript/31d317bcce8f9075d2c7a76b8e969a895f800212/reproducir-sonido/sonido.flac -------------------------------------------------------------------------------- /resolucion-imagen/code.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/parzibyte/ejemplos-javascript/31d317bcce8f9075d2c7a76b8e969a895f800212/resolucion-imagen/code.png -------------------------------------------------------------------------------- /resolucion-imagen/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Tamaño de imagen con JavaScript 8 | 9 | 10 | 11 | Example image 12 |
13 | 14 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /rotar-imagen/captura_codigo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/parzibyte/ejemplos-javascript/31d317bcce8f9075d2c7a76b8e969a895f800212/rotar-imagen/captura_codigo.png -------------------------------------------------------------------------------- /rotar-imagen/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Rotar imagen - https://parzibyte.me 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 78 | 79 | -------------------------------------------------------------------------------- /sweet-alert-2/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Tutorial de SweetAlert 2 8 | 9 | 10 | 11 |

Mostrar alertas con Sweet Alert 2

12 | By Parzibyte 13 |

14 |   15 |   16 |   17 |   18 |   19 |   20 | 22 | 23 | -------------------------------------------------------------------------------- /sweet-alert-2/script.js: -------------------------------------------------------------------------------- 1 | const $btnSimple = document.querySelector("#btnSimple"), 2 | $btnBotonPersonalizado = document.querySelector("#btnBotonPersonalizado"), 3 | $btnTituloYTexto = document.querySelector("#btnTituloYTexto"), 4 | $btnHtmlPersonalizado = document.querySelector("#btnHtmlPersonalizado"), 5 | $btnConfirm = document.querySelector("#btnConfirm"), 6 | $btnInput = document.querySelector("#btnInput"); 7 | 8 | $btnSimple.addEventListener("click", () => { 9 | Swal.fire("Esta es una alerta") 10 | .then(() => { 11 | // Aquí la alerta se ha cerrado 12 | console.log("Alerta cerrada"); 13 | }); 14 | }); 15 | 16 | $btnBotonPersonalizado.addEventListener("click", () => { 17 | Swal.fire({ 18 | title: "Venta realizada", 19 | confirmButtonText: "Aceptar", 20 | }); 21 | }); 22 | 23 | 24 | $btnTituloYTexto.addEventListener("click", () => { 25 | Swal.fire({ 26 | title: "Venta realizada", 27 | text: "La venta fue guardada con el id 123456", 28 | confirmButtonText: "Aceptar", 29 | }); 30 | }); 31 | 32 | 33 | $btnHtmlPersonalizado.addEventListener("click", () => { 34 | Swal.fire({ 35 | html: `

Venta realizada

36 |

Venta guardada con el id 12321312

37 |
38 | Imprimir ticket 39 | `, 40 | }); 41 | }); 42 | 43 | $btnConfirm.addEventListener("click", () => { 44 | 45 | Swal 46 | .fire({ 47 | title: "Venta #123465", 48 | text: "¿Eliminar?", 49 | icon: 'warning', 50 | showCancelButton: true, 51 | confirmButtonText: "Sí, eliminar", 52 | cancelButtonText: "Cancelar", 53 | }) 54 | .then(resultado => { 55 | if (resultado.value) { 56 | // Hicieron click en "Sí" 57 | console.log("*se elimina la venta*"); 58 | } else { 59 | // Dijeron que no 60 | console.log("*NO se elimina la venta*"); 61 | } 62 | }); 63 | }); 64 | 65 | 66 | $btnInput.addEventListener("click", () => { 67 | Swal 68 | .fire({ 69 | title: "Tu nombre", 70 | input: "text", 71 | showCancelButton: true, 72 | confirmButtonText: "Guardar", 73 | cancelButtonText: "Cancelar", 74 | inputValidator: nombre => { 75 | // Si el valor es válido, debes regresar undefined. Si no, una cadena 76 | if (!nombre) { 77 | return "Por favor escribe tu nombre"; 78 | } else { 79 | return undefined; 80 | } 81 | } 82 | }) 83 | .then(resultado => { 84 | if (resultado.value) { 85 | let nombre = resultado.value; 86 | console.log("Hola, " + nombre); 87 | } 88 | }); 89 | }); -------------------------------------------------------------------------------- /tasa-cambio/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Productos 8 | 9 | 10 | 11 | Ajustar tasa 12 | Registrar producto 13 | Ver productos 14 |

Productos

15 |
16 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /tasa-cambio/registrar_producto.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Registrar producto 8 | 9 | 10 | 11 | Ajustar tasa 12 | Registrar producto 13 | Ver productos 14 |

Registrando producto

15 | 18 | 22 | 23 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /tasa-cambio/tasa.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Ajustar tasa 8 | 9 | 10 | 11 | 12 | Ajustar tasa 13 | Registrar producto 14 | Ver productos 15 |
16 | 19 | 20 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /unir-archivos/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Unir archivos con JavaScript 8 | 9 | 10 | 11 |

Unir archivos con JavaScript

12 |

Seleccionar los archivos a unir con el siguiente botón

13 | 14 | 60 | 61 | 62 | -------------------------------------------------------------------------------- /validacion-formularios/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Validar formularios con JavaScript - By Parzibyte 10 | 11 | 13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 |
21 | 22 |
23 |

Validación de formularios con JavaScript

24 | By Parzibyte 25 |
26 |
27 |
28 | 29 |
30 |
31 | 33 |
34 | 35 |
36 |
37 |
38 |
39 | 60 | 61 | 62 | 63 | -------------------------------------------------------------------------------- /webapp-telegram/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Telegram Web App 9 | 10 | 11 | 12 |

Elige tus opciones

13 | 14 | 16 |

17 | 18 | 20 |

21 |

Inicio

22 | 23 |

Fin

24 | 25 |

Radio en metros

26 | 27 |
28 | Seleccione los días de la semana 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 |
51 | 52 | 53 | 98 | 99 | 100 | -------------------------------------------------------------------------------- /wysiwyg/font/summernote.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/parzibyte/ejemplos-javascript/31d317bcce8f9075d2c7a76b8e969a895f800212/wysiwyg/font/summernote.eot -------------------------------------------------------------------------------- /wysiwyg/font/summernote.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/parzibyte/ejemplos-javascript/31d317bcce8f9075d2c7a76b8e969a895f800212/wysiwyg/font/summernote.ttf -------------------------------------------------------------------------------- /wysiwyg/font/summernote.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/parzibyte/ejemplos-javascript/31d317bcce8f9075d2c7a76b8e969a895f800212/wysiwyg/font/summernote.woff -------------------------------------------------------------------------------- /wysiwyg/font/summernote.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/parzibyte/ejemplos-javascript/31d317bcce8f9075d2c7a76b8e969a895f800212/wysiwyg/font/summernote.woff2 -------------------------------------------------------------------------------- /wysiwyg/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Demostrar WYSIWYG 8 | 9 | 10 | 12 | 14 | 15 | 16 | 17 | 18 | 19 |
Comienza a editar este contenido
20 | 21 | 30 | 31 | 32 | --------------------------------------------------------------------------------