diff --git a/.eslintrc.json b/.eslintrc.json index c7231e6..60463f0 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -1,8 +1,11 @@ { "parser": "@typescript-eslint/parser", "parserOptions": { + "ecmaVersion": 6, + "sourceType": "module", "ecmaFeatures": { - "jsx": false + "jsx": false, + "modules": true }, "useJSXTextNode": true, "project": "./tsconfig.json", diff --git a/.githooks/pre-commit/filter.sh b/.githooks/pre-commit/filter.sh index 123ac65..351bc8b 100644 --- a/.githooks/pre-commit/filter.sh +++ b/.githooks/pre-commit/filter.sh @@ -7,7 +7,6 @@ npm run lint if [[ $? != 0 ]]; then echo "lint error." - exit 1; fi diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml new file mode 100644 index 0000000..48a4576 --- /dev/null +++ b/.github/workflows/codeql.yml @@ -0,0 +1,74 @@ +# For most projects, this workflow file will not need changing; you simply need +# to commit it to your repository. +# +# You may wish to alter this file to override the set of languages analyzed, +# or to provide custom queries or build logic. +# +# ******** NOTE ******** +# We have attempted to detect the languages in your repository. Please check +# the `language` matrix defined below to confirm you have the correct set of +# supported CodeQL languages. +# +name: "CodeQL" + +on: + push: + branches: [ "master" ] + pull_request: + # The branches below must be a subset of the branches above + branches: [ "master" ] + schedule: + - cron: '15 23 * * 4' + +jobs: + analyze: + name: Analyze + runs-on: ubuntu-latest + permissions: + actions: read + contents: read + security-events: write + + strategy: + fail-fast: false + matrix: + language: [ 'javascript' ] + # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ] + # Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support + + steps: + - name: Checkout repository + uses: actions/checkout@v3 + + # Initializes the CodeQL tools for scanning. + - name: Initialize CodeQL + uses: github/codeql-action/init@v2 + with: + languages: ${{ matrix.language }} + # If you wish to specify custom queries, you can do so here or in a config file. + # By default, queries listed here will override any specified in a config file. + # Prefix the list here with "+" to use these queries and those in the config file. + + # Details on CodeQL's query packs refer to : https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs + # queries: security-extended,security-and-quality + + + # Autobuild attempts to build any compiled languages (C/C++, C#, Go, or Java). + # If this step fails, then you should remove it and run the build manually (see below) + - name: Autobuild + uses: github/codeql-action/autobuild@v2 + + # ℹ️ Command-line programs to run using the OS shell. + # πŸ“š See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun + + # If the Autobuild fails above, remove it and uncomment the following three lines. + # modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance. + + # - run: | + # echo "Run, Build Application using script" + # ./location_of_script_within_repo/buildscript.sh + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v2 + with: + category: "/language:${{matrix.language}}" diff --git a/.nycrc.json b/.nycrc.json index 44e8ce4..f14268c 100644 --- a/.nycrc.json +++ b/.nycrc.json @@ -7,15 +7,14 @@ "src/common", "src/index.ts", "src/browser.ts", - "src/snowbrush.js", - "src/snowbrush.js.map", "test/", "example/", "dist/", "scripts/", "**/*.d.ts", "docs", - "coverage" + "coverage", + "rollup.config.ts" ], "extension": [".ts"], "reporter": ["html", "text", "lcov"], diff --git a/LICENSE b/LICENSE index 65402f5..688b3f8 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2019 XMind Ltd. +Copyright (c) 2019 - 2023 Xmind Ltd. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md index ab3318b..ef3b6fb 100644 --- a/README.md +++ b/README.md @@ -1,69 +1,73 @@ -# XMind SDK for JavaScript +# πŸ“¦ πŸ“¦ πŸ“¦ ![](https://assets.xmind.net/www/assets/images/xmind-logo-dark-7a5ac2ec22.svg) -[![Build Status](https://travis-ci.org/xmindltd/xmind-sdk-js.svg?branch=master)](https://travis-ci.org/xmindltd/xmind-sdk-js) +[![CodeQL](https://github.com/xmindltd/xmind-sdk-js/actions/workflows/codeql.yml/badge.svg?branch=master)](https://github.com/xmindltd/xmind-sdk-js/actions/workflows/codeql.yml) [![Build status](https://ci.appveyor.com/api/projects/status/qll0sp4ny7bl7yo0/branch/master?svg=true)](https://ci.appveyor.com/project/danielsss/xmind-sdk-js/branch/master) -[![Codacy Badge](https://api.codacy.com/project/badge/Grade/36420399770547e4825f0657eb29118b)](https://www.codacy.com/app/danielsss/xmind-sdk-js?utm_source=github.com&utm_medium=referral&utm_content=xmindltd/xmind-sdk-js&utm_campaign=Badge_Grade) -[![Codacy Badge](https://api.codacy.com/project/badge/Coverage/36420399770547e4825f0657eb29118b)](https://www.codacy.com/app/danielsss/xmind-sdk-js?utm_source=github.com&utm_medium=referral&utm_content=xmindltd/xmind-sdk-js&utm_campaign=Badge_Coverage) +[![Codacy Badge](https://app.codacy.com/project/badge/Grade/a0cef4ab9b024a97b4ef2970fec29158)](https://www.codacy.com/gh/xmindltd/xmind-sdk-js/dashboard?utm_source=github.com&utm_medium=referral&utm_content=xmindltd/xmind-sdk-js&utm_campaign=Badge_Grade) ![npm](https://img.shields.io/npm/v/xmind) ![GitHub](https://img.shields.io/github/license/xmindltd/xmind-sdk-js.svg) -[![npm (scoped)](https://img.shields.io/badge/XMind-ZEN-red.svg)](https://www.xmind.net) +[![npm (scoped)](https://img.shields.io/badge/XMind-ZEN-red.svg)](https://xmind.app) -The official XMind SDK for JavaScript (written by typescript), available for browsers and Node.js backends. +This project is a lightweight official software development kit for JavaScript/Typescript which is available for browsers and Node.js. -This library implements various functions which is similar to our client. If you have used our client before, you will know how to use this library. +This library implements various functions which are similar to our UI applications and You might know the basic concepts of this library if you've used the application before. -In order to use the SDK conveniently, an essential concept you should know is that everything is component and each one of them has a unique component ID. You can add child nodes under the components, however, the Markers and Notes can only be attached to the components. +In order to use it conveniently, an essential concept you should know is that everything is a component and each one of them has a unique component ID. You can add a child node under the components, however, the Markers and Notes can only be attached to the components. -You can open the final `.xmind` files by XMind ZEN. +Eventually, Our UI apps could be used to open the `*.xmind` file generated by this tool. + +Last but not least, we also provided the [Online 🌐 Mind-Mapping](https://xmind.works) for anyone who wants to explore something new. + +## Supported Platforms -Supported Platforms: * Linux * Win32 -* Browser +* Browser (Not Fully Supported) ## Usage and Getting Started -### Usage in Node.js +### Node.js ```shell $ npm i --save xmind ``` -> NOTICE: The `xmind-sdk` is renamed to `xmind` from version: 2.0.0 +> NOTICE: The `xmind-sdk` is renamed to `xmind` from the version: 2.0.0 > -> If you were installed `xmind-sdk`. Please, use `npm i --save xmind` to instead of it. +> Please, use `npm i --save xmind` to replace with it if you were using the `xmind-sdk`. ```js -const {Workbook, Topic, Marker} = require('xmind'); +const { Workbook, Topic, Marker } = require('xmind'); ``` -### Usage in Browser +### Browser or Vue.js ```jsx harmony -import { Workbook, Topic, Marker, Loader, Dumper } from 'xmind'; +import { Workbook, Topic, Marker } from 'xmind'; ``` ```html // HTML // Latest version - + // Specify version - + - ``` -### Simple Usage +### More Examples + + +See [example directory](./example). ```js const { Workbook, Topic, Marker, Zipper } = require('xmind'); -const [workbook, marker] = [new Workbook(), new Marker()]; +const [ workbook, marker ] = [new Workbook(), new Marker()]; const topic = new Topic({sheet: workbook.createSheet('sheet title', 'Central Topic')}); const zipper = new Zipper({path: '/tmp', workbook, filename: 'MyFirstMap'}); @@ -91,53 +95,27 @@ topic zipper.save().then(status => status && console.log('Saved /tmp/MyFirstMap.xmind')); ``` -### More Examples -See [example directory](./example). - -## Loader - -The loader helps you loading an exists .xmind file into `sdk`. - -### Options - -* options.ctx: `JSZip` +## Workbook -> Usage: -> -> ```js -> const { Loader, Topic } = require('xmind'); -> const JSZip = require('jszip'); -> -> const main = async () => { -> const loader = new Loader({ctx: await JSZip.loadAsync('/absolute/path/file.xmind')}); -> const sheets = await loader.loadSheets(); -> /* {[id: string]: Sheet} */ -> const topic = new Topic({sheet: sheets[0], isLoaded: true}); -> } -> ``` -> -> [See fully example](./example/example.loader.js) +The workbook is a temporary storage where all the data are written. ### Methods -#### .loadSheets() => Promise<{[id: string]: Sheet}> - -It returns an array of object which is used to get topic instance as parameter. - -#### .getWorkbook() => Workbook - -The Loader will create a workbook instance automatically. It is useful if you want to save the map via `Zipper`. +#### .createSheet(sheetTitle, topicTitle?) => `Sheet` -## Workbook +Once the workbook is created, then there's a way to build a sheet containing a `root topic`. +In addition, you can customize their titles by parameters. -The workbook is a temporary storage where all the data are written. -### Methods +| Name | Type | Default | Required | +|:---- |:----:|:-------:|:--------:| +| sheetTitle | String | `-` | Y | +| topicTitle | String | `Central Topic` | N | -#### .createSheet(sheetTitle, topicTitle?) => `Sheet` +#### .createSheets(options: Object[]) => `Object[]` -Once the workbook is created, then there's a way to build a sheet containing a `root topic`. In addition, you can custom their titles by parameters. +You can use this method to create sheets in bulk. | Name | Type | Default | Required | @@ -145,10 +123,28 @@ Once the workbook is created, then there's a way to build a sheet containing a ` | sheetTitle | String | `-` | Y | | topicTitle | String | `Central Topic` | N | +It returns an object of sheet identifier([Click here to check how it uses](./example/example.fully.js)). + +```typescript +const sheets = workbook.createSheets([ + {s: 'SheetTitle1', t: 'RootTopicTitle1'}, + {s: 'SheetTitle2', t: 'RootTopicTitle2'} +]); +console.info(sheets); +// [ +// { id: string, title: string }, +// { id: string, title: string } +// ... +// ] +``` + +#### .getSheets() => `Object[]` + +It allows you to get back the identifier of the sheet anytime and anywhere. -#### .loadSheets(sheets: SheetData[]) => Promise<{[id: string]: Sheet}> +#### .getSheet(id: string) => `Sheet` -This method can help you to load sheets from an exists `.xmind` file easily, But we encourage you to load sheets starting with `Loader`. +You can get an instance of the sheet with an existed sheet ID. #### .theme(sheetTitle, themeName) => Boolean @@ -169,22 +165,22 @@ Get component's data from the workbook in the form of `STRING`. #### .validate() => `{status: Boolean, errors: Array | null}` -This is proof that all data are available and complete. +This is the way to prove that all data are available and complete. -The `status` indicates the result of validation which is `true` if it's correct, othewise `false` returns. +The `status` indicates the result of validation which is `true` if it's correct, otherwise `false` returns. ## Topic -The `Topic` is an important constructor function that implements most of the methods. And you are going to depend on it during most operations. +The `Topic` is an important constructor function that implements most of the methods. +And you're going to depend on it during most operations. ### Topic Options -* options.sheet: `workbook.createSheet(...)` -* options.isLoaded: Set true if you starts with `Loader` +* options.sheet <= `workbook.createSheet(...)` You may wonder why we need to offer the `options.sheet` manually? The reason is that `Topic` is implemented independently and most of the methods depend on the instance of the sheet. -In the UI client, you also need to draw the mind map on sheet. +In the UI client, you also need to draw the mind map on the sheet. > usage: > @@ -201,26 +197,28 @@ In the UI client, you also need to draw the mind map on sheet. Set the component to be parent node. If there isn't component ID, the `Central Topic` will become as parent node. -#### .cid(title?) => String +#### .cid(title?, options?: { customId?: string, parentId?: string }) => String -Use .cid to get component ID corresponding to the `title`. -> _!!! NOTE THAT:_ You should avoid duplicating the component `title` if use `title` to search the component ID. +Use this method to get componentId. -If none of the components has been added, at least `Central Topic`'ID could be returned. +> You should use `customId` or `parentId` for getting the `componentId` if there are some duplicated topic titles. -If you don't specify title in the period of calling .cid, the last added component ID would be returned. +If you don't specify the title in the period of calling `.cid()`, +the last `componentId` that you've added would be returned. #### .cids() => {$cid: $title} -That will return all added components which is not included `note` or `marker`. +It will return all the `key/value`s in once. #### .add(options) => Topic Add a topic component under parent node. -| Name | Type | Default | Required | -|:----:|:----:|:-------:|:--------:| -| options.title | String | null | Y | +| Name | Type | Default | Required | +|:----:|:----:|:---------------------------------------------------:|:--------:| +| options.title | String | null | Y | +| options.parentId | String | The previous topic that you've operated on | N | +| options.customId | String | It would be useful if you have the same topic title | N | #### .note(text, del?) => Topic @@ -232,10 +230,30 @@ Attach a text to parent node. | text | String | null | Y | text message | | del | Boolean | false | N | detach the note from current parent node if the `del` is true | +#### .addLabel(text) => Topic + +Add label text to the component, also you can add label to the same component many times. + +| Name | Type | Default | Required | Description | +|:----:|:----:|:-------:|:--------:|:------------| +| text | String | null | Y | label text string | + + +#### .removeLabels(componentId?) => Topic + +Remove all the labels from the component. + +> If you don't give the componentId, then remove labels from the currently component. + +| Name | Type | Default | Required | Description | +|:----:|:----:|:-------:|:--------:|:------------| +| componentId | String | null | N | - | #### .marker(object) => Topic -Attach a marker flag to the parent node. Moreover, you can detach a marker flag from the parent node by setting `object.del` as `true`. default: `false` +Attach a marker flag to the parent node. +Moreover, you can detach a marker flag from the parent node by setting `object.del` as `true`. +Default: `false` Example: @@ -255,7 +273,7 @@ topic.marker(Object.assign({}, marker.smiley('cry'), {del: true})); You can use `.image()` to get `image key` back. -Also you need to write image into manifest by `zip.updateManifestMetadata()`. +However, you need to write image into manifest by `zip.updateManifestMetadata()`. > [See image example](./example/example.image.js) @@ -264,14 +282,14 @@ Also you need to write image into manifest by `zip.updateManifestMetadata()`. Attach a summary component to parent node including all children. In the meantime, the `edge` can be used to set the scope of summary component. > _!!! NOTE THAT_ > -> The summary does not allow to be added under `Central Topic` +> The summary doesn't allow to be added under `Central Topic` > > The `edge` must parallel to parent node | Name | Type | Default | Required | |:---- |:----:|:-------:|:--------:| | options.title | String | null | Y | -| options.edge | String | null | N | +| options.edge | String | null | N | > [About `edge`](./docs/edge.graphic.txt) @@ -316,7 +334,7 @@ We provide an instance of `Marker` that includes all the markers. Such as: > **The `name` of marker is available [!here](./docs/icons.md)** > -> You also can use **Marker.groups** and **Marker.names** to find out available names +> You can also use the **Marker.groups** and **Marker.names** to find out available names of Marker. ### Static methods @@ -327,7 +345,7 @@ List available group names. #### Marker.names(groupName) => Array\ -Get the flag names by `groupName`. +* Get the flag names by `groupName`. ## Zipper @@ -338,7 +356,7 @@ The module of `Zipper` only works under backend. ### Zipper Options -| Name | Type | Default | Required | Description | +| Name | Type | Default | Required | Description | |:---- |:----:|:-------:|:--------:|:------------| | options.path | String | `-` | Y | The path is where to save the `.xmind` file | | options.workbook | Workbook | `-` | Y | The instance of Workbook | @@ -348,35 +366,37 @@ The module of `Zipper` only works under backend. Update manifest for image insertion. -| Name | Type | Default | Required | Description | +| Name | Type | Default | Required | Description | |:---- |:----:|:-------:|:--------:|:------------| | key | String | null | Y | The key only can get by topic.image() | | content | Buffer | null | Y | The buffer data of image | #### .removeManifestMetadata(key) => Zipper -Remove a pair of key/value from manifest. +Remove a pair of key / value from manifest. #### .save() => Promise\ Save components to the logic disk in the form of zip. -### Dumper +## Dumper The module of `Dumper` only works under browser. #### .dumping() => Array<{filename: string, value: string}> -Return an array of the object composed of file content. In order to open it in the official software, you need to compress these files in the form of zip with end of `.xmind`. +Return an array of objects composed of file content. +In order to open it in the official software, +you need to compress these files in the form of zip with the suffix `.xmind`. > **Important** > -> Do not include top level folders, otherwise the software can't extract files +> Don't include top level folders, otherwise the software can't extract files ## Contributing -Thank you for be interesting in the SDK. +Thank you for being interested in the SDK. -If you have any problems or suggestions please let us know πŸ™‚ +If you have any problems or suggestions, please let's know. πŸ™‚ We also welcome you to submit a pull request for any big or small issues. diff --git a/appveyor.yml b/appveyor.yml index 63b7991..27aff38 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,7 +1,7 @@ environment: matrix: - - nodejs_version: '8' - nodejs_version: '10' + - nodejs_version: '12' install: diff --git a/example/example.browser.html b/example/example.browser.html index 04e7256..a1a76b5 100644 --- a/example/example.browser.html +++ b/example/example.browser.html @@ -6,7 +6,7 @@ - +