Make htmx IE11 compatible again + tests IE11 compatible (#1687)

* Make htmx IE11 compatible again + tests IE11 compatible

* IE11 compatible handmade socket mock for ws-ext tests

* Fallback when xpath isn't supported, hx-on wildcard now working on IE11

* Merge remote-tracking branch 'upstream/relative-url-in-hx-boost' into ie11-compatibility
This commit is contained in:
Vincent 2023-09-06 17:55:18 +02:00 committed by GitHub
parent 1763cfc64e
commit 55c30b5607
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 449 additions and 181 deletions

237
package-lock.json generated
View File

@ -1,27 +1,24 @@
{
"name": "htmx.org",
"version": "1.9.3",
"version": "1.9.5",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "htmx.org",
"version": "1.9.3",
"version": "1.9.5",
"license": "BSD 2-Clause",
"devDependencies": {
"chai": "^4.3.7",
"chai-dom": "^1.11.0",
"fs-extra": "^9.1.0",
"mocha": "^10.2.0",
"mocha": "^9.2.2",
"mocha-chrome": "^2.2.0",
"mocha-webdriver-runner": "^0.6.4",
"mock-socket": "^9.2.1",
"sinon": "^9.2.4",
"typescript": "^4.9.5",
"uglify-js": "^3.17.4"
},
"engines": {
"node": "15.x"
}
},
"node_modules/@sinonjs/commons": {
@ -65,6 +62,12 @@
"integrity": "sha512-LB2R1Oyhpg8gu4SON/mfforE525+Hi/M1ineICEDftqNVTyFg1aRIeGuTvXAoWHc4nbrFncWtJgMmoyRvuGh7A==",
"dev": true
},
"node_modules/@ungap/promise-all-settled": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz",
"integrity": "sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q==",
"dev": true
},
"node_modules/@zbigg/treesync": {
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/@zbigg/treesync/-/treesync-0.3.0.tgz",
@ -385,23 +388,22 @@
}
},
"node_modules/debug": {
"version": "4.3.4",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
"integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
"version": "4.3.3",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz",
"integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==",
"dev": true,
"dependencies": {
"ms": "2.1.2"
},
"engines": {
"node": ">=6.0"
},
"peerDependenciesMeta": {
"supports-color": {
"optional": true
}
}
},
"node_modules/debug/node_modules/ms": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
"dev": true
},
"node_modules/decamelize": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz",
@ -625,6 +627,15 @@
"integrity": "sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ==",
"dev": true
},
"node_modules/growl": {
"version": "1.10.5",
"resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz",
"integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==",
"dev": true,
"engines": {
"node": ">=4.x"
}
},
"node_modules/has-flag": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
@ -795,6 +806,12 @@
"integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=",
"dev": true
},
"node_modules/isexe": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
"integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
"dev": true
},
"node_modules/json-parse-better-errors": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz",
@ -1059,39 +1076,46 @@
}
},
"node_modules/mocha": {
"version": "10.2.0",
"resolved": "https://registry.npmjs.org/mocha/-/mocha-10.2.0.tgz",
"integrity": "sha512-IDY7fl/BecMwFHzoqF2sg/SHHANeBoMMXFlS9r0OXKDssYE1M5O43wUY/9BVPeIvfH2zmEbBfseqN9gBQZzXkg==",
"version": "9.2.2",
"resolved": "https://registry.npmjs.org/mocha/-/mocha-9.2.2.tgz",
"integrity": "sha512-L6XC3EdwT6YrIk0yXpavvLkn8h+EU+Y5UcCHKECyMbdUIxyMuZj4bX4U9e1nvnvUUvQVsV2VHQr5zLdcUkhW/g==",
"dev": true,
"dependencies": {
"@ungap/promise-all-settled": "1.1.2",
"ansi-colors": "4.1.1",
"browser-stdout": "1.3.1",
"chokidar": "3.5.3",
"debug": "4.3.4",
"debug": "4.3.3",
"diff": "5.0.0",
"escape-string-regexp": "4.0.0",
"find-up": "5.0.0",
"glob": "7.2.0",
"growl": "1.10.5",
"he": "1.2.0",
"js-yaml": "4.1.0",
"log-symbols": "4.1.0",
"minimatch": "5.0.1",
"minimatch": "4.2.1",
"ms": "2.1.3",
"nanoid": "3.3.3",
"nanoid": "3.3.1",
"serialize-javascript": "6.0.0",
"strip-json-comments": "3.1.1",
"supports-color": "8.1.1",
"workerpool": "6.2.1",
"which": "2.0.2",
"workerpool": "6.2.0",
"yargs": "16.2.0",
"yargs-parser": "20.2.4",
"yargs-unparser": "2.0.0"
},
"bin": {
"_mocha": "bin/_mocha",
"mocha": "bin/mocha.js"
"mocha": "bin/mocha"
},
"engines": {
"node": ">= 14.0.0"
"node": ">= 12.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/mochajs"
}
},
"node_modules/mocha-chrome": {
@ -1118,15 +1142,6 @@
"node": ">= 8.0.0"
}
},
"node_modules/mocha-chrome/node_modules/debug": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
"integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
"dev": true,
"dependencies": {
"ms": "^2.1.1"
}
},
"node_modules/mocha-webdriver-runner": {
"version": "0.6.4",
"resolved": "https://registry.npmjs.org/mocha-webdriver-runner/-/mocha-webdriver-runner-0.6.4.tgz",
@ -1289,26 +1304,17 @@
}
},
"node_modules/mocha/node_modules/minimatch": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.0.1.tgz",
"integrity": "sha512-nLDxIFRyhDblz3qMuq+SoRZED4+miJ/G+tdDrjkkkRnjAsBexeGpgjLEQ0blJy7rHhR2b93rhQY4SvyWu9v03g==",
"version": "4.2.1",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-4.2.1.tgz",
"integrity": "sha512-9Uq1ChtSZO+Mxa/CL1eGizn2vRn3MlLgzhT0Iz8zaY8NdvxvB0d5QdPFmCKf7JKA9Lerx5vRrnwO03jsSfGG9g==",
"dev": true,
"dependencies": {
"brace-expansion": "^2.0.1"
"brace-expansion": "^1.1.7"
},
"engines": {
"node": ">=10"
}
},
"node_modules/mocha/node_modules/minimatch/node_modules/brace-expansion": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
"integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
"dev": true,
"dependencies": {
"balanced-match": "^1.0.0"
}
},
"node_modules/mocha/node_modules/ms": {
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
@ -1409,9 +1415,9 @@
}
},
"node_modules/ms": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz",
"integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==",
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
"dev": true
},
"node_modules/nanoassert": {
@ -1432,9 +1438,9 @@
}
},
"node_modules/nanoid": {
"version": "3.3.3",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.3.tgz",
"integrity": "sha512-p1sjXuopFs0xg+fPASzQ28agW1oHD7xDsd9Xkf3T15H3c/cifrFHVwrh74PdoklAPi+i7MdRsE47vm2r6JoB+w==",
"version": "3.3.1",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.1.tgz",
"integrity": "sha512-n6Vs/3KGyxPQd6uO0eH4Bv0ojGSUvuLlIHtC3Y0kEO23YRge8H9x1GCzLn28YX0H66pMkxuaeESFq4tKISKwdw==",
"dev": true,
"bin": {
"nanoid": "bin/nanoid.cjs"
@ -2166,10 +2172,25 @@
"spdx-expression-parse": "^3.0.0"
}
},
"node_modules/which": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
"integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
"dev": true,
"dependencies": {
"isexe": "^2.0.0"
},
"bin": {
"node-which": "bin/node-which"
},
"engines": {
"node": ">= 8"
}
},
"node_modules/workerpool": {
"version": "6.2.1",
"resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.2.1.tgz",
"integrity": "sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw==",
"version": "6.2.0",
"resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.2.0.tgz",
"integrity": "sha512-Rsk5qQHJ9eowMH28Jwhe8HEbmdYDX4lwoMWshiCXugjtHqMD9ZbiqSDLxcsfdqsETPzVUtX5s1Z5kStiIM6l4A==",
"dev": true
},
"node_modules/wrap-ansi": {
@ -2325,6 +2346,12 @@
"integrity": "sha512-LB2R1Oyhpg8gu4SON/mfforE525+Hi/M1ineICEDftqNVTyFg1aRIeGuTvXAoWHc4nbrFncWtJgMmoyRvuGh7A==",
"dev": true
},
"@ungap/promise-all-settled": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz",
"integrity": "sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q==",
"dev": true
},
"@zbigg/treesync": {
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/@zbigg/treesync/-/treesync-0.3.0.tgz",
@ -2587,20 +2614,12 @@
}
},
"debug": {
"version": "4.3.4",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
"integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
"version": "4.3.3",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz",
"integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==",
"dev": true,
"requires": {
"ms": "2.1.2"
},
"dependencies": {
"ms": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
"dev": true
}
}
},
"decamelize": {
@ -2779,6 +2798,12 @@
"integrity": "sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ==",
"dev": true
},
"growl": {
"version": "1.10.5",
"resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz",
"integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==",
"dev": true
},
"has-flag": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
@ -2907,6 +2932,12 @@
"integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=",
"dev": true
},
"isexe": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
"integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
"dev": true
},
"json-parse-better-errors": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz",
@ -3138,29 +3169,32 @@
}
},
"mocha": {
"version": "10.2.0",
"resolved": "https://registry.npmjs.org/mocha/-/mocha-10.2.0.tgz",
"integrity": "sha512-IDY7fl/BecMwFHzoqF2sg/SHHANeBoMMXFlS9r0OXKDssYE1M5O43wUY/9BVPeIvfH2zmEbBfseqN9gBQZzXkg==",
"version": "9.2.2",
"resolved": "https://registry.npmjs.org/mocha/-/mocha-9.2.2.tgz",
"integrity": "sha512-L6XC3EdwT6YrIk0yXpavvLkn8h+EU+Y5UcCHKECyMbdUIxyMuZj4bX4U9e1nvnvUUvQVsV2VHQr5zLdcUkhW/g==",
"dev": true,
"requires": {
"@ungap/promise-all-settled": "1.1.2",
"ansi-colors": "4.1.1",
"browser-stdout": "1.3.1",
"chokidar": "3.5.3",
"debug": "4.3.4",
"debug": "4.3.3",
"diff": "5.0.0",
"escape-string-regexp": "4.0.0",
"find-up": "5.0.0",
"glob": "7.2.0",
"growl": "1.10.5",
"he": "1.2.0",
"js-yaml": "4.1.0",
"log-symbols": "4.1.0",
"minimatch": "5.0.1",
"minimatch": "4.2.1",
"ms": "2.1.3",
"nanoid": "3.3.3",
"nanoid": "3.3.1",
"serialize-javascript": "6.0.0",
"strip-json-comments": "3.1.1",
"supports-color": "8.1.1",
"workerpool": "6.2.1",
"which": "2.0.2",
"workerpool": "6.2.0",
"yargs": "16.2.0",
"yargs-parser": "20.2.4",
"yargs-unparser": "2.0.0"
@ -3271,23 +3305,12 @@
}
},
"minimatch": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.0.1.tgz",
"integrity": "sha512-nLDxIFRyhDblz3qMuq+SoRZED4+miJ/G+tdDrjkkkRnjAsBexeGpgjLEQ0blJy7rHhR2b93rhQY4SvyWu9v03g==",
"version": "4.2.1",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-4.2.1.tgz",
"integrity": "sha512-9Uq1ChtSZO+Mxa/CL1eGizn2vRn3MlLgzhT0Iz8zaY8NdvxvB0d5QdPFmCKf7JKA9Lerx5vRrnwO03jsSfGG9g==",
"dev": true,
"requires": {
"brace-expansion": "^2.0.1"
},
"dependencies": {
"brace-expansion": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
"integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
"dev": true,
"requires": {
"balanced-match": "^1.0.0"
}
}
"brace-expansion": "^1.1.7"
}
},
"ms": {
@ -3377,17 +3400,6 @@
"loglevel": "^1.4.1",
"meow": "^5.0.0",
"nanobus": "^4.2.0"
},
"dependencies": {
"debug": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
"integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
"dev": true,
"requires": {
"ms": "^2.1.1"
}
}
}
},
"mocha-webdriver-runner": {
@ -3417,9 +3429,9 @@
"dev": true
},
"ms": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz",
"integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==",
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
"dev": true
},
"nanoassert": {
@ -3440,9 +3452,9 @@
}
},
"nanoid": {
"version": "3.3.3",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.3.tgz",
"integrity": "sha512-p1sjXuopFs0xg+fPASzQ28agW1oHD7xDsd9Xkf3T15H3c/cifrFHVwrh74PdoklAPi+i7MdRsE47vm2r6JoB+w==",
"version": "3.3.1",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.1.tgz",
"integrity": "sha512-n6Vs/3KGyxPQd6uO0eH4Bv0ojGSUvuLlIHtC3Y0kEO23YRge8H9x1GCzLn28YX0H66pMkxuaeESFq4tKISKwdw==",
"dev": true
},
"nanoscheduler": {
@ -4040,10 +4052,19 @@
"spdx-expression-parse": "^3.0.0"
}
},
"which": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
"integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
"dev": true,
"requires": {
"isexe": "^2.0.0"
}
},
"workerpool": {
"version": "6.2.1",
"resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.2.1.tgz",
"integrity": "sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw==",
"version": "6.2.0",
"resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.2.0.tgz",
"integrity": "sha512-Rsk5qQHJ9eowMH28Jwhe8HEbmdYDX4lwoMWshiCXugjtHqMD9ZbiqSDLxcsfdqsETPzVUtX5s1Z5kStiIM6l4A==",
"dev": true
},
"wrap-ansi": {

View File

@ -39,7 +39,7 @@
"chai": "^4.3.7",
"chai-dom": "^1.11.0",
"fs-extra": "^9.1.0",
"mocha": "^10.2.0",
"mocha": "^9.2.2",
"mocha-chrome": "^2.2.0",
"mocha-webdriver-runner": "^0.6.4",
"mock-socket": "^9.2.1",

View File

@ -25,28 +25,28 @@
if (delayElt) {
const delayInMilliseconds =
delayElt.getAttribute('data-loading-delay') || 200
const timeout = setTimeout(() => {
const timeout = setTimeout(function () {
doCallback()
loadingStatesUndoQueue.push(() => {
mayProcessUndoCallback(targetElt, () => undoCallback())
loadingStatesUndoQueue.push(function () {
mayProcessUndoCallback(targetElt, undoCallback)
})
}, delayInMilliseconds)
loadingStatesUndoQueue.push(() => {
mayProcessUndoCallback(targetElt, () => clearTimeout(timeout))
loadingStatesUndoQueue.push(function () {
mayProcessUndoCallback(targetElt, function () { clearTimeout(timeout) })
})
} else {
doCallback()
loadingStatesUndoQueue.push(() => {
mayProcessUndoCallback(targetElt, () => undoCallback())
loadingStatesUndoQueue.push(function () {
mayProcessUndoCallback(targetElt, undoCallback)
})
}
}
function getLoadingStateElts(loadingScope, type, path) {
return Array.from(htmx.findAll(loadingScope, `[${type}]`)).filter(
(elt) => mayProcessLoadingStateByPath(elt, path)
return Array.from(htmx.findAll(loadingScope, "[" + type + "]")).filter(
function (elt) { return mayProcessLoadingStateByPath(elt, path) }
)
}
@ -74,7 +74,7 @@
let loadingStateEltsByType = {}
loadingStateTypes.forEach((type) => {
loadingStateTypes.forEach(function (type) {
loadingStateEltsByType[type] = getLoadingStateElts(
container,
type,
@ -82,87 +82,91 @@
)
})
loadingStateEltsByType['data-loading'].forEach((sourceElt) => {
getLoadingTarget(sourceElt).forEach((targetElt) => {
loadingStateEltsByType['data-loading'].forEach(function (sourceElt) {
getLoadingTarget(sourceElt).forEach(function (targetElt) {
queueLoadingState(
sourceElt,
targetElt,
() =>
(targetElt.style.display =
function () {
targetElt.style.display =
sourceElt.getAttribute('data-loading') ||
'inline-block'),
() => (targetElt.style.display = 'none')
'inline-block' },
function () { targetElt.style.display = 'none' }
)
})
})
loadingStateEltsByType['data-loading-class'].forEach(
(sourceElt) => {
function (sourceElt) {
const classNames = sourceElt
.getAttribute('data-loading-class')
.split(' ')
getLoadingTarget(sourceElt).forEach((targetElt) => {
getLoadingTarget(sourceElt).forEach(function (targetElt) {
queueLoadingState(
sourceElt,
targetElt,
() =>
classNames.forEach((className) =>
targetElt.classList.add(className)
),
() =>
classNames.forEach((className) =>
targetElt.classList.remove(className)
)
function () {
classNames.forEach(function (className) {
targetElt.classList.add(className)
})
},
function() {
classNames.forEach(function (className) {
targetElt.classList.remove(className)
})
}
)
})
}
)
loadingStateEltsByType['data-loading-class-remove'].forEach(
(sourceElt) => {
function (sourceElt) {
const classNames = sourceElt
.getAttribute('data-loading-class-remove')
.split(' ')
getLoadingTarget(sourceElt).forEach((targetElt) => {
getLoadingTarget(sourceElt).forEach(function (targetElt) {
queueLoadingState(
sourceElt,
targetElt,
() =>
classNames.forEach((className) =>
targetElt.classList.remove(className)
),
() =>
classNames.forEach((className) =>
targetElt.classList.add(className)
)
function () {
classNames.forEach(function (className) {
targetElt.classList.remove(className)
})
},
function() {
classNames.forEach(function (className) {
targetElt.classList.add(className)
})
}
)
})
}
)
loadingStateEltsByType['data-loading-disable'].forEach(
(sourceElt) => {
getLoadingTarget(sourceElt).forEach((targetElt) => {
function (sourceElt) {
getLoadingTarget(sourceElt).forEach(function (targetElt) {
queueLoadingState(
sourceElt,
targetElt,
() => (targetElt.disabled = true),
() => (targetElt.disabled = false)
function() { targetElt.disabled = true },
function() { targetElt.disabled = false }
)
})
}
)
loadingStateEltsByType['data-loading-aria-busy'].forEach(
(sourceElt) => {
getLoadingTarget(sourceElt).forEach((targetElt) => {
function (sourceElt) {
getLoadingTarget(sourceElt).forEach(function (targetElt) {
queueLoadingState(
sourceElt,
targetElt,
() => (targetElt.setAttribute("aria-busy", "true")),
() => (targetElt.removeAttribute("aria-busy"))
function () { targetElt.setAttribute("aria-busy", "true") },
function () { targetElt.removeAttribute("aria-busy") }
)
})
}

View File

@ -5,7 +5,8 @@ htmx.defineExtension('morphdom-swap', {
handleSwap: function (swapStyle, target, fragment) {
if (swapStyle === 'morphdom') {
if (fragment.nodeType === Node.DOCUMENT_FRAGMENT_NODE) {
morphdom(target, fragment.firstElementChild);
// IE11 doesn't support DocumentFragment.firstElementChild
morphdom(target, fragment.firstElementChild || fragment.firstChild);
return [target];
} else {
morphdom(target, fragment.outerHTML);

View File

@ -573,9 +573,17 @@ return (function () {
}
}
function startsWith(str, prefix) {
return str.substring(0, prefix.length) === prefix
}
function endsWith(str, suffix) {
return str.substring(str.length - suffix.length) === suffix
}
function normalizeSelector(selector) {
var trimmedSelector = selector.trim();
if (trimmedSelector.startsWith("<") && trimmedSelector.endsWith("/>")) {
if (startsWith(trimmedSelector, "<") && endsWith(trimmedSelector, "/>")) {
return trimmedSelector.substring(1, trimmedSelector.length - 2);
} else {
return trimmedSelector;
@ -1864,12 +1872,25 @@ return (function () {
}
function findHxOnWildcardElements(elt) {
if (!document.evaluate) return []
var node = null
var elements = []
if (document.evaluate) {
var iter = document.evaluate('//*[@*[ starts-with(name(), "hx-on:") or starts-with(name(), "data-hx-on:") ]]', elt)
while (node = iter.iterateNext()) elements.push(node)
} else {
var allElements = document.getElementsByTagName("*")
for (var i = 0; i < allElements.length; i++) {
var attributes = allElements[i].attributes
for (var j = 0; j < attributes.length; j++) {
var attrName = attributes[j].name
if (startsWith(attrName, "hx-on:") || startsWith(attrName, "data-hx-on:")) {
elements.push(allElements[i])
}
}
}
}
let node = null
const elements = []
const iter = document.evaluate('//*[@*[ starts-with(name(), "hx-on:") or starts-with(name(), "data-hx-on:") ]]', elt)
while (node = iter.iterateNext()) elements.push(node)
return elements
}
@ -1975,10 +1996,10 @@ return (function () {
for (var i = 0; i < elt.attributes.length; i++) {
var name = elt.attributes[i].name
var value = elt.attributes[i].value
if (name.startsWith("hx-on:") || name.startsWith("data-hx-on:")) {
if (startsWith(name, "hx-on:") || startsWith(name, "data-hx-on:")) {
let eventName = name.slice(name.indexOf(":") + 1)
// if the eventName starts with a colon, prepend "htmx" for shorthand support
if (eventName.startsWith(":")) eventName = "htmx" + eventName
if (startsWith(eventName, ":")) eventName = "htmx" + eventName
addHxOnEventHandler(elt, eventName, value)
}
@ -2207,7 +2228,13 @@ return (function () {
// so we can prevent privileged data entering the cache.
// The page will still be reachable as a history entry, but htmx will fetch it
// live from the server onpopstate rather than look in the localStorage cache
var disableHistoryCache = getDocument().querySelector('[hx-history="false" i],[data-hx-history="false" i]');
var disableHistoryCache
try {
disableHistoryCache = getDocument().querySelector('[hx-history="false" i],[data-hx-history="false" i]')
} catch (e) {
// IE11: insensitive modifier not supported so fallback to case sensitive selector
disableHistoryCache = getDocument().querySelector('[hx-history="false"],[data-hx-history="false"]')
}
if (!disableHistoryCache) {
triggerEvent(getDocument().body, "htmx:beforeHistorySave", {path: path, historyElt: elt});
saveToHistoryCache(path, cleanInnerHtmlForHistory(elt), getDocument().title, window.scrollY);
@ -2220,7 +2247,7 @@ return (function () {
// remove the cache buster parameter, if any
if (htmx.config.getCacheBusterParam) {
path = path.replace(/org\.htmx\.cache-buster=[^&]*&?/, '')
if (path.endsWith('&') || path.endsWith("?")) {
if (endsWith(path, '&') || endsWith(path, "?")) {
path = path.slice(0, -1);
}
}
@ -2856,9 +2883,18 @@ return (function () {
}
function verifyPath(elt, path, requestConfig) {
var url = new URL(path, document.location.href);
var origin = document.location.origin;
var sameHost = origin === url.origin;
var sameHost
var url
if (typeof URL === "function") {
url = new URL(path, document.location.href);
var origin = document.location.origin;
sameHost = origin === url.origin;
} else {
// IE11 doesn't support URL
url = path
sameHost = startsWith(path, document.location.origin)
}
if (htmx.config.selfRequestsOnly) {
if (!sameHost) {
return false;

View File

@ -18,12 +18,13 @@ describe("hx-disinherit attribute", function() {
var btn = byId("bx1");
btn.click();
this.server.respond();
btn.innerHTML.should.equal(response_inner);
btn.firstChild.id.should.equal("snowflake");
btn.innerText.should.equal("Hello world");
})
it('disinherit exclude single attribute', function () {
var response_inner = '<div id="snowflake" class="">Hello world</div>'
var response_inner = '<div id="snowflake">Hello world</div>'
var response = '<div id="unique">' + response_inner + '</div>'
this.server.respondWith("GET", "/test", response);
@ -31,7 +32,9 @@ describe("hx-disinherit attribute", function() {
var btn = byId("bx1");
btn.click();
this.server.respond();
btn.innerHTML.should.equal(response + '<span id="cta" class="">Click Me!</span>');
btn.firstChild.id.should.equal("unique")
btn.firstChild.firstChild.id.should.equal("snowflake")
btn.childNodes[1].innerText.should.equal("Click Me!")
});
it('disinherit exclude multiple attributes', function () {
@ -47,7 +50,9 @@ describe("hx-disinherit attribute", function() {
this.server.respond();
console.log(btn.innerHTML);
console.log(response);
btn.innerHTML.should.equal('<span id="cta" class="">' + response + '</span>');
btn.firstChild.id.should.equal("cta")
btn.firstChild.firstChild.id.should.equal("unique")
btn.firstChild.firstChild.firstChild.id.should.equal("snowflake")
});
it('disinherit exclude all attributes', function () {
@ -62,7 +67,8 @@ describe("hx-disinherit attribute", function() {
var btn = byId("bx1");
btn.click();
this.server.respond();
btn.innerHTML.should.equal(response);
btn.firstChild.id.should.equal("unique");
btn.firstChild.firstChild.id.should.equal("snowflake");
});
it('same-element inheritance disable', function () {
@ -73,7 +79,8 @@ describe("hx-disinherit attribute", function() {
var btn = make('<button hx-select="#snowflake" hx-target="#container" hx-trigger="click" hx-get="/test" hx-swap="outerHTML" hx-disinherit="*"><div id="container"></div></button>')
btn.click();
this.server.respond();
btn.innerHTML.should.equal(response_inner);
btn.firstChild.id.should.equal("snowflake");
btn.firstChild.innerText.should.equal("Hello world");
});
it('same-element inheritance disable with child nodes', function () {
@ -86,7 +93,8 @@ describe("hx-disinherit attribute", function() {
var btn = byId("bx1");
btn.click();
this.server.respond();
btn.innerHTML.should.equal('<div id="target" class="">unique-snowflake</div>');
btn.firstChild.id.should.equal('target');
btn.firstChild.innerText.should.equal('unique-snowflake');
var count = (div.parentElement.innerHTML.match(/snowflake/g) || []).length;
count.should.equal(2); // hx-select of parent div and newly loaded inner content
});

View File

@ -841,7 +841,9 @@ describe("Core htmx AJAX Tests", function(){
var btn = make('<button hx-get="/test">Click Me!</button>')
btn.click();
this.server.respond();
btn.innerText.should.equal("Clicked!");
if (supportsSvgTitles()) { // IE 11
btn.innerText.should.equal("Clicked!");
}
window.document.title.should.equal(originalTitle);
});
@ -855,7 +857,9 @@ describe("Core htmx AJAX Tests", function(){
var btn = make('<button hx-get="/test">Click Me!</button>')
btn.click();
this.server.respond();
btn.innerText.should.equal("Clicked!");
if (supportsSvgTitles()) { // IE 11
btn.innerText.should.equal("Clicked!");
}
window.document.title.should.equal(newTitle);
});
@ -1058,6 +1062,11 @@ describe("Core htmx AJAX Tests", function(){
})
it('properly handles clicked submit button with a value outside a htmx form', function () {
if (!supportsFormAttribute()) {
this._runnable.title += " - Skipped as IE11 doesn't support form attribute"
this.skip()
return
}
var values;
this.server.respondWith("Post", "/test", function (xhr) {
values = getParameters(xhr);
@ -1075,6 +1084,11 @@ describe("Core htmx AJAX Tests", function(){
})
it('properly handles clicked submit input with a value outside a htmx form', function () {
if (!supportsFormAttribute()) {
this._runnable.title += " - Skipped as IE11 doesn't support form attribute"
this.skip()
return
}
var values;
this.server.respondWith("Post", "/test", function (xhr) {
values = getParameters(xhr);
@ -1146,6 +1160,11 @@ describe("Core htmx AJAX Tests", function(){
})
it('properly handles clicked submit button with a value inside a form, referencing another form', function () {
if (!supportsFormAttribute()) {
this._runnable.title += " - Skipped as IE11 doesn't support form attribute"
this.skip()
return
}
var values;
this.server.respondWith("Post", "/test", function (xhr) {
values = getParameters(xhr);
@ -1166,6 +1185,11 @@ describe("Core htmx AJAX Tests", function(){
})
it('properly handles clicked submit input with a value inside a form, referencing another form', function () {
if (!supportsFormAttribute()) {
this._runnable.title += " - Skipped as IE11 doesn't support form attribute"
this.skip()
return
}
var values;
this.server.respondWith("Post", "/test", function (xhr) {
values = getParameters(xhr);

View File

@ -116,8 +116,14 @@ describe("Core htmx Events", function() {
});
it("htmx:configRequest on form gives access to submit event", function () {
var skip = false
var submitterId;
var handler = htmx.on("htmx:configRequest", function (evt) {
// submitter may be null, but undefined means the browser doesn't support it
if (typeof evt.detail.triggeringEvent.submitter === "undefined") {
skip = true
return
}
evt.detail.headers['X-Submitter-Id'] = evt.detail.triggeringEvent.submitter.id;
});
try {
@ -129,6 +135,10 @@ describe("Core htmx Events", function() {
var btn = byId('b1');
btn.click();
this.server.respond();
if (skip) {
this._runnable.title += " - Skipped as IE11 doesn't support submitter"
this.skip()
}
should.equal(submitterId, "b1")
} finally {
htmx.off("htmx:configRequest", handler);
@ -379,6 +389,12 @@ describe("Core htmx Events", function() {
});
it("htmx:sendError is called after a failed request", function (done) {
if (IsIE11()) {
// IE will throw an exception on xhr.open with the URL below, xhr.send won't even be called
this._runnable.title += " - Skipped on IE11 as xhr.send won't even be called with a file URL"
this.skip()
return
}
var called = false;
var handler = htmx.on("htmx:sendError", function (evt) {
called = true;

View File

@ -22,7 +22,11 @@ describe("Core htmx internals Tests", function() {
})
it("makeFragment works with template wrapping", function(){
htmx.config.useTemplateFragments = true;
if (!supportsTemplates()) {
this._runnable.title += " - Skipped as IE11 doesn't support templates"
this.skip()
return
}
try {
htmx._("makeFragment")("<html></html>").children.length.should.equal(0);
htmx._("makeFragment")("<html><body></body></html>").children.length.should.equal(0);
@ -46,6 +50,11 @@ describe("Core htmx internals Tests", function() {
it("makeFragment works with template wrapping and funky combos", function(){
if (!supportsTemplates()) {
this._runnable.title += " - Skipped as IE11 doesn't support templates"
this.skip()
return
}
htmx.config.useTemplateFragments = true;
try {
var fragment = htmx._("makeFragment")("<td></td><div></div>");

View File

@ -49,10 +49,14 @@ describe("Core htmx perf Tests", function() {
})
it("history snapshot cleaning should be fast", function(){
//
var size = 5 * 1024 // ~350K in size, about the size of CNN's body tag :p
if (IsIE11()) {
// So slow in IE11 it freezes the browser and blocks other tests, pretty annoying
size = 5 * 100 // Seriously this already takes ~1.5 SECOND to run, more simply makes it crash
}
var workArea = getWorkArea();
var html = "<div class='foo bar'>Yay, really large HTML documents are fun!</div>\n";
html = stringRepeat(html, 5 * 1024); // ~350K in size, about the size of CNN's body tag :p
html = stringRepeat(html, size);
workArea.insertAdjacentHTML("beforeend", html)
var start = performance.now();
htmx._("cleanInnerHtmlForHistory")(workArea);

View File

@ -129,6 +129,12 @@ describe("Core htmx Regression Tests", function(){
})
it('a form can reset based on the htmx:afterRequest event', function() {
if (IsIE11()) {
this._runnable.title += " - Skipped as hyperscript isn't IE11 compatible"
this.skip()
return
}
this.server.respondWith("POST", "/test", "posted");
var form = make('<div id="d1"></div><form _="on htmx:afterRequest reset() me" hx-post="/test" hx-target="#d1">' +

View File

@ -110,6 +110,12 @@ describe("Core htmx client side validation tests", function(){
it('hyperscript validation error prevents request', function()
{
if (IsIE11()) {
this._runnable.title += " - Skipped as hyperscript isn't IE11 compatible"
this.skip()
return
}
this.server.respondWith("POST", "/test", "Clicked!");
var form = make('<form hx-post="/test" hx-trigger="click">' +

View File

@ -2,6 +2,11 @@ describe("hyperscript integration", function() {
beforeEach(function () {
this.server = makeServer();
clearWorkArea();
if (IsIE11()) {
this.title += " - Skipped as hyperscript isn't IE11 compatible"
this.skip()
}
});
afterEach(function () {
this.server.restore();

View File

@ -1,7 +1,84 @@
describe("web-sockets extension", function () {
// mock-socket isn't IE11 compatible, thus this handmade one
// Using the same syntax as the library, so the initial tests didn't require changes to work
// TODO when we get rid of IE11 for htmx2, replace this by mock-socket since it ofc doesn't implement every feature
function mockWebsocket() {
var mockSocketClient = {
addEventListener: function (event, handler) {
var handlers = this._listeners[event] || []
handlers.push(handler)
this._listeners[event] = handlers
},
on: function (event, handler) {
this.addEventListener(event, handler)
},
send: function (data) {
mockSocketServer._fireEvent("message", data)
},
connect: function () {
this._open = true
mockSocketServer._fireEvent("connection", mockSocketServer)
setTimeout(function () {
this._fireEvent("open", {type: "open"})
}.bind(this), 2)
},
close: function () {
if (this._open) {
this._open = false
this._fireEvent("close", {type: "close", code: 0})
}
},
_listeners: {},
_fireEvent: function (event, data) {
var handlers = this._listeners[event] || []
if (typeof this["on" + event] === "function") {
handlers.push(this["on" + event])
}
for (var i = 0; i < handlers.length; i++) {
handlers[i](data)
}
},
_open: false,
}
var mockSocketServer = {
addEventListener: function (event, handler) {
var handlers = this._listeners[event] || []
handlers.push(handler)
this._listeners[event] = handlers
},
on: function (event, handler) {
this.addEventListener(event, handler)
},
close: function () {
mockSocketClient.close()
},
stop: function () {
},
emit: function (event, data) {
mockSocketClient._fireEvent(event, {data: data})
},
clients: function () { // Replicate old mock-socket syntax to avoid huge file diff to merge
return mockSocketClient._open ? [1] : []
},
_listeners: {},
_fireEvent: function (event, data) {
var handlers = this._listeners[event] || []
for (var i = 0; i < handlers.length; i++) {
handlers[i](data)
}
},
}
return {
client: mockSocketClient,
server: mockSocketServer,
}
}
beforeEach(function () {
this.server = makeServer();
this.socketServer = new Mock.Server('ws://localhost:8080');
// this.socketServer = new Mock.Server('ws://localhost:8080');
var mockedSocket = mockWebsocket();
this.socketServer = mockedSocket.server;
this.messages = [];
this.clock = sinon.useFakeTimers();
@ -19,12 +96,19 @@ describe("web-sockets extension", function () {
}
clearWorkArea();
this.oldCreateWebSocket = htmx.createWebSocket;
htmx.createWebSocket = function () {
mockedSocket.client.connect()
return mockedSocket.client
};
});
afterEach(function () {
this.server.restore();
clearWorkArea();
this.socketServer.close();
this.socketServer.stop();
this.clock.restore();
htmx.createWebSocket = this.oldCreateWebSocket;
});
it('can establish connection with the server', function () {
@ -454,6 +538,12 @@ describe("web-sockets extension", function () {
})
it('sends data to the server with external non-htmx form + submit button & value', function () {
if (!supportsFormAttribute()) {
this._runnable.title += " - Skipped as IE11 doesn't support form attribute"
this.skip()
return
}
make('<div hx-ext="ws" ws-connect="ws://localhost:8080">' +
'<form ws-send id="form">' +
'<input type="hidden" name="foo" value="bar">' +
@ -481,6 +571,12 @@ describe("web-sockets extension", function () {
})
it('sends data to the server with external non-htmx form + submit input & value', function () {
if (!supportsFormAttribute()) {
this._runnable.title += " - Skipped as IE11 doesn't support form attribute"
this.skip()
return
}
make('<div hx-ext="ws" ws-connect="ws://localhost:8080">' +
'<form ws-send id="form">' +
'<input type="hidden" name="foo" value="bar">' +

View File

@ -160,6 +160,7 @@
<div id="mocha"></div>
<script class="mocha-exec">
mocha.setup({globals: ['$0', '$1', '$2', '$3', '$4', 'performance', 'requestAnimationFrame', 'cancelAnimationFrame']}); <!-- IE11 -->
mocha.run();
</script>
<em>Work Area</em>

View File

@ -5,6 +5,7 @@ function byId(id) {
}
function make(htmlStr) {
htmlStr = htmlStr.trim()
var makeFn = function () {
var range = document.createRange();
var fragment = range.createContextualFragment(htmlStr);
@ -38,7 +39,7 @@ function getWorkArea() {
}
function clearWorkArea() {
const workArea = getWorkArea();
var workArea = getWorkArea();
if (workArea) workArea.innerHTML = "";
}
@ -107,3 +108,33 @@ function log(val) {
console.log(val);
return val;
}
// region IE11
function supportsTemplates() {
return typeof document.createElement("template").content !== "undefined"
}
function supportsSvgTitles() {
// Need to append the element to the body, otherwise innerText will add the svg title to the returned value...
var tempButton = document.createElement("button")
tempButton.innerHTML = '<svg><title>Svg title</title></svg>Text';
document.body.appendChild(tempButton)
var titleOk = tempButton.innerText === "Text"
document.body.removeChild(tempButton)
return titleOk
}
function supportsFormAttribute() {
var parser = new DOMParser()
return !!parser.parseFromString('<button form="form"></button><form id="form"></form>', "text/html").body.firstChild.form
}
function supportsXPath() {
return typeof document.evaluate !== "undefined"
}
function IsIE11() {
return !supportsTemplates() && !supportsSvgTitles() && !supportsFormAttribute() && !supportsXPath()
}
// endregion