diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml
new file mode 100644
index 0000000..3282be8
--- /dev/null
+++ b/.github/workflows/pr.yml
@@ -0,0 +1,28 @@
+name: Pull Request
+
+on:
+ pull_request:
+ branches: ['master']
+
+jobs:
+ codestyle:
+ runs-on: ubuntu-latest
+
+ name: Codestyle
+
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v3
+
+ - uses: actions/setup-node@v4
+ with:
+ node-version: 20
+
+ - name: Install dependencies
+ run: npm ci
+
+ - name: Check prettier
+ run: npx prettier --check .
+
+ - name: Check eslint
+ run: npx eslint --max-warnings 0 .
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
new file mode 100644
index 0000000..1a47ab3
--- /dev/null
+++ b/.github/workflows/release.yml
@@ -0,0 +1,58 @@
+name: Release
+
+on:
+ push:
+ tags:
+ - 'v[0-9]+.[0-9]+.[0-9]+'
+
+permissions:
+ contents: write
+ id-token: write
+
+jobs:
+ build:
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v3
+ - name: Set env
+ run: echo "RELEASE_VERSION=${GITHUB_REF#refs/*/}" >> $GITHUB_ENV
+
+ - uses: actions/setup-node@v4
+ with:
+ node-version: 20
+
+ - name: Generate changelog
+ run: |
+ npm i -g conventional-changelog-cli@^3.0.0
+ conventional-changelog -p angular -o CHANGELOG.md -r 2
+ tail -n +2 CHANGELOG.md > CHANGELOG #remove first line
+ sed -i -e '/./,$!d' -e :a -e '/^\n*$/{$d;N;ba' -e '}' CHANGELOG #trim empty lines
+
+ - name: Build
+ run: |
+ npm ci
+ npm run build
+ cd dist
+ zip -r ../release.zip *
+
+ - name: Release
+ uses: actions/create-release@v1
+ id: create_release
+ with:
+ draft: false
+ prerelease: false
+ release_name: TreeJS ${{ env.RELEASE_VERSION }}
+ tag_name: ${{ env.RELEASE_VERSION }}
+ body_path: CHANGELOG
+ env:
+ GITHUB_TOKEN: ${{ github.token }}
+ - name: Upload release artifact
+ uses: actions/upload-release-asset@v1
+ env:
+ GITHUB_TOKEN: ${{ github.token }}
+ with:
+ upload_url: ${{ steps.create_release.outputs.upload_url }}
+ asset_path: ./release.zip
+ asset_name: TreeJS-${{ env.RELEASE_VERSION }}.zip
+ asset_content_type: application/zip
diff --git a/.gitignore b/.gitignore
index 5273281..51b9af5 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,7 +1,7 @@
# See http://help.github.com/ignore-files/ for more about ignoring files.
# compiled output
-# dist
+dist
tmp
/out-tsc
diff --git a/dist/tree.min.js b/dist/tree.min.js
deleted file mode 100644
index df3e143..0000000
--- a/dist/tree.min.js
+++ /dev/null
@@ -1,8 +0,0 @@
-/**
- * TreeJS is a JavaScript library for displaying TreeViews
- * on the web.
- *
- * @author Matthias Thalmann
- * @copyright Matthias Thalmann
- */
-const TreeUtil={default_leaf_icon:"🖹",default_parent_icon:"🗁",default_open_icon:"◢",default_close_icon:"▶",isDOM(e){try{return e instanceof HTMLElement}catch{return"object"==typeof e&&1===e.nodeType&&"object"==typeof e.style&&"object"==typeof e.ownerDocument}},getProperty:(e,t,n)=>void 0===e[t]?n:e[t],expandNode(e){e.setExpanded(!0),e.isLeaf()||e.getChildren().forEach((e=>{TreeUtil.expandNode(e)}))},collapseNode(e){e.setExpanded(!1),e.isLeaf()||e.getChildren().forEach((e=>{TreeUtil.collapseNode(e)}))},getSelectedNodesForNode(e){if(!(e instanceof TreeNode))throw new Error("Parameter 1 must be of type TreeNode");const t=new Array;return e.isSelected()&&t.push(e),e.getChildren().forEach((e=>{e.isSelected()&&-1===t.indexOf(e)&&t.push(e),e.isLeaf()||TreeUtil.getSelectedNodesForNode(e).forEach((e=>{-1===t.indexOf(e)&&t.push(e)}))})),t}},TreeConfig={leaf_icon:TreeUtil.default_leaf_icon,parent_icon:TreeUtil.default_parent_icon,open_icon:TreeUtil.default_open_icon,close_icon:TreeUtil.default_close_icon,context_menu:void 0};function TreeNode(e,t){const n=new Array,o=new Array;let i=!0,r=!0,s=!1;if(e){if("string"!=typeof e&&"function"!=typeof e.toString)throw new Error("Parameter 1 must be of type String or Object, where it must have the function toString()")}else e="";t&&"object"==typeof t?(i=TreeUtil.getProperty(t,"expanded",!0),r=TreeUtil.getProperty(t,"enabled",!0),s=TreeUtil.getProperty(t,"selected",!1)):t={},this.addChild=function(e){if(TreeUtil.getProperty(t,"allowsChildren",!0)){if(!(e instanceof TreeNode))throw new Error("Parameter 1 must be of type TreeNode");n.push(e),Object.defineProperty(e,"parent",{value:this,writable:!1,enumerable:!0,configurable:!0})}else console.warn("Option allowsChildren is set to false, no child added")},this.removeChildPos=function(e){void 0!==n[e]&&void 0!==n[e]&&n.splice(e,1)},this.removeChild=function(e){if(!(e instanceof TreeNode))throw new Error("Parameter 1 must be of type TreeNode");this.removeChildPos(this.getIndexOfChild(e))},this.getChildren=function(){return n},this.getChildCount=function(){return n.length},this.getIndexOfChild=function(e){for(let t=0;t{let t=e.target;for(;void 0===t.tj_node||t.classList.contains("tj_container");)t=t.parentElement;const n=t.tj_node;if(void 0!==n&&n.isEnabled())if(!1===e.ctrlKey&&(n.isLeaf()?n.open():(n.toggleExpanded(),o.reload()),n.on("click")(e,n)),!0===e.ctrlKey)n.toggleSelected(),o.reload();else{const e=n.getRoot();e instanceof TreeNode&&TreeUtil.getSelectedNodesForNode(e).forEach((e=>{e.setSelected(!1)})),n.setSelected(!0),o.reload()}})),r.addEventListener("contextmenu",(e=>{let t=e.target;for(;void 0===t.tj_node||t.classList.contains("tj_container");)t=t.parentElement;const n=t.tj_node;void 0!==n&&(void 0!==n.getListener("contextmenu")?(n.on("contextmenu")(e,n),e.preventDefault()):"function"==typeof TreeConfig.context_menu&&(TreeConfig.context_menu(e,n),e.preventDefault()))})),e.isLeaf()&&!TreeUtil.getProperty(e.getOptions(),"forceParent",!1)){let o="",i=TreeUtil.getProperty(e.getOptions(),"icon","");""!==i||""!=(i=TreeUtil.getProperty(n,"leaf_icon",""))?o+=`${i}`:o+=`${TreeConfig.leaf_icon}`,r.innerHTML=`${o+e.toString()}`,r.classList.add("tj_leaf"),t.appendChild(r)}else{let o="";e.isExpanded()?o+=`${TreeConfig.open_icon}`:o+=`${TreeConfig.close_icon}`;const s=TreeUtil.getProperty(e.getOptions(),"icon","");if(""!==s||""!=(s=TreeUtil.getProperty(n,"parent_icon",""))?o+=`${s}`:o+=`${TreeConfig.parent_icon}`,r.innerHTML=`${o+e.toString()}`,t.appendChild(r),e.isExpanded()){const n=document.createElement("ul");e.getChildren().forEach((e=>{n.appendChild(i(e))})),t.appendChild(n)}}return t}n&&"object"==typeof n||(n={}),this.setRoot=function(t){e instanceof TreeNode&&(e=t)},this.getRoot=function(){return e},this.expandAllNodes=function(){e.setExpanded(!0),e.getChildren().forEach((e=>{TreeUtil.expandNode(e)}))},this.expandPath=function(e){if(!(e instanceof TreePath))throw new Error("Parameter 1 must be of type TreePath");e.getPath().forEach((e=>{e.setExpanded(!0)}))},this.collapseAllNodes=function(){e.setExpanded(!1),e.getChildren().forEach((e=>{TreeUtil.collapseNode(e)}))},this.setContainer=function(e){if(TreeUtil.isDOM(e))t=e;else if((e=document.querySelector(e))instanceof Array&&(e=e[0]),!TreeUtil.isDOM(e))throw new Error("Parameter 1 must be either DOM-Object or CSS-QuerySelector (#, .)")},this.getContainer=function(){return t},this.setOptions=function(e){"object"==typeof e&&(n=e)},this.changeOption=function(e,t){n[e]=t},this.getOptions=function(){return n},this.getSelectedNodes=function(){return TreeUtil.getSelectedNodesForNode(e)},this.reload=function(){if(null===t)return void console.warn("No container specified");t.classList.add("tj_container");const o=document.createElement("ul");TreeUtil.getProperty(n,"show_root",!0)?o.appendChild(i(e)):e.getChildren().forEach((e=>{o.appendChild(i(e))})),t.innerHTML="",t.appendChild(o)},void 0!==t&&this.reload()}function TreePath(e,t){let n=new Array;this.setPath=function(e,t){for(n=new Array;void 0!==t&&!t.equals(e);)n.push(t),t=t.parent;if(!t.equals(e))throw n=new Array,new Error("Node is not contained in the tree of root");return n.push(e),n=n.reverse(),n},this.getPath=function(){return n},this.toString=function(){return n.join(" - ")},e instanceof TreeNode&&t instanceof TreeNode&&this.setPath(e,t)}function test(){return console.log("Test function executed"),"Test completed"}
\ No newline at end of file
diff --git a/dist/treejs.min.css b/dist/treejs.min.css
deleted file mode 100644
index 1e1f7dc..0000000
--- a/dist/treejs.min.css
+++ /dev/null
@@ -1 +0,0 @@
-.tj_container *{box-sizing:border-box;position:relative}.tj_container ul{list-style-type:none;padding-left:2em}.tj_container>ul:first-of-type{padding:0}.tj_container li span.tj_description{-webkit-touch-callout:none;border-radius:2px;cursor:pointer;display:block;padding:2px 5px;text-align:left;-webkit-user-select:none;-khtml-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.tj_container li span.tj_description:hover{background-color:#ccc}.tj_container li span.tj_icon,.tj_container li span.tj_mod_icon{display:inline-block;margin-right:.5em}.tj_container li span.tj_mod_icon,.tj_container li span.tj_mod_icon *{width:1em}.tj_container li span.tj_description.tj_leaf{margin-left:1.5em}.tj_container li[disabled=""]{color:#b5b5b5}.tj_container li[disabled=""]:hover span.tj_description{background-color:inherit;cursor:default}.tj_container span.tj_description.selected{background-color:#2b2b2b;color:#fff}.tj_container span.tj_description.selected:hover{background-color:#606060}
\ No newline at end of file