diff --git a/.prettierrc b/.prettierrc index de3c347..94d339e 100644 --- a/.prettierrc +++ b/.prettierrc @@ -1,6 +1,5 @@ { "printWidth": 140, - "parser": "typescript", "semi": true, "tabWidth": 2, "useTabs": false, diff --git a/.travis.yml b/.travis.yml index cff5a1d..9ac9f06 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,7 +2,7 @@ language: node_js dist: trusty sudo: required node_js: -- '8' +- '12' before_install: - npm install -g @angular/cli @angular/compiler-cli typescript install: npm install diff --git a/package-lock.json b/package-lock.json index 09fbfec..968083b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -536,6 +536,453 @@ "path-exists": "^4.0.0" } }, + "fsevents": { + "version": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.9.tgz", + "integrity": "sha512-oeyj2H3EjjonWcFjD5NvZNE9Rqe4UW+nQBU2HNeKw0koVLEFIhtyETyAakeAM3de7Z/SW5kcA+fZUait9EApnw==", + "requires": { + "nan": "^2.12.1", + "node-pre-gyp": "^0.12.0" + }, + "dependencies": { + "abbrev": { + "version": "1.1.1", + "resolved": false, + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==" + }, + "ansi-regex": { + "version": "2.1.1", + "resolved": false, + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" + }, + "aproba": { + "version": "1.2.0", + "resolved": false, + "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==" + }, + "are-we-there-yet": { + "version": "1.1.5", + "resolved": false, + "integrity": "sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==", + "requires": { + "delegates": "^1.0.0", + "readable-stream": "^2.0.6" + } + }, + "balanced-match": { + "version": "1.0.0", + "resolved": false, + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": false, + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "chownr": { + "version": "1.1.1", + "resolved": false, + "integrity": "sha512-j38EvO5+LHX84jlo6h4UzmOwi0UgW61WRyPtJz4qaadK5eY3BTS5TY/S1Stc3Uk2lIM6TPevAlULiEJwie860g==" + }, + "concat-map": { + "version": "0.0.1", + "resolved": false, + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" + }, + "console-control-strings": { + "version": "1.1.0", + "resolved": false, + "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=" + }, + "core-util-is": { + "version": "1.0.2", + "resolved": false, + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" + }, + "debug": { + "version": "4.1.1", + "resolved": false, + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "requires": { + "ms": "^2.1.1" + } + }, + "deep-extend": { + "version": "0.6.0", + "resolved": false, + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==" + }, + "delegates": { + "version": "1.0.0", + "resolved": false, + "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=" + }, + "detect-libc": { + "version": "1.0.3", + "resolved": false, + "integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=" + }, + "fs-minipass": { + "version": "1.2.5", + "resolved": false, + "integrity": "sha512-JhBl0skXjUPCFH7x6x61gQxrKyXsxB5gcgePLZCwfyCGGsTISMoIeObbrvVeP6Xmyaudw4TT43qV2Gz+iyd2oQ==", + "requires": { + "minipass": "^2.2.1" + } + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": false, + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" + }, + "gauge": { + "version": "2.7.4", + "resolved": false, + "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=", + "requires": { + "aproba": "^1.0.3", + "console-control-strings": "^1.0.0", + "has-unicode": "^2.0.0", + "object-assign": "^4.1.0", + "signal-exit": "^3.0.0", + "strip-ansi": "^3.0.1", + "wide-align": "^1.1.0" + } + }, + "glob": { + "version": "7.1.3", + "resolved": false, + "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==", + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "has-unicode": { + "version": "2.0.1", + "resolved": false, + "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=" + }, + "iconv-lite": { + "version": "0.4.24", + "resolved": false, + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, + "ignore-walk": { + "version": "3.0.1", + "resolved": false, + "integrity": "sha512-DTVlMx3IYPe0/JJcYP7Gxg7ttZZu3IInhuEhbchuqneY9wWe5Ojy2mXLBaQFUQmo0AW2r3qG7m1mg86js+gnlQ==", + "requires": { + "minimatch": "^3.0.4" + } + }, + "inflight": { + "version": "1.0.6", + "resolved": false, + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.3", + "resolved": false, + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" + }, + "ini": { + "version": "1.3.5", + "resolved": false, + "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==" + }, + "isarray": { + "version": "1.0.0", + "resolved": false, + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + }, + "minimatch": { + "version": "3.0.4", + "resolved": false, + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "0.0.8", + "resolved": false, + "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=" + }, + "minipass": { + "version": "2.3.5", + "resolved": false, + "integrity": "sha512-Gi1W4k059gyRbyVUZQ4mEqLm0YIUiGYfvxhF6SIlk3ui1WVxMTGfGdQ2SInh3PDrRTVvPKgULkpJtT4RH10+VA==", + "requires": { + "safe-buffer": "^5.1.2", + "yallist": "^3.0.0" + } + }, + "minizlib": { + "version": "1.2.1", + "resolved": false, + "integrity": "sha512-7+4oTUOWKg7AuL3vloEWekXY2/D20cevzsrNT2kGWm+39J9hGTCBv8VI5Pm5lXZ/o3/mdR4f8rflAPhnQb8mPA==", + "requires": { + "minipass": "^2.2.1" + } + }, + "mkdirp": { + "version": "0.5.1", + "resolved": false, + "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", + "requires": { + "minimist": "0.0.8" + } + }, + "ms": { + "version": "2.1.1", + "resolved": false, + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==" + }, + "needle": { + "version": "2.3.0", + "resolved": false, + "integrity": "sha512-QBZu7aAFR0522EyaXZM0FZ9GLpq6lvQ3uq8gteiDUp7wKdy0lSd2hPlgFwVuW1CBkfEs9PfDQsQzZghLs/psdg==", + "requires": { + "debug": "^4.1.0", + "iconv-lite": "^0.4.4", + "sax": "^1.2.4" + } + }, + "node-pre-gyp": { + "version": "0.12.0", + "resolved": false, + "integrity": "sha512-4KghwV8vH5k+g2ylT+sLTjy5wmUOb9vPhnM8NHvRf9dHmnW/CndrFXy2aRPaPST6dugXSdHXfeaHQm77PIz/1A==", + "requires": { + "detect-libc": "^1.0.2", + "mkdirp": "^0.5.1", + "needle": "^2.2.1", + "nopt": "^4.0.1", + "npm-packlist": "^1.1.6", + "npmlog": "^4.0.2", + "rc": "^1.2.7", + "rimraf": "^2.6.1", + "semver": "^5.3.0", + "tar": "^4" + } + }, + "nopt": { + "version": "4.0.1", + "resolved": false, + "integrity": "sha1-0NRoWv1UFRk8jHUFYC0NF81kR00=", + "requires": { + "abbrev": "1", + "osenv": "^0.1.4" + } + }, + "npm-bundled": { + "version": "1.0.6", + "resolved": false, + "integrity": "sha512-8/JCaftHwbd//k6y2rEWp6k1wxVfpFzB6t1p825+cUb7Ym2XQfhwIC5KwhrvzZRJu+LtDE585zVaS32+CGtf0g==" + }, + "npm-packlist": { + "version": "1.4.1", + "resolved": false, + "integrity": "sha512-+TcdO7HJJ8peiiYhvPxsEDhF3PJFGUGRcFsGve3vxvxdcpO2Z4Z7rkosRM0kWj6LfbK/P0gu3dzk5RU1ffvFcw==", + "requires": { + "ignore-walk": "^3.0.1", + "npm-bundled": "^1.0.1" + } + }, + "npmlog": { + "version": "4.1.2", + "resolved": false, + "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==", + "requires": { + "are-we-there-yet": "~1.1.2", + "console-control-strings": "~1.1.0", + "gauge": "~2.7.3", + "set-blocking": "~2.0.0" + } + }, + "object-assign": { + "version": "4.1.1", + "resolved": false, + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" + }, + "once": { + "version": "1.4.0", + "resolved": false, + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "requires": { + "wrappy": "1" + } + }, + "os-homedir": { + "version": "1.0.2", + "resolved": false, + "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=" + }, + "os-tmpdir": { + "version": "1.0.2", + "resolved": false, + "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=" + }, + "osenv": { + "version": "0.1.5", + "resolved": false, + "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==", + "requires": { + "os-homedir": "^1.0.0", + "os-tmpdir": "^1.0.0" + } + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": false, + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" + }, + "process-nextick-args": { + "version": "2.0.0", + "resolved": false, + "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==" + }, + "rc": { + "version": "1.2.8", + "resolved": false, + "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", + "requires": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + }, + "dependencies": { + "minimist": { + "version": "1.2.0", + "resolved": false, + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=" + } + } + }, + "readable-stream": { + "version": "2.3.6", + "resolved": false, + "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "rimraf": { + "version": "2.6.3", + "resolved": false, + "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", + "requires": { + "glob": "^7.1.3" + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": false, + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "safer-buffer": { + "version": "2.1.2", + "resolved": false, + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "sax": { + "version": "1.2.4", + "resolved": false, + "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==" + }, + "semver": { + "version": "5.7.0", + "resolved": false, + "integrity": "sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==" + }, + "set-blocking": { + "version": "2.0.0", + "resolved": false, + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=" + }, + "signal-exit": { + "version": "3.0.2", + "resolved": false, + "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=" + }, + "string_decoder": { + "version": "1.1.1", + "resolved": false, + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "requires": { + "safe-buffer": "~5.1.0" + } + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": false, + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "strip-json-comments": { + "version": "2.0.1", + "resolved": false, + "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=" + }, + "tar": { + "version": "4.4.8", + "resolved": false, + "integrity": "sha512-LzHF64s5chPQQS0IYBn9IN5h3i98c12bo4NCO7e0sGM2llXQ3p2FGC5sdENN4cTW48O915Sh+x+EXx7XW96xYQ==", + "requires": { + "chownr": "^1.1.1", + "fs-minipass": "^1.2.5", + "minipass": "^2.3.4", + "minizlib": "^1.1.1", + "mkdirp": "^0.5.0", + "safe-buffer": "^5.1.2", + "yallist": "^3.0.2" + } + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": false, + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" + }, + "wide-align": { + "version": "1.1.3", + "resolved": false, + "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==" + }, + "wrappy": { + "version": "1.0.2", + "resolved": false, + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" + }, + "yallist": { + "version": "3.0.3", + "resolved": false, + "integrity": "sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A==" + } + } + }, "get-caller-file": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", @@ -563,6 +1010,11 @@ "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", "dev": true }, + "nan": { + "version": "2.14.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.0.tgz", + "integrity": "sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg==" + }, "p-locate": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", @@ -2439,6 +2891,9 @@ "invariant": "^2.2.2", "levenary": "^1.1.1", "semver": "^5.5.0" + }, + "dependencies": { + "node-releases": {} } }, "@babel/preset-modules": { @@ -8206,6 +8661,24 @@ "semver": "^6.0.0" } }, + "make-dir": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", + "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", + "dev": true, + "requires": { + "pify": "^4.0.1", + "semver": "^5.6.0" + }, + "dependencies": { + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true + } + } + }, "semver": { "version": "6.3.0", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", @@ -8719,6 +9192,10 @@ "path-exists": "^4.0.0" } }, + "fsevents": { + "version": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.2.tgz", + "integrity": "sha512-R4wDiBwZ0KzpgOWetKDug1FZcYhqYnUYKtfZYt4mD5SBz76q0KR4Q9o7GIPamsVPGmW3EYPPJ0dOOjvx32ldZA==" + }, "get-caller-file": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", @@ -9856,6 +10333,73 @@ "color-convert": "^2.0.1" } }, + "autoprefixer": { + "version": "9.7.6", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-9.7.6.tgz", + "integrity": "sha512-F7cYpbN7uVVhACZTeeIeealwdGM6wMtfWARVLTy5xmKtgVdBNJvbDRoCK3YO1orcs7gv/KwYlb3iXwu9Ug9BkQ==", + "dev": true, + "requires": { + "browserslist": "^4.11.1", + "caniuse-lite": "^1.0.30001039", + "chalk": "^2.4.2", + "normalize-range": "^0.1.2", + "num2fraction": "^1.2.2", + "postcss": "^7.0.27", + "postcss-value-parser": "^4.0.3" + }, + "dependencies": { + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, "chalk": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", @@ -9921,6 +10465,7 @@ "universalify": "^1.0.0" } }, + "normalize-package-data": {}, "rimraf": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", diff --git a/projects/ngx-text-diff/src/lib/components/components.module.ts b/projects/ngx-text-diff/src/lib/components/components.module.ts new file mode 100644 index 0000000..b419c01 --- /dev/null +++ b/projects/ngx-text-diff/src/lib/components/components.module.ts @@ -0,0 +1,16 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { LoaderSpinnerComponent } from './loader-spinner/loader-spinner.component'; +import { SideBySideTableComponent } from './side-by-side-table/side-by-side-table.component'; +import { LineByLineTableComponent } from './line-by-line-table/line-by-line-table.component'; +import { DirectivesModule } from '../directives/directives.module'; +import { PipesModule } from '../pipes/pipes.module'; +import { CdkTableModule } from '@angular/cdk/table'; +import { ScrollingModule } from '@angular/cdk/scrolling'; + +@NgModule({ + declarations: [LoaderSpinnerComponent, SideBySideTableComponent, LineByLineTableComponent], + imports: [CommonModule, CdkTableModule, ScrollingModule, DirectivesModule, PipesModule], + exports: [LoaderSpinnerComponent, SideBySideTableComponent, LineByLineTableComponent], +}) +export class ComponentsModule {} diff --git a/projects/ngx-text-diff/src/lib/components/line-by-line-table/line-by-line-table.component.css b/projects/ngx-text-diff/src/lib/components/line-by-line-table/line-by-line-table.component.css new file mode 100644 index 0000000..e69de29 diff --git a/projects/ngx-text-diff/src/lib/components/line-by-line-table/line-by-line-table.component.html b/projects/ngx-text-diff/src/lib/components/line-by-line-table/line-by-line-table.component.html new file mode 100644 index 0000000..08db190 --- /dev/null +++ b/projects/ngx-text-diff/src/lib/components/line-by-line-table/line-by-line-table.component.html @@ -0,0 +1,69 @@ +
+ + + + + + + + + + + + + + + + + + + + + + + + + +
+ + {{ row.lineNumber | lineNumber }} + + + {{ row.lineNumberRight | lineNumber }} + + + {{ row.prefix | linePrefix }} + + + + + + + + +
+
diff --git a/projects/ngx-text-diff/src/lib/components/line-by-line-table/line-by-line-table.component.spec.ts b/projects/ngx-text-diff/src/lib/components/line-by-line-table/line-by-line-table.component.spec.ts new file mode 100644 index 0000000..5bb4768 --- /dev/null +++ b/projects/ngx-text-diff/src/lib/components/line-by-line-table/line-by-line-table.component.spec.ts @@ -0,0 +1,25 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { LineByLineTableComponent } from './line-by-line-table.component'; + +describe('LineByLineTableComponent', () => { + let component: LineByLineTableComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ LineByLineTableComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(LineByLineTableComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/projects/ngx-text-diff/src/lib/components/line-by-line-table/line-by-line-table.component.ts b/projects/ngx-text-diff/src/lib/components/line-by-line-table/line-by-line-table.component.ts new file mode 100644 index 0000000..a89301e --- /dev/null +++ b/projects/ngx-text-diff/src/lib/components/line-by-line-table/line-by-line-table.component.ts @@ -0,0 +1,28 @@ +import { Component, Input, OnInit } from '@angular/core'; +import { Observable } from 'rxjs'; +import { DiffLineByLineResult, DiffLineResult, DiffPart } from '../../ngx-text-diff.model'; + +@Component({ + selector: 'td-line-by-line-table', + templateUrl: './line-by-line-table.component.html', + styleUrls: ['./line-by-line-table.component.css'] +}) +export class LineByLineTableComponent implements OnInit { + @Input() id: string; + @Input() data: Observable; + @Input() displayedColumns: string[] = ['lineNumber', 'lineNumberRight', 'prefix', 'lineContent']; + + constructor() { } + + ngOnInit(): void { + } + + trackTableRows(index, row: DiffLineByLineResult) { + return row?.lineNumber ?? index; + } + + trackDiffs(index, diff: DiffPart) { + return index; + } + +} diff --git a/projects/ngx-text-diff/src/lib/loader-spinner/loader-spinner.component.css b/projects/ngx-text-diff/src/lib/components/loader-spinner/loader-spinner.component.css similarity index 100% rename from projects/ngx-text-diff/src/lib/loader-spinner/loader-spinner.component.css rename to projects/ngx-text-diff/src/lib/components/loader-spinner/loader-spinner.component.css diff --git a/projects/ngx-text-diff/src/lib/loader-spinner/loader-spinner.component.html b/projects/ngx-text-diff/src/lib/components/loader-spinner/loader-spinner.component.html similarity index 100% rename from projects/ngx-text-diff/src/lib/loader-spinner/loader-spinner.component.html rename to projects/ngx-text-diff/src/lib/components/loader-spinner/loader-spinner.component.html diff --git a/projects/ngx-text-diff/src/lib/loader-spinner/loader-spinner.component.ts b/projects/ngx-text-diff/src/lib/components/loader-spinner/loader-spinner.component.ts similarity index 100% rename from projects/ngx-text-diff/src/lib/loader-spinner/loader-spinner.component.ts rename to projects/ngx-text-diff/src/lib/components/loader-spinner/loader-spinner.component.ts diff --git a/projects/ngx-text-diff/src/lib/components/side-by-side-table/side-by-side-table.component.css b/projects/ngx-text-diff/src/lib/components/side-by-side-table/side-by-side-table.component.css new file mode 100644 index 0000000..b101c5e --- /dev/null +++ b/projects/ngx-text-diff/src/lib/components/side-by-side-table/side-by-side-table.component.css @@ -0,0 +1,99 @@ +.td-table-container { + grid-column: 1 / 2; + grid-row: 2; + width: 100%; + max-width: 100%; + overflow-x: auto; +} + +.td-table-wrapper { + display: flex; + width: 200%; +} + +.td-table { + border: 1px solid darkgray; + max-height: 50vh; + width: 100%; + max-width: 100%; +} + +.fit-column { + width: 1px; + white-space: nowrap; +} + +.line-number-col { + position: relative; /* I am the fallback */ + + /* Give it everything you got! (use an auto prefixer...) */ + position: -webkit-sticky; + position: sticky; + left: 0; + top: auto; + border-right: 1px solid #ddd; + color: #999; + text-align: right; + background-color: #f7f7f7; + padding-left: 10px; + padding-right: 10px; + font-size: 87.5%; +} + +.line-number-col-left { + color: #999; + padding-left: 10px; + padding-right: 10px; + text-align: right; + background-color: #f7f7f7; + font-size: 87.5%; +} + +.insert-row, +.insert-row > .line-number-col { + background-color: #dfd; + border-color: #b4e2b4; +} + +.delete-row, +.delete-row > .line-number-col { + background-color: #fee8e9; + border-color: #e9aeae; +} + +.empty-row { + background-color: #f7f7f7; + height: 24px; +} + +.td-table td { + border-top: 0; + padding-top: 0; + padding-bottom: 0; + white-space: nowrap; + max-width: 50%; +} + +pre { + margin-bottom: 0; +} + +td.content-col { + padding: 0; + margin: 0; + line-height: 24px; +} + +td.prefix-col { + padding-left: 10px; + padding-right: 10px; + line-height: 24px; +} + +.insert-row > .highlight { + background-color: #acf2bd !important; +} + +.delete-row > .highlight { + background-color: #fdb8c0 !important; +} diff --git a/projects/ngx-text-diff/src/lib/components/side-by-side-table/side-by-side-table.component.html b/projects/ngx-text-diff/src/lib/components/side-by-side-table/side-by-side-table.component.html new file mode 100644 index 0000000..7d0208b --- /dev/null +++ b/projects/ngx-text-diff/src/lib/components/side-by-side-table/side-by-side-table.component.html @@ -0,0 +1,56 @@ +
+ + + + + + + + + + + + + + + + + + + + +
+ + {{ row?.lineNumber | lineNumber }} + + + {{ row?.prefix | linePrefix }} + + + + + + + + +
+
diff --git a/projects/ngx-text-diff/src/lib/components/side-by-side-table/side-by-side-table.component.spec.ts b/projects/ngx-text-diff/src/lib/components/side-by-side-table/side-by-side-table.component.spec.ts new file mode 100644 index 0000000..46fd7d1 --- /dev/null +++ b/projects/ngx-text-diff/src/lib/components/side-by-side-table/side-by-side-table.component.spec.ts @@ -0,0 +1,25 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { SideBySideTableComponent } from './side-by-side-table.component'; + +describe('SideBySideTableComponent', () => { + let component: SideBySideTableComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ SideBySideTableComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(SideBySideTableComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/projects/ngx-text-diff/src/lib/components/side-by-side-table/side-by-side-table.component.ts b/projects/ngx-text-diff/src/lib/components/side-by-side-table/side-by-side-table.component.ts new file mode 100644 index 0000000..d7afea8 --- /dev/null +++ b/projects/ngx-text-diff/src/lib/components/side-by-side-table/side-by-side-table.component.ts @@ -0,0 +1,27 @@ +import { Component, Input, OnInit } from '@angular/core'; +import { Observable } from 'rxjs'; +import { DiffLineResult, DiffPart, DiffTableRowResult } from '../../ngx-text-diff.model'; + +@Component({ + selector: 'td-side-by-side-table', + templateUrl: './side-by-side-table.component.html', + styleUrls: ['./side-by-side-table.component.css'], +}) +export class SideBySideTableComponent implements OnInit { + @Input() id: string; + @Input() data: Observable; + @Input() displayedColumns: string[] = ['lineNumber', 'prefix', 'lineContent']; + @Input() prefix = ''; + + constructor() {} + + ngOnInit(): void {} + + trackTableRows(index, row: DiffLineResult) { + return row?.lineNumber ?? index; + } + + trackDiffs(index, diff: DiffPart) { + return index; + } +} diff --git a/projects/ngx-text-diff/src/lib/directives/directives.module.ts b/projects/ngx-text-diff/src/lib/directives/directives.module.ts new file mode 100644 index 0000000..668e99e --- /dev/null +++ b/projects/ngx-text-diff/src/lib/directives/directives.module.ts @@ -0,0 +1,10 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { ContainerDirective } from './ngx-text-diff-container.directive'; + +@NgModule({ + declarations: [ContainerDirective], + imports: [CommonModule], + exports: [ContainerDirective], +}) +export class DirectivesModule {} diff --git a/projects/ngx-text-diff/src/lib/ngx-text-diff-container.directive.ts b/projects/ngx-text-diff/src/lib/directives/ngx-text-diff-container.directive.ts similarity index 100% rename from projects/ngx-text-diff/src/lib/ngx-text-diff-container.directive.ts rename to projects/ngx-text-diff/src/lib/directives/ngx-text-diff-container.directive.ts diff --git a/projects/ngx-text-diff/src/lib/format-line.pipe.spec.ts b/projects/ngx-text-diff/src/lib/format-line.pipe.spec.ts deleted file mode 100644 index d40edb1..0000000 --- a/projects/ngx-text-diff/src/lib/format-line.pipe.spec.ts +++ /dev/null @@ -1,8 +0,0 @@ -/*import { FormatLinePipe } from './format-line.pipe'; - -describe('FormatLinePipe', () => { - it('create an instance', () => { - const pipe = new FormatLinePipe(); - expect(pipe).toBeTruthy(); - }); -});*/ diff --git a/projects/ngx-text-diff/src/lib/format-line.pipe.ts b/projects/ngx-text-diff/src/lib/format-line.pipe.ts deleted file mode 100644 index b7ee634..0000000 --- a/projects/ngx-text-diff/src/lib/format-line.pipe.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { Pipe, PipeTransform } from '@angular/core'; - -@Pipe({ - name: 'formatLine' -}) -export class FormatLinePipe implements PipeTransform { - transform(line: string, diffs?: string[]): string { - if (!line) { - return ' '; - } - if (!!diffs && diffs.length > 0) { - /*diffs.forEach(diff => { - line = line.replace(diff, `${diff}`); - });*/ - } - return line - .replace(/&/g, '&') - .replace(//g, '>') - .replace(/"/g, '"') - .replace(/ /g, ' '); - } -} diff --git a/projects/ngx-text-diff/src/lib/ngx-text-diff.component.html b/projects/ngx-text-diff/src/lib/ngx-text-diff.component.html index 2c8341b..2945b58 100644 --- a/projects/ngx-text-diff/src/lib/ngx-text-diff.component.html +++ b/projects/ngx-text-diff/src/lib/ngx-text-diff.component.html @@ -1,6 +1,5 @@
-
- -
- - - - - - - - - -
- {{ row.leftContent?.lineNumber !== -1 ? row.leftContent?.lineNumber : ' ' }} - - {{ row.leftContent?.prefix || ' ' }} - - - - -
-
- -
- - - - - - - - - -
- {{ row.rightContent?.lineNumber !== -1 ? row.rightContent?.lineNumber : ' ' }} - - {{ row.rightContent?.prefix || ' ' }} - - - - -
-
- - + + + + + + + + +
diff --git a/projects/ngx-text-diff/src/lib/ngx-text-diff.component.ts b/projects/ngx-text-diff/src/lib/ngx-text-diff.component.ts index 46bc88b..1816bc7 100644 --- a/projects/ngx-text-diff/src/lib/ngx-text-diff.component.ts +++ b/projects/ngx-text-diff/src/lib/ngx-text-diff.component.ts @@ -1,20 +1,31 @@ import { + AfterViewInit, ChangeDetectorRef, Component, + EventEmitter, Input, OnDestroy, OnInit, Output, - EventEmitter, - ViewChildren, QueryList, - AfterViewInit + ViewChildren, } from '@angular/core'; -import { DiffContent, DiffPart, DiffTableFormat, DiffTableFormatOption, DiffTableRowResult, DiffResults } from './ngx-text-diff.model'; +import { + DiffContent, + DiffLineByLineResult, + DiffLineResult, + DiffPart, + DiffResults, + DiffTableFormat, + DiffTableFormatOption, + DiffTableLineByLine, + DiffTableRowResult, + DiffTableSideBySide, +} from './ngx-text-diff.model'; import { NgxTextDiffService } from './ngx-text-diff.service'; -import { Observable, Subscription } from 'rxjs'; -import { ContainerDirective } from './ngx-text-diff-container.directive'; -import { ScrollDispatcher, CdkScrollable } from '@angular/cdk/scrolling'; +import { BehaviorSubject, Observable, Subscription } from 'rxjs'; +import { ContainerDirective } from './directives/ngx-text-diff-container.directive'; +import { CdkScrollable, ScrollDispatcher } from '@angular/cdk/scrolling'; @Component({ selector: 'td-ngx-text-diff', @@ -22,7 +33,6 @@ import { ScrollDispatcher, CdkScrollable } from '@angular/cdk/scrolling'; styleUrls: ['./ngx-text-diff.component.css'], }) export class NgxTextDiffComponent implements OnInit, AfterViewInit, OnDestroy { - private _hideMatchingLines = false; @ViewChildren(ContainerDirective) containers: QueryList; @Input() format: DiffTableFormat = 'SideBySide'; @Input() left = ''; @@ -31,14 +41,7 @@ export class NgxTextDiffComponent implements OnInit, AfterViewInit, OnDestroy { @Input() loading = false; @Input() showToolbar = true; @Input() showBtnToolbar = true; - @Input() - get hideMatchingLines() { - return this._hideMatchingLines; - } - - set hideMatchingLines(hide: boolean) { - this.hideMatchingLinesChanged(hide); - } + @Input() hideMatchingLines = false; @Input() outerContainerClass: string; @Input() outerContainerStyle: any; @Input() toolbarClass: string; @@ -49,11 +52,17 @@ export class NgxTextDiffComponent implements OnInit, AfterViewInit, OnDestroy { @Output() compareResults = new EventEmitter(); subscriptions: Subscription[] = []; tableRows: DiffTableRowResult[] = []; - filteredTableRows: DiffTableRowResult[] = []; - tableRowsLineByLine: DiffTableRowResult[] = []; - filteredTableRowsLineByLine: DiffTableRowResult[] = []; diffsCount = 0; + private _leftContent: BehaviorSubject = new BehaviorSubject([]); + leftContent$: Observable = this._leftContent.asObservable(); + + private _rightContent: BehaviorSubject = new BehaviorSubject([]); + rightContent$: Observable = this._rightContent.asObservable(); + + private _lineByLine: BehaviorSubject = new BehaviorSubject([]); + lineByLine$: Observable = this._lineByLine.asObservable(); + formatOptions: DiffTableFormatOption[] = [ { id: 'side-by-side', @@ -74,25 +83,17 @@ export class NgxTextDiffComponent implements OnInit, AfterViewInit, OnDestroy { constructor(private scrollService: ScrollDispatcher, private diff: NgxTextDiffService, private cd: ChangeDetectorRef) {} ngOnInit() { - this.loading = true; if (this.diffContent) { this.subscriptions.push( this.diffContent.subscribe(content => { - this.loading = true; this.left = content.leftContent; this.right = content.rightContent; - this.renderDiffs() - .then(() => { - this.cd.detectChanges(); - this.loading = false; - }) - .catch(() => (this.loading = false)); + this.initTable(); }) ); } - this.renderDiffs() - .then(() => (this.loading = false)) - .catch(e => (this.loading = false)); + + this.initTable(); } ngAfterViewInit() { @@ -105,81 +106,53 @@ export class NgxTextDiffComponent implements OnInit, AfterViewInit, OnDestroy { } } - hideMatchingLinesChanged(value: boolean) { - this._hideMatchingLines = value; - if (this.hideMatchingLines) { - this.filteredTableRows = this.tableRows.filter( - row => (row.leftContent && row.leftContent.prefix === '-') || (row.rightContent && row.rightContent.prefix === '+') - ); - this.filteredTableRowsLineByLine = this.tableRowsLineByLine.filter( - row => (row.leftContent && row.leftContent.prefix === '-') || (row.rightContent && row.rightContent.prefix === '+') - ); - } else { - this.filteredTableRows = this.tableRows; - this.filteredTableRowsLineByLine = this.tableRowsLineByLine; + hideMatchingLinesChanged(hideMatchingLines: boolean) { + if (this.hideMatchingLines !== hideMatchingLines) { + this.hideMatchingLines = hideMatchingLines; + this.populateTable(); } } setDiffTableFormat(format: DiffTableFormat) { - this.format = format; + if (format !== this.format) { + this.format = format; + this.populateTable(); + } } - async renderDiffs() { - try { - this.diffsCount = 0; - this.tableRows = await this.diff.getDiffsByLines(this.left, this.right); - this.tableRowsLineByLine = this.tableRows.reduce((tableLineByLine: DiffTableRowResult[], row: DiffTableRowResult) => { - if (!tableLineByLine) { - tableLineByLine = []; - } - if (row.hasDiffs) { - if (row.leftContent) { - tableLineByLine.push({ - leftContent: row.leftContent, - rightContent: null, - belongTo: row.belongTo, - hasDiffs: true, - numDiffs: row.numDiffs, - }); - } - if (row.rightContent) { - tableLineByLine.push({ - leftContent: null, - rightContent: row.rightContent, - belongTo: row.belongTo, - hasDiffs: true, - numDiffs: row.numDiffs, - }); - } - } else { - tableLineByLine.push(row); - } + initTable() { + this.diff + .getDiffsByLines(this.left, this.right) + .then(results => { + this.tableRows = results; + this.populateTable(); + }) + .catch(err => { + this.tableRows = []; + this.populateTable(); + }); + } - return tableLineByLine; - }, []); - this.diffsCount = this.tableRows.filter(row => row.hasDiffs).length; - this.filteredTableRows = this.tableRows; - this.filteredTableRowsLineByLine = this.tableRowsLineByLine; - this.emitCompareResultsEvent(); - } catch (e) { - throw e; + populateTable() { + switch (this.format) { + case 'LineByLine': + this.populateLineByLineTable(); + break; + case 'SideBySide': + this.populateSideBySideTable(); + break; } } - emitCompareResultsEvent() { - const diffResults: DiffResults = { - hasDiff: this.diffsCount > 0, - diffsCount: this.diffsCount, - rowsWithDiff: this.tableRows - .filter(row => row.hasDiffs) - .map(row => ({ - leftLineNumber: row.leftContent ? row.leftContent.lineNumber : null, - rightLineNumber: row.rightContent ? row.rightContent.lineNumber : null, - numDiffs: row.numDiffs, - })), - }; - - this.compareResults.next(diffResults); + private populateSideBySideTable() { + const results: DiffTableSideBySide = this.diff.getSideBySide(this.tableRows, this.hideMatchingLines); + this._leftContent.next(results.left); + this._rightContent.next(results.right); + } + + private populateLineByLineTable() { + const results: DiffTableLineByLine = this.diff.getLineByLine(this.tableRows, this.hideMatchingLines); + this._lineByLine.next(results.rows); } trackTableRows(index, row: DiffTableRowResult) { @@ -191,17 +164,19 @@ export class NgxTextDiffComponent implements OnInit, AfterViewInit, OnDestroy { } private initScrollListener() { - this.subscriptions.push(this.scrollService.scrolled().subscribe((scrollableEv: CdkScrollable) => { - if (scrollableEv && this.synchronizeScrolling) { - const scrollableId = scrollableEv.getElementRef().nativeElement.id; - const nonScrolledContainer: ContainerDirective = this.containers.find(container => container.id !== scrollableId); - if (nonScrolledContainer) { - nonScrolledContainer.element.scrollTo({ - top: scrollableEv.measureScrollOffset('top'), - left: scrollableEv.measureScrollOffset('left'), - }); + this.subscriptions.push( + this.scrollService.scrolled().subscribe((scrollableEv: CdkScrollable) => { + if (scrollableEv && this.synchronizeScrolling) { + const scrollableId = scrollableEv.getElementRef().nativeElement.id; + const nonScrolledContainer: ContainerDirective = this.containers.find(container => container.id !== scrollableId); + if (nonScrolledContainer) { + nonScrolledContainer.element.scrollTo({ + top: scrollableEv.measureScrollOffset('top'), + left: scrollableEv.measureScrollOffset('left'), + }); + } } - } - })); + }) + ); } } diff --git a/projects/ngx-text-diff/src/lib/ngx-text-diff.model.ts b/projects/ngx-text-diff/src/lib/ngx-text-diff.model.ts index 7f0c9f3..b824efb 100644 --- a/projects/ngx-text-diff/src/lib/ngx-text-diff.model.ts +++ b/projects/ngx-text-diff/src/lib/ngx-text-diff.model.ts @@ -27,6 +27,20 @@ export interface DiffLineResult { lineDiffs: DiffPart[]; } +export interface DiffTableSideBySide { + left: DiffLineResult[]; + right: DiffLineResult[]; +} + +export interface DiffTableLineByLine { + rows: DiffLineByLineResult[]; +} + +export interface DiffLineByLineResult extends DiffLineResult { + lineNumber: number; + lineNumberRight: number; +} + export interface DiffTableRowResult { leftContent: DiffLineResult; rightContent: DiffLineResult; diff --git a/projects/ngx-text-diff/src/lib/ngx-text-diff.module.ts b/projects/ngx-text-diff/src/lib/ngx-text-diff.module.ts index 8522567..648aa83 100644 --- a/projects/ngx-text-diff/src/lib/ngx-text-diff.module.ts +++ b/projects/ngx-text-diff/src/lib/ngx-text-diff.module.ts @@ -2,14 +2,15 @@ import { NgModule } from '@angular/core'; import { NgxTextDiffComponent } from './ngx-text-diff.component'; import { CommonModule } from '@angular/common'; import { FormsModule } from '@angular/forms'; -import { LoaderSpinnerComponent } from './loader-spinner/loader-spinner.component'; -import { FormatLinePipe } from './format-line.pipe'; -import { ContainerDirective } from './ngx-text-diff-container.directive'; import { ScrollingModule } from '@angular/cdk/scrolling'; +import { CdkTableModule } from '@angular/cdk/table'; +import { DirectivesModule } from './directives/directives.module'; +import { PipesModule } from './pipes/pipes.module'; +import { ComponentsModule } from './components/components.module'; @NgModule({ - imports: [CommonModule, FormsModule, ScrollingModule], - declarations: [NgxTextDiffComponent, LoaderSpinnerComponent, FormatLinePipe, ContainerDirective], + imports: [CommonModule, FormsModule, ScrollingModule, CdkTableModule, DirectivesModule, PipesModule, ComponentsModule], + declarations: [NgxTextDiffComponent], exports: [NgxTextDiffComponent], }) export class NgxTextDiffModule {} diff --git a/projects/ngx-text-diff/src/lib/ngx-text-diff.service.ts b/projects/ngx-text-diff/src/lib/ngx-text-diff.service.ts index c8a34c4..73431a6 100644 --- a/projects/ngx-text-diff/src/lib/ngx-text-diff.service.ts +++ b/projects/ngx-text-diff/src/lib/ngx-text-diff.service.ts @@ -1,10 +1,10 @@ import { Injectable } from '@angular/core'; import { Diff, DIFF_DELETE, DIFF_EQUAL, DIFF_INSERT, diff_match_patch } from 'diff-match-patch'; -import { DiffLineResult, DiffPart, DiffTableRowResult } from './ngx-text-diff.model'; -import { isEmpty, isNil } from './ngx-text-diff.utils'; +import { DiffLineResult, DiffPart, DiffTableLineByLine, DiffTableRowResult, DiffTableSideBySide } from './ngx-text-diff.model'; +import { isEmpty } from './ngx-text-diff.utils'; @Injectable({ - providedIn: 'root' + providedIn: 'root', }) export class NgxTextDiffService { diffParser: diff_match_patch; @@ -17,21 +17,19 @@ export class NgxTextDiffService { this.diffParser = new diff_match_patch(); } - getDiffsByLines(left: string, right: string): Promise { - return new Promise((resolve, reject) => { - const a = this.diffParser.diff_linesToChars_(left, right); - const lineText1 = a.chars1; - const lineText2 = a.chars2; - const linesArray = a.lineArray; - const diffs: Diff[] = this.diffParser.diff_main(lineText1, lineText2, true); - this.diffParser.diff_charsToLines_(diffs, linesArray); - const rows: DiffTableRowResult[] = this.formatOutput(diffs); - if (!rows) { - reject('Error'); - } + async getDiffsByLines(left: string, right: string): Promise { + const a = this.diffParser.diff_linesToChars_(left, right); + const lineText1 = a.chars1; + const lineText2 = a.chars2; + const linesArray = a.lineArray; + const diffs: Diff[] = this.diffParser.diff_main(lineText1, lineText2, true); + this.diffParser.diff_charsToLines_(diffs, linesArray); + const rows: DiffTableRowResult[] = this.formatOutput(diffs); + if (!rows) { + throw new Error('Error'); + } - resolve(rows); - }); + return rows; } private formatOutput(diffs: Diff[]): DiffTableRowResult[] { @@ -63,13 +61,13 @@ export class NgxTextDiffService { lineNumber: lineLeft, lineContent: line, lineDiffs: [], - prefix: '' + prefix: '', }; rightContent = { lineNumber: lineRight, lineContent: line, lineDiffs: [], - prefix: '' + prefix: '', }; rowTemp = { leftContent, @@ -100,7 +98,7 @@ export class NgxTextDiffService { lineNumber: lineLeft, lineContent: line, lineDiffs: [{ content: line, isDiff: true }], - prefix: '-' + prefix: '-', }; if (rightDiffRow) { rightDiffRow.leftContent = leftContent; @@ -143,7 +141,7 @@ export class NgxTextDiffService { lineNumber: lineRight, lineContent: line, lineDiffs: [{ content: line, isDiff: true }], - prefix: '+' + prefix: '+', }; if (leftDiffRow) { leftDiffRow.rightContent = rightContent; @@ -179,7 +177,7 @@ export class NgxTextDiffService { if (result.leftContent) { diffCount += result.leftContent.lineDiffs.filter(diff => diff.isDiff).length; } - if (result.leftContent) { + if (result.rightContent) { diffCount += result.rightContent.lineDiffs.filter(diff => diff.isDiff).length; } return diffCount; @@ -218,4 +216,59 @@ export class NgxTextDiffService { return diffParts; } + + getSideBySide(rows: DiffTableRowResult[], onlyDiffs = false): DiffTableSideBySide { + return rows + .filter(row => !onlyDiffs || row.hasDiffs) + .reduce( + (temp: DiffTableSideBySide, row: DiffTableRowResult) => { + temp.left.push(row.leftContent); + temp.right.push(row.rightContent); + + return temp; + }, + { left: [], right: [] } + ); + } + + getLineByLine(rows: DiffTableRowResult[], onlyDiffs = false): DiffTableLineByLine { + return rows + .filter(row => !onlyDiffs || row.hasDiffs) + .reduce( + (temp: DiffTableLineByLine, row: DiffTableRowResult) => { + const { leftContent, rightContent, hasDiffs } = row; + if (!hasDiffs) { + temp.rows.push({ + lineNumber: leftContent?.lineNumber, + lineNumberRight: rightContent?.lineNumber, + lineContent: leftContent?.lineContent, + prefix: leftContent?.prefix, + lineDiffs: [], + }); + } else { + if (!!leftContent) { + temp.rows.push({ + lineNumber: leftContent.lineNumber, + lineNumberRight: -1, + lineContent: leftContent.lineContent, + prefix: leftContent.prefix, + lineDiffs: leftContent.lineDiffs, + }); + } + if (!!rightContent) { + temp.rows.push({ + lineNumber: -1, + lineNumberRight: rightContent.lineNumber, + lineContent: rightContent.lineContent, + prefix: rightContent.prefix, + lineDiffs: rightContent.lineDiffs, + }); + } + } + + return temp; + }, + { rows: [] } + ); + } } diff --git a/projects/ngx-text-diff/src/lib/pipes/line-content.pipe.spec.ts b/projects/ngx-text-diff/src/lib/pipes/line-content.pipe.spec.ts new file mode 100644 index 0000000..d369bb9 --- /dev/null +++ b/projects/ngx-text-diff/src/lib/pipes/line-content.pipe.spec.ts @@ -0,0 +1,8 @@ +import { LineContentPipe } from './line-content.pipe'; + +describe('LineContentPipe', () => { + it('create an instance', () => { + const pipe = new LineContentPipe(); + expect(pipe).toBeTruthy(); + }); +}); diff --git a/projects/ngx-text-diff/src/lib/pipes/line-content.pipe.ts b/projects/ngx-text-diff/src/lib/pipes/line-content.pipe.ts new file mode 100644 index 0000000..8383bca --- /dev/null +++ b/projects/ngx-text-diff/src/lib/pipes/line-content.pipe.ts @@ -0,0 +1,14 @@ +import { Pipe, PipeTransform } from '@angular/core'; + +@Pipe({ + name: 'lineContent', +}) +export class LineContentPipe implements PipeTransform { + transform(line: string, diffs?: string[]): string { + if (!line) { + return ' '; + } + + return line.replace(/&/g, '&').replace(//g, '>').replace(/"/g, '"').replace(/ /g, ' '); + } +} diff --git a/projects/ngx-text-diff/src/lib/pipes/line-number.pipe.spec.ts b/projects/ngx-text-diff/src/lib/pipes/line-number.pipe.spec.ts new file mode 100644 index 0000000..d5f1664 --- /dev/null +++ b/projects/ngx-text-diff/src/lib/pipes/line-number.pipe.spec.ts @@ -0,0 +1,8 @@ +import { LineNumberPipe } from './line-number.pipe'; + +describe('LineNumberPipe', () => { + it('create an instance', () => { + const pipe = new LineNumberPipe(); + expect(pipe).toBeTruthy(); + }); +}); diff --git a/projects/ngx-text-diff/src/lib/pipes/line-number.pipe.ts b/projects/ngx-text-diff/src/lib/pipes/line-number.pipe.ts new file mode 100644 index 0000000..1c29a12 --- /dev/null +++ b/projects/ngx-text-diff/src/lib/pipes/line-number.pipe.ts @@ -0,0 +1,10 @@ +import { Pipe, PipeTransform } from '@angular/core'; + +@Pipe({ + name: 'lineNumber', +}) +export class LineNumberPipe implements PipeTransform { + transform(value: number, ...args: unknown[]): number | string { + return value !== -1 ? value : ' '; + } +} diff --git a/projects/ngx-text-diff/src/lib/pipes/line-prefix.pipe.spec.ts b/projects/ngx-text-diff/src/lib/pipes/line-prefix.pipe.spec.ts new file mode 100644 index 0000000..5aa0ef3 --- /dev/null +++ b/projects/ngx-text-diff/src/lib/pipes/line-prefix.pipe.spec.ts @@ -0,0 +1,8 @@ +import { LinePrefixPipe } from './line-prefix.pipe'; + +describe('LinePrefixPipe', () => { + it('create an instance', () => { + const pipe = new LinePrefixPipe(); + expect(pipe).toBeTruthy(); + }); +}); diff --git a/projects/ngx-text-diff/src/lib/pipes/line-prefix.pipe.ts b/projects/ngx-text-diff/src/lib/pipes/line-prefix.pipe.ts new file mode 100644 index 0000000..285f9ab --- /dev/null +++ b/projects/ngx-text-diff/src/lib/pipes/line-prefix.pipe.ts @@ -0,0 +1,12 @@ +import { Pipe, PipeTransform } from '@angular/core'; + +@Pipe({ + name: 'linePrefix' +}) +export class LinePrefixPipe implements PipeTransform { + + transform(value: string, ...args: unknown[]): string { + return value ?? ' '; + } + +} diff --git a/projects/ngx-text-diff/src/lib/pipes/pipes.module.ts b/projects/ngx-text-diff/src/lib/pipes/pipes.module.ts new file mode 100644 index 0000000..2f04bd1 --- /dev/null +++ b/projects/ngx-text-diff/src/lib/pipes/pipes.module.ts @@ -0,0 +1,12 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { LineNumberPipe } from './line-number.pipe'; +import { LineContentPipe } from './line-content.pipe'; +import { LinePrefixPipe } from './line-prefix.pipe'; + +@NgModule({ + declarations: [LineNumberPipe, LineContentPipe, LinePrefixPipe], + imports: [CommonModule], + exports: [LineNumberPipe, LineContentPipe, LinePrefixPipe], +}) +export class PipesModule {} diff --git a/src/app/home/home.component.ts b/src/app/home/home.component.ts index c7a3b7b..0b7b7e1 100644 --- a/src/app/home/home.component.ts +++ b/src/app/home/home.component.ts @@ -52,9 +52,8 @@ export class HomeComponent implements OnInit { } submitComparison() { - this.submitted = false; - this.contentObservable.next(this.content); this.submitted = true; + this.contentObservable.next(this.content); } handleChange(side: 'left' | 'right', value: string) {