├── .babelrc
├── .editorconfig
├── .eslintrc.js
├── .gitignore
├── .mocharc.e2e.env.js
├── .mocharc.e2e.js
├── CHANGELOG.md
├── CONTRIBUTING.md
├── Jenkinsfile
├── LICENSE
├── Makefile
├── Makefile.inc
├── README.md
├── SETUP.md
├── assets
├── angular.svg
├── react.svg
└── vue.svg
├── config
├── rollup.config.build.js
├── rollup.config.dev.js
└── rollup.config.js
├── dist
├── iink.esm.js
├── iink.esm.js.map
├── iink.min.css
├── iink.min.js
└── iink.min.js.map
├── docker
├── builder
│ └── Dockerfile
├── examples
│ ├── Dockerfile
│ ├── createIndexFile.sh
│ ├── entrypoint.sh
│ └── nginx.conf
└── wait-tcp
│ ├── Dockerfile
│ └── entrypoint.sh
├── docs
├── Editor.html
├── Editor.js.html
├── EditorFacade.js.html
├── configuration_Constants.js.html
├── configuration_DefaultBehaviors.js.html
├── configuration_DefaultConfiguration.js.html
├── configuration_DefaultPenStyle.js.html
├── configuration_DefaultTheme.js.html
├── configuration_LoggerConfig.js.html
├── eastereggs_InkImporter.js.html
├── event_Event.js.html
├── fonts
│ ├── OpenSans-Bold-webfont.eot
│ ├── OpenSans-Bold-webfont.svg
│ ├── OpenSans-Bold-webfont.woff
│ ├── OpenSans-BoldItalic-webfont.eot
│ ├── OpenSans-BoldItalic-webfont.svg
│ ├── OpenSans-BoldItalic-webfont.woff
│ ├── OpenSans-Italic-webfont.eot
│ ├── OpenSans-Italic-webfont.svg
│ ├── OpenSans-Italic-webfont.woff
│ ├── OpenSans-Light-webfont.eot
│ ├── OpenSans-Light-webfont.svg
│ ├── OpenSans-Light-webfont.woff
│ ├── OpenSans-LightItalic-webfont.eot
│ ├── OpenSans-LightItalic-webfont.svg
│ ├── OpenSans-LightItalic-webfont.woff
│ ├── OpenSans-Regular-webfont.eot
│ ├── OpenSans-Regular-webfont.svg
│ ├── OpenSans-Regular-webfont.woff
│ ├── OpenSans-Semibold-webfont.eot
│ ├── OpenSans-Semibold-webfont.svg
│ ├── OpenSans-Semibold-webfont.ttf
│ ├── OpenSans-Semibold-webfont.woff
│ ├── OpenSans-SemiboldItalic-webfont.eot
│ ├── OpenSans-SemiboldItalic-webfont.svg
│ ├── OpenSans-SemiboldItalic-webfont.ttf
│ └── OpenSans-SemiboldItalic-webfont.woff
├── global.html
├── grabber_PointerEventGrabber.js.html
├── index.html
├── model_InkModel.js.html
├── model_RecognizerContext.js.html
├── model_StrokeComponent.js.html
├── model_Symbol.js.html
├── model_UndoRedoContext.js.html
├── model_UndoRedoManager.js.html
├── recognizer_CryptoHelper.js.html
├── recognizer_DefaultRecognizer.js.html
├── recognizer_RecognizerService.js.html
├── recognizer_rest_iinkRestRecognizer.js.html
├── recognizer_rest_networkInterface.js.html
├── recognizer_websocket_WsBuilder.js.html
├── recognizer_websocket_WsRecognizerUtil.js.html
├── recognizer_websocket_iinkWsRecognizer.js.html
├── recognizer_websocket_networkWSInterface.js.html
├── renderer_QuadraticUtils.js.html
├── renderer_canvas_CanvasRenderer.js.html
├── renderer_canvas_ImageRenderer.js.html
├── renderer_canvas_stroker_QuadraticCanvasStroker.js.html
├── renderer_canvas_symbols_MathSymbolCanvasRenderer.js.html
├── renderer_canvas_symbols_ShapeSymbolCanvasRenderer.js.html
├── renderer_canvas_symbols_StrokeSymbolCanvasRenderer.js.html
├── renderer_canvas_symbols_TextSymbolCanvasRenderer.js.html
├── renderer_svg_SVGRenderer.js.html
├── renderer_svg_stroker_QuadraticSVGStroker.js.html
├── renderer_svg_symbols_StrokeSymbolSVGRenderer.js.html
├── scripts
│ ├── linenumber.js
│ └── prettify
│ │ ├── Apache-License-2.0.txt
│ │ ├── lang-css.js
│ │ └── prettify.js
├── smartguide_SmartGuide.js.html
├── styles
│ ├── jsdoc-default.css
│ ├── prettify-jsdoc.css
│ └── prettify-tomorrow.css
└── util_PromiseHelper.js.html
├── examples
├── assets
│ └── img
│ │ ├── clear.svg
│ │ ├── document.svg
│ │ ├── edit.svg
│ │ ├── eraser.svg
│ │ ├── pen.svg
│ │ ├── plus.svg
│ │ ├── redo.svg
│ │ └── undo.svg
├── dev
│ ├── debug.html
│ ├── index.html
│ └── style
│ │ ├── guide-text.html
│ │ ├── style-math.html
│ │ └── style-text.html
├── examples.css
├── experimental
│ ├── websocket_diagram_iink.html
│ └── websocket_text_document_iink.html
├── index.html
├── non-version-specific
│ ├── change_configuration.html
│ ├── handle_errors.html
│ └── on_demand_exports.html
└── v4
│ ├── diagram.css
│ ├── my-custom-class.css
│ ├── rest_diagram_iink.html
│ ├── rest_math_iink.html
│ ├── rest_no_ui.html
│ ├── rest_raw_content_iink.html
│ ├── rest_text_iink.html
│ ├── rest_text_iink_eraser.html
│ ├── websocket_math_custom_resources.html
│ ├── websocket_math_custom_resources_compiled.html
│ ├── websocket_math_iink.html
│ ├── websocket_math_iink_eraser.html
│ ├── websocket_math_import_jiix.html
│ ├── websocket_math_inside_page.html
│ ├── websocket_text_custom_lexicon.html
│ ├── websocket_text_custom_resources.html
│ ├── websocket_text_customize_editor_css.html
│ ├── websocket_text_customize_stroke_style.html
│ ├── websocket_text_file_export.html
│ ├── websocket_text_highlight_words.html
│ ├── websocket_text_iink.html
│ ├── websocket_text_iink_eraser.html
│ ├── websocket_text_iink_import_jiix.html
│ ├── websocket_text_iink_no_guides.html
│ ├── websocket_text_iink_search.html
│ ├── websocket_text_import_content.html
│ ├── websocket_text_interact.html
│ ├── websocket_text_local_storage_text.html
│ ├── websocket_text_multiple_inputs.html
│ ├── websocket_text_multiple_inputs.js
│ └── websocket_text_pointer_events.html
├── index.html
├── package-lock.json
├── package.json
├── preview.gif
├── src
├── Editor.js
├── EditorFacade.js
├── configuration
│ ├── Constants.js
│ ├── DefaultBehaviors.js
│ ├── DefaultConfiguration.js
│ ├── DefaultPenStyle.js
│ ├── DefaultTheme.js
│ └── LoggerConfig.js
├── eastereggs
│ └── InkImporter.js
├── event
│ └── Event.js
├── grabber
│ └── PointerEventGrabber.js
├── iink.css
├── iink.js
├── model
│ ├── InkModel.js
│ ├── RecognizerContext.js
│ ├── StrokeComponent.js
│ ├── Symbol.js
│ ├── UndoRedoContext.js
│ └── UndoRedoManager.js
├── recognizer
│ ├── CryptoHelper.js
│ ├── DefaultRecognizer.js
│ ├── RecognizerService.js
│ ├── rest
│ │ ├── iinkRestRecognizer.js
│ │ └── networkInterface.js
│ └── websocket
│ │ ├── WsBuilder.js
│ │ ├── WsRecognizerUtil.js
│ │ ├── iinkWsRecognizer.js
│ │ └── networkWSInterface.js
├── renderer
│ ├── QuadraticUtils.js
│ ├── canvas
│ │ ├── CanvasRenderer.js
│ │ ├── ImageRenderer.js
│ │ ├── stroker
│ │ │ └── QuadraticCanvasStroker.js
│ │ └── symbols
│ │ │ ├── MathSymbolCanvasRenderer.js
│ │ │ ├── ShapeSymbolCanvasRenderer.js
│ │ │ ├── StrokeSymbolCanvasRenderer.js
│ │ │ └── TextSymbolCanvasRenderer.js
│ └── svg
│ │ ├── SVGRenderer.js
│ │ ├── stroker
│ │ └── QuadraticSVGStroker.js
│ │ └── symbols
│ │ └── StrokeSymbolSVGRenderer.js
├── smartguide
│ └── SmartGuide.js
└── util
│ └── PromiseHelper.js
├── test
├── .eslintrc.js
├── lib
│ ├── configuration.js
│ ├── inks
│ │ ├── 3times2.json
│ │ ├── emphasized.json
│ │ ├── equation.json
│ │ ├── equation2.json
│ │ ├── equation3.json
│ │ ├── fence.json
│ │ ├── fourSquare.json
│ │ ├── hello.json
│ │ ├── helloHowAreYou.json
│ │ ├── helloStrike.json
│ │ ├── highlighted.json
│ │ ├── music.json
│ │ ├── one.json
│ │ ├── rabText.json
│ │ ├── rc_de_291.json
│ │ ├── rc_es_233.json
│ │ ├── rc_fr_187.json
│ │ ├── rc_fr_simple.json
│ │ ├── rc_it_216.json
│ │ ├── rc_ja_87.json
│ │ ├── rc_ja_90.json
│ │ ├── rc_ko_262.json
│ │ ├── rc_ko_282.json
│ │ ├── shape.json
│ │ ├── system.json
│ │ └── welcome_en_US.json
│ └── inksDatas.js
├── mocha
│ └── partial
│ │ ├── 00-configuration
│ │ ├── Constants.spec.babel.js
│ │ ├── DefaultBehaviors.spec.babel.js
│ │ ├── DefaultConfiguration.spec.babel.js
│ │ └── LoggerConfig.spec.babel.js
│ │ ├── 01-model
│ │ ├── InkModel.spec.babel.js
│ │ ├── StrokeComponent.spec.babel.js
│ │ └── UndoRedoManager.spec.babel.js
│ │ ├── 02-behaviors
│ │ └── PointerEventGrabber.spec.babel.js
│ │ └── CryptoHelper.spec.babel.js
└── playwright
│ ├── 01-ws-math.spec.js
│ ├── 02-ws-math-import.spec.js
│ ├── 03-ws-text.spec.js
│ ├── 04-ws-text-hightlight-word.spec.js
│ ├── 05-rest-text.spec.js
│ ├── 06-ws-math-custom-resources.spec.js
│ ├── 07-ws-text-custom-lexicon.spec.js
│ ├── 08-rest-raw-content.spec.js
│ ├── 09-change-configuration.spec.js
│ ├── 10-ws-text-big-text.spec.js
│ └── helper
│ ├── index.js
│ └── mochaHooks.js
└── yarn.lock
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": [
3 | [
4 | "@babel/env"
5 | ]
6 | ],
7 | "plugins": [
8 | "@babel/transform-runtime"
9 | ]
10 | }
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | [*]
2 | charset=utf-8
3 | end_of_line=lf
4 | insert_final_newline=false
5 | indent_style=space
6 | indent_size=4
7 |
8 | [{.babelrc,.stylelintrc,.eslintrc,jest.config,*.json,*.jsb3,*.jsb2,*.bowerrc}]
9 | indent_style=space
10 | indent_size=2
11 |
12 | [{*.applejs,*.js,*.html}]
13 | indent_style=space
14 | indent_size=2
15 |
16 | [*.js.flow]
17 | indent_style=space
18 | indent_size=2
19 |
20 | [{jshint.json,*.jshintrc}]
21 | indent_style=space
22 | indent_size=2
23 |
24 | [{*.jscs.json,*.jscsrc}]
25 | indent_style=space
26 | indent_size=2
27 |
28 | [*.scss]
29 | indent_style=space
30 | indent_size=2
31 |
32 | [*.coffee]
33 | indent_style=space
34 | indent_size=2
35 |
36 | [{.analysis_options,*.yml,*.yaml}]
37 | indent_style=space
38 | indent_size=2
39 |
40 |
--------------------------------------------------------------------------------
/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | env: {
3 | browser: true,
4 | es6: true
5 | },
6 | extends: [
7 | 'standard'
8 | ],
9 | parserOptions: {
10 | ecmaVersion: 2018,
11 | sourceType: 'module'
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | /node_modules/
2 | **/*.iml
3 | **/*.idea/
4 | /bower_components/
5 | **/*/delivery/
6 | /test/**/*.xml
7 | /test/mocha/results/
8 | /test/nightwatch/results/
9 | /test/webdriverio/results/
10 | /test/e2e/*
11 | .vscode
--------------------------------------------------------------------------------
/.mocharc.e2e.env.js:
--------------------------------------------------------------------------------
1 | process.env.LAUNCH_URL = process.env.LAUNCH_URL || 'http://localhost:8080'
2 | process.env.HEADLESS = true
3 | process.env.BROWSER = process.env.BROWSER || 'chromium'
--------------------------------------------------------------------------------
/.mocharc.e2e.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | exit: false,
3 | recursive: true,
4 | spec: './test/playwright/*.spec.js',
5 | require: '.mocharc.e2e.env.js,./test/playwright/helper/mochaHooks.js',
6 | timeout: 30000,
7 | parallel: false,
8 | reporter: 'spec'
9 | }
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # [v2.0.2](https://github.com/MyScript/iinkJS/tree/v2.0.2)
2 |
3 | ## Bug fix
4 | - responseCallback() always raises an error [link](https://github.com/MyScript/iinkJS/issues/18)
5 | - add query param to applicationKey
6 | # [v2.0.1](https://github.com/MyScript/iinkJS/tree/v2.0.1)
7 |
8 | ## Bug fix
9 | - bad link on the Get source code for Change configuration
10 | - enhance iink eraser websocket sample
11 | - math example: empty mathML is displayed in result instead of nothing
12 | - customize example, colour picker disappears after undo-redo
13 | - gesture.enable = false broken
14 | - can change configuration if first host is wrong
15 |
16 | # [v1.5.4](https://github.com/MyScript/iinkJS/tree/v1.5.4)
17 |
18 | ## Features
19 | - minor modification for specific integration
20 | # [v1.5.3](https://github.com/MyScript/iinkJS/tree/v1.5.3)
21 |
22 | ## Bug fix
23 | @Editor
24 | - fix change configuration IInks doesn't set the right font
25 |
26 | # [v1.5.2](https://github.com/MyScript/iinkJS/tree/v1.5.2)
27 |
28 | ## Bug fix
29 | @Editor
30 | - fix change configuration ink disappears in example configured in REST
31 |
32 | # [v1.5.1](https://github.com/MyScript/iinkJS/tree/v1.5.1)
33 |
34 | ## Bug fix
35 | @Editor
36 | - fix change configuration restart websocket connection
37 | - fix lost connection due to inactivity is now displayed
38 | - fix style is wrapped by global class and can be customized
39 |
40 | @examples
41 | - fix bad position of the searching highlight in searching text example
42 | - new examples with eraser
43 |
44 | ## Features
45 | - erase mode is now an option in websocket text
46 |
47 | ## Chore
48 | - refactor of examples
49 |
50 | # [v1.4.5](https://github.com/MyScript/iinkJS/tree/v1.4.5)
51 |
52 | ## Bug fix
53 |
54 | - fix missing ink on iOS with Scribble feature on
55 |
56 | # [v1.4.4](https://github.com/MyScript/iinkJS/tree/v1.4.4)
57 |
58 | ## Features
59 |
60 | - REST requests use `fetch` instead of `XMLHttpRequest`
61 |
62 | ## Bugs fix
63 |
64 | @Editor
65 | - fix setTheme not sent on reconnect or language change
66 | - fix resize on REST mode
67 |
68 | @examples
69 | - remove mixed-content image
70 |
71 | @docs
72 | - generated directly in sub folder /docs and accessible at https://myscript.github.io/iinkJS/docs/
73 |
74 | # [v1.4.3](https://github.com/MyScript/iinkJS/tree/v1.4.3)
75 |
76 | ## Bugs fix
77 |
78 | @Editor
79 | - fix `response is not a function` on reconnect
80 | - fix setTheme error on init
81 | - fix setTheme sent twice on init
82 |
83 | @examples
84 | - update katex to 0.12.0
85 | - simplify clean latex methods
86 |
87 | ## Chore
88 |
89 | - chore(deps): update rollup-plugin-terser to v7
90 |
91 | # [v1.4.2](https://github.com/MyScript/iinkJS/tree/v1.4.2)
92 |
93 | ## Bugs fix
94 |
95 | - fix(reco): last export not taken
96 |
97 | ## Chore
98 |
99 | - chore(deps): update minimist to 1.2.5
100 | - chore(deps): bump lodash from 4.17.15 to 4.17.19
101 |
102 | # [v1.4.1](https://github.com/MyScript/iinkJS/tree/v1.4.1)
103 |
104 | ## Breaking changes
105 |
106 | - ⚠ Package was renamed from `myscript` to `iink-js` to synchronize our SDK release version
107 | - V3 API is not available anymore, as such, configuration : `recognitionParams.(v3|v4)` was renamed to `recognitionParams.iink`
108 | - Remove stats from editor
109 | - Callbacks have been replaced by Promises
110 |
111 | ## Features
112 |
113 | - Editor exposes a new method to properly close websocket connection
114 | - Editor makes an API call to get list of available languages.
115 |
116 | ## Bugs fix
117 |
118 | @Editor
119 | - Fix `canClear` flag
120 | - Fix `clear` method
121 | - Fix error display
122 | - Fix lost strokes on reconnection
123 |
124 | @examples
125 | - Fix scrolling on iOS
126 | - Fix click on iOS after simple recognition
127 | - Fix export to file with FileSaver
128 |
129 | ### Chore
130 |
131 | - Remove `esdoc` to use `jsdoc` with npx
132 | - Upgrade tooling
133 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing
2 |
3 | We gladly welcome pull requests to iinkJS. If you have any questions, or need help to solve a problem, feel free to stop by the [MyScript forum](https://developer-support.myscript.com/support/discussions/forums/16000096021).
4 |
5 | ## CLA
6 |
7 | In order to contribute, you must first agree to the **Contributor License Agreement** available [here](http://goo.gl/forms/YyzZ9VSvYG).
8 |
9 | Make sure you read the article "[Contributing to Open Source on GitHub](https://guides.github.com/activities/contributing-to-open-source/)" to understand the contributing process.
--------------------------------------------------------------------------------
/Jenkinsfile:
--------------------------------------------------------------------------------
1 |
2 | def debianBuildDockerImage
3 | String dockerArgs = '-v /var/run/docker.sock:/var/run/docker.sock --userns host --privileged -v $HOME:$HOME -e "HOME=${HOME}'
4 |
5 | pipeline {
6 | agent { label "docker" }
7 | options { disableConcurrentBuilds() }
8 | environment {
9 | PROJECTNAME = "iink-js ${env.BRANCH_NAME}"
10 | PROJECTHOME = '/tmp/iink-js'
11 | PROJECT_DIR = "${WORKSPACE.replace('/var/jenkins_home/workspace','/dockervolumes/cloud/master/jenkins/workspace')}"
12 | APPLICATION_KEY = credentials('APPLICATION_KEY')
13 | HMAC_KEY = credentials('HMAC_KEY')
14 | MAKE_ARGS =" PROJECT_DIR=${env.PROJECT_DIR} HOME=${env.PROJECTHOME} BUILDID=${env.BUILD_NUMBER} DEV_APPLICATIONKEY=${env.APPLICATION_KEY} DEV_HMACKEY=${env.HMAC_KEY} "
15 | }
16 |
17 | stages {
18 |
19 | stage('Build docker builder') {
20 | steps {
21 | script {
22 | debianBuildDockerImage = docker.build("iink-js.debian-builder:${env.BUILD_ID}", '-f ./docker/builder/Dockerfile ./')
23 | }
24 | }
25 | }
26 |
27 | stage ('purge'){
28 | steps {
29 | script {
30 | debianBuildDockerImage.inside(dockerArgs) {
31 | sh "make ${env.MAKE_ARGS} purge"
32 | }
33 | }
34 | }
35 | }
36 |
37 | stage ('prepare'){
38 | steps {
39 | script {
40 | debianBuildDockerImage.inside(dockerArgs) {
41 | sh "make ${env.MAKE_ARGS} prepare"
42 | sh "make ${env.MAKE_ARGS} docker-wait-tcp"
43 | }
44 | }
45 | }
46 | }
47 |
48 | stage ('init_examples'){
49 | steps {
50 | script {
51 | debianBuildDockerImage.inside(dockerArgs) {
52 | sh "make ${env.MAKE_ARGS} docker-examples init_examples"
53 | }
54 | }
55 | }
56 | }
57 |
58 | stage('test browser with legacy server') {
59 |
60 | failFast false
61 |
62 | parallel {
63 |
64 | stage ('test-chromium'){
65 | steps {
66 | script {
67 | debianBuildDockerImage.inside(dockerArgs) {
68 | sh "BROWSER=chromium make ${env.MAKE_ARGS} test-e2e"
69 | }
70 | }
71 | }
72 | }
73 |
74 | stage ('test-webkit'){
75 | steps {
76 | script {
77 | debianBuildDockerImage.inside(dockerArgs) {
78 | sh "BROWSER=webkit make ${env.MAKE_ARGS} test-e2e"
79 | }
80 | }
81 | }
82 | }
83 |
84 | stage ('test-firefox'){
85 | steps {
86 | script {
87 | debianBuildDockerImage.inside(dockerArgs) {
88 | sh "BROWSER=firefox make ${env.MAKE_ARGS} test-e2e"
89 | }
90 | }
91 | }
92 | }
93 | }
94 |
95 | }
96 |
97 | stage ('audit'){
98 | steps {
99 | script {
100 | debianBuildDockerImage.inside(dockerArgs) {
101 | sh "npm audit --production"
102 | }
103 | }
104 | }
105 | }
106 | }
107 |
108 | post {
109 | always {
110 | sh "make ${env.MAKE_ARGS} killdocker"
111 | }
112 |
113 | success {
114 | slackSend color: "good", message: "${env.PROJECTNAME}: Build success ${env.JOB_NAME} ${env.BUILD_NUMBER}."
115 | }
116 | unstable {
117 | slackSend color: "warning", message: "${env.PROJECTNAME}: Unstable build, ${currentBuild.fullDisplayName} is unstable"
118 | }
119 | failure {
120 | slackSend color: "danger", message: "@group ${env.PROJECTNAME}: FAILURE, ${currentBuild.fullDisplayName} failed see there ${env.BUILD_URL}"
121 | }
122 | /* changed {
123 | slackSend color: "good", message: "${env.PROJECTNAME}: Build changed"
124 | }*/
125 | }
126 | }
127 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright MyScript.
2 | Licensed under the Apache License, Version 2.0 (the "License");
3 | you may not use this file except in compliance with the License.
4 | You may obtain a copy of the License at
5 | http://www.apache.org/licenses/LICENSE-2.0
6 | Unless required by applicable law or agreed to in writing, software
7 | distributed under the License is distributed on an "AS IS" BASIS,
8 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
9 | See the License for the specific language governing permissions and
10 | limitations under the License.
11 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | include Makefile.inc
2 |
3 | ALL: clean prepare docker test ## (default) Build all and launch test.
4 |
5 | .PHONY: ALL purge clean prepare build docker test docs
6 |
7 | purge: ## Reset the local directory as if a fresh git checkout was just make.
8 | @rm -rf node_modules
9 |
10 | clean: ## Remove all produced binaries.
11 | @rm -rf dist
12 | @rm -rf docs
13 |
14 | prepare: ## Install all dependencies.
15 | @PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD=1 npm install --force
16 |
17 | build: clean ## Building the dist files from sources.
18 | @npm run build
19 |
20 | docs: ## Building the doc files from sources.
21 | @npm run docs
22 |
23 | docker-wait-tcp:
24 | @cd docker/wait-tcp/ && docker build -t $(WAITTCP_DOCKERREPOSITORY) .
25 |
26 | docker-examples: build ## Build the docker image containing last version of myscript-js and examples.
27 | @rm -rf docker/examples/delivery/
28 | @mkdir -p docker/examples/delivery
29 | @cp -R dist docker/examples/delivery/
30 | @cp -R examples docker/examples/delivery/
31 | @cp -R node_modules docker/examples/delivery/
32 | @cd docker/examples/ && \
33 | docker build \
34 | --build-arg applicationkey=${DEV_APPLICATIONKEY} \
35 | --build-arg hmackey=${DEV_HMACKEY} \
36 | -t $(EXAMPLES_DOCKERREPOSITORY) .
37 |
38 | killdocker:
39 | @docker ps -a | grep "iinkjs-$(DOCKERTAG)-$(BUILDENV)-" | awk '{print $$1}' | xargs -r docker rm -f 2>/dev/null 1>/dev/null || true
40 |
41 |
42 | local-test-e2e: docker-examples init_examples
43 | @$(MAKE) BROWSER=$(BROWSER) test-e2e
44 |
45 | test-e2e:
46 | @if [[ $(DEVLOCAL) == true ]]; then \
47 | EXAMPLES_IP=localhost; \
48 | else \
49 | EXAMPLES_IP=$$(docker inspect --format '{{ .NetworkSettings.IPAddress }}' $(TEST_DOCKER_EXAMPLES_INSTANCE_NAME)); \
50 | fi && \
51 | docker run -i --rm \
52 | -v $(CURRENT_PWD):/home/pwuser/tests \
53 | $(DOCKER_EXAMPLES_PARAMETERS) \
54 | -e LAUNCH_URL="http://$${EXAMPLES_IP}:$(EXAMPLES_LISTEN_PORT)" \
55 | -e BROWSER=$(BROWSER) \
56 | -w "/home/pwuser/tests" \
57 | --name "playwright-$(BROWSER)-$(BUILDID)" mcr.microsoft.com/playwright:v1.16.0 \
58 | yarn test:e2e
59 |
60 | dev-test: docker-examples init_examples ## Launch all the requirements for launching tests
61 |
62 | _launch_examples:
63 | @echo "Starting examples container!"
64 | @docker run -d \
65 | -e "LISTEN_PORT=$(EXAMPLES_LISTEN_PORT)" \
66 | -e "APISCHEME=$(APISCHEME)" \
67 | -e "APIHOST=$(APIHOST)" \
68 | -e "APPLICATIONKEY=$(DEV_APPLICATIONKEY)" \
69 | -e "HMACKEY=$(DEV_HMACKEY)" \
70 | $(DOCKER_EXAMPLES_PARAMETERS) \
71 | --name $(TEST_DOCKER_EXAMPLES_INSTANCE_NAME) $(EXAMPLES_DOCKERREPOSITORY)
72 |
73 | _check_examples:
74 | @docker run --rm \
75 | --link $(TEST_DOCKER_EXAMPLES_INSTANCE_NAME):WAITHOST \
76 | -e "WAIT_PORT=$(EXAMPLES_LISTEN_PORT)" \
77 | -e "WAIT_SERVICE=Test examples" \
78 | $(WAITTCP_DOCKERREPOSITORY)
79 | @echo "Examples started!"
80 |
81 | init_examples: _launch_examples _check_examples
82 |
83 | help: ## This help.
84 | @awk 'BEGIN {FS = ":.*?## "} /^[a-zA-Z_-]+:.*?## / {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' $(MAKEFILE_LIST)
85 |
--------------------------------------------------------------------------------
/Makefile.inc:
--------------------------------------------------------------------------------
1 | SHELL = /bin/bash
2 | GIT_VERSION := $(shell git describe --tags --long --always)
3 | CURRENT_USER_UID := $(shell id -u)
4 | CURRENT_USER_GID := $(shell id -g)
5 | CURRENT_PWD := $(shell pwd)
6 |
7 | ifeq ($(findstring dev-,$(MAKECMDGOALS)),)
8 | BUILDID := $(shell date +"%Y-%m-%d_%H_%M_%S_%N")
9 | else
10 | BUILDID := DEV
11 | endif
12 |
13 | FULL = false
14 | OFFLINE = false
15 | IINKAPILOCAL = false
16 | CDKAPILOCAL = false
17 | DEVLOCAL = false
18 | CURRENT_DIR := $(shell dirname $(realpath $(lastword $(MAKEFILE_LIST))))
19 | PROJECT_DIR ?= $(CURRENT_DIR)
20 | GIT_BRANCH := $(shell git rev-parse --abbrev-ref HEAD)
21 |
22 | MAKE := $(MAKE) PROJECT_DIR=$(PROJECT_DIR) BUILDID=$(BUILDID) --no-print-directory
23 | NPM_CACHE = $(HOME)/.npm
24 |
25 | DOCKERTAG := master
26 | GIT_TAG = 2.0.0
27 |
28 | REGISTRY = registry.corp.myscript.com:5000
29 | DOC_DOCKERREPOSITORY = $(REGISTRY)/iinkjs-docs:$(DOCKERTAG)
30 | EXAMPLES_DOCKERREPOSITORY = $(REGISTRY)/iinkjs-examples:$(DOCKERTAG)
31 |
32 | CONFIGURATION_DOCKERTAG := 1.0.0
33 | MOCHA_DOCKERREPOSITORY = $(REGISTRY)/iink-js-mocha:$(CONFIGURATION_DOCKERTAG)
34 | WAITTCP_DOCKERREPOSITORY = $(REGISTRY)/iink-js-wait-tcp:$(CONFIGURATION_DOCKERTAG)
35 |
36 | BUILDENV := test
37 | TEST_DOCKER_NAME_PREFIX := iinkjs-$(DOCKERTAG)-$(BUILDENV)-$(BUILDID)
38 | TEST_DOCKER_EXAMPLES_INSTANCE_NAME := $(TEST_DOCKER_NAME_PREFIX)-examples
39 |
40 | APPLICATIONKEY := 7d223f9e-a3cb-4213-ba4b-85e930605f8b
41 | HMACKEY := 5ab1935e-529a-4d48-a695-158450e52b13
42 |
43 | DEV_APPLICATIONKEY := 515131ab-35fa-411c-bb4d-3917e00faf60
44 | DEV_HMACKEY := 54b2ca8a-6752-469d-87dd-553bb450e9ad
45 |
46 | APIHOST := cloud-internal-master.corp.myscript.com
47 | APISCHEME := https
48 | ifeq ($(CDKAPILOCAL),true)
49 | APIHOST := localhost:8894
50 | APISCHEME := http
51 | endif
52 | ifeq ($(IINKAPILOCAL),true)
53 | APIHOST := localhost:8897
54 | APISCHEME := http
55 | endif
56 |
57 | ifeq ($(DEVLOCAL),true)
58 | DOCKER_EXAMPLES_PARAMETERS := --net=host --userns=host
59 | EXAMPLES_LISTEN_PORT := 8080
60 | else
61 | EXAMPLES_LISTEN_PORT := 80
62 | endif
63 |
--------------------------------------------------------------------------------
/SETUP.md:
--------------------------------------------------------------------------------
1 | # Environment setup
2 |
3 | ## Configure project
4 |
5 | 1. Download sources
6 | 2. Install dependencies.
7 | * `npm install`
8 | 3. Build the project using our npm script.
9 | * `npm run build`
10 | 4. Run the server and the live reload using our npm script.
11 | * `npm run dev`. Examples will be available on `http://localhost:8080/examples/index.html`
12 |
13 | **Start coding**
14 |
15 | 5. Debug using your favorite browser dev tools. The sources will be available under the webpack source folder (for chrome dev tools). Every change in sources will trigger a rebuild with linter and basic tests.
16 |
17 | ## Set up your IDE
18 |
19 | At MyScript, we use WebStorm to develop our library. You can find below some help to configure WebStorm like we do. Obviously, feel free to use any code editor as the configuration can be adapted for any editor.
20 |
21 | ### WebStorm
22 |
23 | Configure all the librairies to have a good code completion [https://blog.jetbrains.com/webstorm/2014/07/how-webstorm-works-completion-for-javascript-libraries/](https://blog.jetbrains.com/webstorm/2014/07/how-webstorm-works-completion-for-javascript-libraries/)
24 |
25 | Configure the code format by going in Files -> Settings the Editor -> Code Style -> Javascript and press the button manage. Load the configuration file locate in ./dev/AIRBNB. This will allow you to reformat the javascript code with IDEA formatter as expected by most of configured ES6 rules.
26 |
27 | Debug mocha test
28 | - Add a mocha test configuration with test directory
29 | - Configure the launcher with the extra mocha option `--compilers js:babel-core/register`
30 |
31 | Activate ESLint checks [https://www.jetbrains.com/help/webstorm/2016.2/eslint.html](https://www.jetbrains.com/help/webstorm/2016.2/eslint.html) and use the automatic search option.
32 |
--------------------------------------------------------------------------------
/assets/angular.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/assets/react.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
6 |
7 |
31 |
32 |
33 |
34 |
35 |
36 |
--------------------------------------------------------------------------------
/assets/vue.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/config/rollup.config.build.js:
--------------------------------------------------------------------------------
1 | import config from './rollup.config'
2 |
3 | export default config
4 |
--------------------------------------------------------------------------------
/config/rollup.config.dev.js:
--------------------------------------------------------------------------------
1 | import serve from 'rollup-plugin-serve'
2 | import livereload from 'rollup-plugin-livereload'
3 | import config from './rollup.config'
4 |
5 | config[0].plugins.push(
6 | serve({
7 | open: true,
8 | verbose: true,
9 | contentBase: '',
10 | host: 'localhost',
11 | port: 8080,
12 | headers: {
13 | 'Access-Control-Allow-Origin': '*'
14 | }
15 | }),
16 | livereload({
17 | watch: [
18 | 'dist',
19 | 'examples'
20 | ]
21 | })
22 | )
23 |
24 | config.watch = {
25 | include: 'src/**'
26 | }
27 |
28 | export default config
29 |
--------------------------------------------------------------------------------
/config/rollup.config.js:
--------------------------------------------------------------------------------
1 | import json from '@rollup/plugin-json'
2 | import resolve from '@rollup/plugin-node-resolve'
3 | import commonjs from 'rollup-plugin-commonjs'
4 | import babel from '@rollup/plugin-babel'
5 | import { terser } from 'rollup-plugin-terser'
6 | import postcss from 'rollup-plugin-postcss'
7 | import toImport from 'postcss-import'
8 |
9 | const plugins = [
10 | json(),
11 | resolve(),
12 | commonjs({
13 | namedExports: {
14 | 'node_modules/loglevel/lib/loglevel.js': ['noConflict']
15 | }
16 | }),
17 | babel({
18 | exclude: 'node_modules/**',
19 | babelrc: false,
20 | babelHelpers: 'runtime',
21 | presets: [
22 | ['@babel/env']
23 | ],
24 | plugins: [
25 | '@babel/transform-runtime'
26 | ]
27 | }),
28 | terser({
29 | keep_fnames: true
30 | }),
31 | postcss({
32 | plugins: [toImport],
33 | inject: false
34 | })
35 | ]
36 |
37 | export default [{
38 | input: 'src/iink.js',
39 | output: [
40 | {
41 | name: 'iink',
42 | file: 'dist/iink.min.js',
43 | format: 'umd',
44 | exports: 'named'
45 | }
46 | ],
47 | plugins
48 | }, {
49 | input: 'src/iink.js',
50 | output: [
51 | {
52 | file: 'dist/iink.esm.js',
53 | format: 'es'
54 | }
55 | ],
56 | plugins
57 | }]
58 |
--------------------------------------------------------------------------------
/docker/builder/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM debian:bullseye-slim
2 | ENV DEBIAN_FRONTEND noninteractive
3 | RUN apt update -yyq;
4 | RUN apt install make npm yarn git curl -yyq
5 | RUN curl -fsSL https://get.docker.com -o get-docker.sh && sh ./get-docker.sh
6 |
--------------------------------------------------------------------------------
/docker/examples/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM nginx:stable-alpine
2 |
3 | COPY nginx.conf /etc/nginx/nginx.conf
4 | COPY delivery /usr/share/nginx/html
5 | COPY *.sh /
6 | RUN chmod a+x /entrypoint.sh
7 |
8 | RUN /createIndexFile.sh
9 |
10 | ENV LISTEN_PORT 80
11 | ENV APISCHEME "https"
12 | ENV APIHOST "webdemoapi.myscript.com"
13 | ARG applicationkey
14 | ARG hmackey
15 | ENV DEV_APPLICATIONKEY=$applicationkey
16 | ENV DEV_HMACKEY=$hmackey
17 |
18 | ENTRYPOINT ["/entrypoint.sh"]
19 |
--------------------------------------------------------------------------------
/docker/examples/createIndexFile.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | set -e
4 |
5 | echo "
6 |
7 | iinkJS examples
8 |
9 |
10 |
11 |
12 |
13 | " > /usr/share/nginx/html/index.html
14 |
--------------------------------------------------------------------------------
/docker/examples/entrypoint.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | set -e
4 |
5 | echo "Running nginx"
6 | echo "========================"
7 | echo "LISTEN_PORT=>>${LISTEN_PORT}<<"
8 | echo "APIHOST=${APIHOST}"
9 | echo "APISCHEME=${APISCHEME}"
10 | echo "APPLICATIONKEY=${DEV_APPLICATIONKEY}"
11 | echo "HMACKEY=${DEV_HMACKEY}"
12 |
13 |
14 | sed -i -e "s/\(listen\s\+\)\(80;\)/\1${LISTEN_PORT};/g" /etc/nginx/conf.d/default.conf
15 | cat /etc/nginx/conf.d/default.conf
16 |
17 | for filename in /usr/share/nginx/html/examples/**/*.*; do
18 | sed -i "s/scheme: 'https'/scheme: '${APISCHEME}'/g" "${filename}"
19 |
20 | sed -i "s/webdemoapi.myscript.com/${APIHOST}/g" "${filename}"
21 | sed -i "s/newcloud.myscript.com/${APIHOST}/g" "${filename}"
22 |
23 | sed -i "s/515131ab-35fa-411c-bb4d-3917e00faf60/${DEV_APPLICATIONKEY}/g" "${filename}"
24 | sed -i "s/54b2ca8a-6752-469d-87dd-553bb450e9ad/${DEV_HMACKEY}/g" "${filename}"
25 |
26 | sed -i "s/7d223f9e-a3cb-4213-ba4b-85e930605f8b/${DEV_APPLICATIONKEY}/g" "${filename}"
27 | sed -i "s/5ab1935e-529a-4d48-a695-158450e52b13/${DEV_HMACKEY}/g" "${filename}"
28 | done
29 |
30 | nginx
31 |
--------------------------------------------------------------------------------
/docker/examples/nginx.conf:
--------------------------------------------------------------------------------
1 | user nginx;
2 | worker_processes 1;
3 | daemon off;
4 | error_log /var/log/nginx/error.log warn;
5 | pid /var/run/nginx.pid;
6 |
7 |
8 | events {
9 | worker_connections 1024;
10 | }
11 |
12 |
13 | http {
14 | include /etc/nginx/mime.types;
15 | default_type application/octet-stream;
16 |
17 | log_format main '$remote_addr - $remote_user [$time_local] "$request" '
18 | '$status $body_bytes_sent "$http_referer" '
19 | '"$http_user_agent" "$http_x_forwarded_for"';
20 |
21 | access_log /var/log/nginx/access.log main;
22 |
23 | sendfile off;
24 | expires 0;
25 | autoindex on;
26 | #tcp_nopush on;
27 |
28 | keepalive_timeout 65;
29 |
30 | #gzip on;
31 |
32 | include /etc/nginx/conf.d/*.conf;
33 | }
34 |
--------------------------------------------------------------------------------
/docker/wait-tcp/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM debian:buster-slim
2 | ENV DEBIAN_FRONTEND noninteractive
3 |
4 | RUN apt update -yy;apt install -yy curl jq netcat-openbsd;rm -rf /var/lib/apt/* /var/cache/apt/*
5 |
6 | ENV WAIT_SERVICE "Undef"
7 | ENV WAIT_PORT 80
8 | ENV WAIT_HOST WAITHOST
9 | ENV TIMEOUT 90
10 | ENV DEBUG false
11 | ADD /entrypoint.sh /entrypoint.sh
12 |
13 | ENTRYPOINT ["/entrypoint.sh"]
14 |
--------------------------------------------------------------------------------
/docker/wait-tcp/entrypoint.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | if [ ${DEBUG} != "false" ]; then
4 | set -xve
5 | fi
6 |
7 | echo -n " => Waiting for ${WAIT_SERVICE} on tcp Port ${WAIT_PORT}"
8 | CPT=0
9 | while ! nc -z -w 1 ${WAIT_HOST} ${WAIT_PORT}; do
10 | ((CPT++))
11 | if [ ${CPT} -gt ${TIMEOUT} ]; then
12 | echo " Timeout!"
13 | exit 1
14 | fi;
15 | sleep 1
16 | echo -n .
17 | done
18 | echo " Found!"
19 |
--------------------------------------------------------------------------------
/docs/fonts/OpenSans-Bold-webfont.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MyScript/iinkJS/5e55c677617f56edde2877cf4c31231cf970c790/docs/fonts/OpenSans-Bold-webfont.eot
--------------------------------------------------------------------------------
/docs/fonts/OpenSans-Bold-webfont.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MyScript/iinkJS/5e55c677617f56edde2877cf4c31231cf970c790/docs/fonts/OpenSans-Bold-webfont.woff
--------------------------------------------------------------------------------
/docs/fonts/OpenSans-BoldItalic-webfont.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MyScript/iinkJS/5e55c677617f56edde2877cf4c31231cf970c790/docs/fonts/OpenSans-BoldItalic-webfont.eot
--------------------------------------------------------------------------------
/docs/fonts/OpenSans-BoldItalic-webfont.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MyScript/iinkJS/5e55c677617f56edde2877cf4c31231cf970c790/docs/fonts/OpenSans-BoldItalic-webfont.woff
--------------------------------------------------------------------------------
/docs/fonts/OpenSans-Italic-webfont.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MyScript/iinkJS/5e55c677617f56edde2877cf4c31231cf970c790/docs/fonts/OpenSans-Italic-webfont.eot
--------------------------------------------------------------------------------
/docs/fonts/OpenSans-Italic-webfont.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MyScript/iinkJS/5e55c677617f56edde2877cf4c31231cf970c790/docs/fonts/OpenSans-Italic-webfont.woff
--------------------------------------------------------------------------------
/docs/fonts/OpenSans-Light-webfont.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MyScript/iinkJS/5e55c677617f56edde2877cf4c31231cf970c790/docs/fonts/OpenSans-Light-webfont.eot
--------------------------------------------------------------------------------
/docs/fonts/OpenSans-Light-webfont.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MyScript/iinkJS/5e55c677617f56edde2877cf4c31231cf970c790/docs/fonts/OpenSans-Light-webfont.woff
--------------------------------------------------------------------------------
/docs/fonts/OpenSans-LightItalic-webfont.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MyScript/iinkJS/5e55c677617f56edde2877cf4c31231cf970c790/docs/fonts/OpenSans-LightItalic-webfont.eot
--------------------------------------------------------------------------------
/docs/fonts/OpenSans-LightItalic-webfont.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MyScript/iinkJS/5e55c677617f56edde2877cf4c31231cf970c790/docs/fonts/OpenSans-LightItalic-webfont.woff
--------------------------------------------------------------------------------
/docs/fonts/OpenSans-Regular-webfont.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MyScript/iinkJS/5e55c677617f56edde2877cf4c31231cf970c790/docs/fonts/OpenSans-Regular-webfont.eot
--------------------------------------------------------------------------------
/docs/fonts/OpenSans-Regular-webfont.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MyScript/iinkJS/5e55c677617f56edde2877cf4c31231cf970c790/docs/fonts/OpenSans-Regular-webfont.woff
--------------------------------------------------------------------------------
/docs/fonts/OpenSans-Semibold-webfont.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MyScript/iinkJS/5e55c677617f56edde2877cf4c31231cf970c790/docs/fonts/OpenSans-Semibold-webfont.eot
--------------------------------------------------------------------------------
/docs/fonts/OpenSans-Semibold-webfont.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MyScript/iinkJS/5e55c677617f56edde2877cf4c31231cf970c790/docs/fonts/OpenSans-Semibold-webfont.ttf
--------------------------------------------------------------------------------
/docs/fonts/OpenSans-Semibold-webfont.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MyScript/iinkJS/5e55c677617f56edde2877cf4c31231cf970c790/docs/fonts/OpenSans-Semibold-webfont.woff
--------------------------------------------------------------------------------
/docs/fonts/OpenSans-SemiboldItalic-webfont.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MyScript/iinkJS/5e55c677617f56edde2877cf4c31231cf970c790/docs/fonts/OpenSans-SemiboldItalic-webfont.eot
--------------------------------------------------------------------------------
/docs/fonts/OpenSans-SemiboldItalic-webfont.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MyScript/iinkJS/5e55c677617f56edde2877cf4c31231cf970c790/docs/fonts/OpenSans-SemiboldItalic-webfont.ttf
--------------------------------------------------------------------------------
/docs/fonts/OpenSans-SemiboldItalic-webfont.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MyScript/iinkJS/5e55c677617f56edde2877cf4c31231cf970c790/docs/fonts/OpenSans-SemiboldItalic-webfont.woff
--------------------------------------------------------------------------------
/docs/scripts/linenumber.js:
--------------------------------------------------------------------------------
1 | /*global document */
2 | (function() {
3 | var source = document.getElementsByClassName('prettyprint source linenums');
4 | var i = 0;
5 | var lineNumber = 0;
6 | var lineId;
7 | var lines;
8 | var totalLines;
9 | var anchorHash;
10 |
11 | if (source && source[0]) {
12 | anchorHash = document.location.hash.substring(1);
13 | lines = source[0].getElementsByTagName('li');
14 | totalLines = lines.length;
15 |
16 | for (; i < totalLines; i++) {
17 | lineNumber++;
18 | lineId = 'line' + lineNumber;
19 | lines[i].id = lineId;
20 | if (lineId === anchorHash) {
21 | lines[i].className += ' selected';
22 | }
23 | }
24 | }
25 | })();
26 |
--------------------------------------------------------------------------------
/docs/scripts/prettify/lang-css.js:
--------------------------------------------------------------------------------
1 | PR.registerLangHandler(PR.createSimpleLexer([["pln",/^[\t\n\f\r ]+/,null," \t\r\n"]],[["str",/^"(?:[^\n\f\r"\\]|\\(?:\r\n?|\n|\f)|\\[\S\s])*"/,null],["str",/^'(?:[^\n\f\r'\\]|\\(?:\r\n?|\n|\f)|\\[\S\s])*'/,null],["lang-css-str",/^url\(([^"')]*)\)/i],["kwd",/^(?:url|rgb|!important|@import|@page|@media|@charset|inherit)(?=[^\w-]|$)/i,null],["lang-css-kw",/^(-?(?:[_a-z]|\\[\da-f]+ ?)(?:[\w-]|\\\\[\da-f]+ ?)*)\s*:/i],["com",/^\/\*[^*]*\*+(?:[^*/][^*]*\*+)*\//],["com",
2 | /^(?:<\!--|--\>)/],["lit",/^(?:\d+|\d*\.\d+)(?:%|[a-z]+)?/i],["lit",/^#[\da-f]{3,6}/i],["pln",/^-?(?:[_a-z]|\\[\da-f]+ ?)(?:[\w-]|\\\\[\da-f]+ ?)*/i],["pun",/^[^\s\w"']+/]]),["css"]);PR.registerLangHandler(PR.createSimpleLexer([],[["kwd",/^-?(?:[_a-z]|\\[\da-f]+ ?)(?:[\w-]|\\\\[\da-f]+ ?)*/i]]),["css-kw"]);PR.registerLangHandler(PR.createSimpleLexer([],[["str",/^[^"')]+/]]),["css-str"]);
3 |
--------------------------------------------------------------------------------
/docs/styles/prettify-jsdoc.css:
--------------------------------------------------------------------------------
1 | /* JSDoc prettify.js theme */
2 |
3 | /* plain text */
4 | .pln {
5 | color: #000000;
6 | font-weight: normal;
7 | font-style: normal;
8 | }
9 |
10 | /* string content */
11 | .str {
12 | color: hsl(104, 100%, 24%);
13 | font-weight: normal;
14 | font-style: normal;
15 | }
16 |
17 | /* a keyword */
18 | .kwd {
19 | color: #000000;
20 | font-weight: bold;
21 | font-style: normal;
22 | }
23 |
24 | /* a comment */
25 | .com {
26 | font-weight: normal;
27 | font-style: italic;
28 | }
29 |
30 | /* a type name */
31 | .typ {
32 | color: #000000;
33 | font-weight: normal;
34 | font-style: normal;
35 | }
36 |
37 | /* a literal value */
38 | .lit {
39 | color: #006400;
40 | font-weight: normal;
41 | font-style: normal;
42 | }
43 |
44 | /* punctuation */
45 | .pun {
46 | color: #000000;
47 | font-weight: bold;
48 | font-style: normal;
49 | }
50 |
51 | /* lisp open bracket */
52 | .opn {
53 | color: #000000;
54 | font-weight: bold;
55 | font-style: normal;
56 | }
57 |
58 | /* lisp close bracket */
59 | .clo {
60 | color: #000000;
61 | font-weight: bold;
62 | font-style: normal;
63 | }
64 |
65 | /* a markup tag name */
66 | .tag {
67 | color: #006400;
68 | font-weight: normal;
69 | font-style: normal;
70 | }
71 |
72 | /* a markup attribute name */
73 | .atn {
74 | color: #006400;
75 | font-weight: normal;
76 | font-style: normal;
77 | }
78 |
79 | /* a markup attribute value */
80 | .atv {
81 | color: #006400;
82 | font-weight: normal;
83 | font-style: normal;
84 | }
85 |
86 | /* a declaration */
87 | .dec {
88 | color: #000000;
89 | font-weight: bold;
90 | font-style: normal;
91 | }
92 |
93 | /* a variable name */
94 | .var {
95 | color: #000000;
96 | font-weight: normal;
97 | font-style: normal;
98 | }
99 |
100 | /* a function name */
101 | .fun {
102 | color: #000000;
103 | font-weight: bold;
104 | font-style: normal;
105 | }
106 |
107 | /* Specify class=linenums on a pre to get line numbering */
108 | ol.linenums {
109 | margin-top: 0;
110 | margin-bottom: 0;
111 | }
112 |
--------------------------------------------------------------------------------
/docs/styles/prettify-tomorrow.css:
--------------------------------------------------------------------------------
1 | /* Tomorrow Theme */
2 | /* Original theme - https://github.com/chriskempson/tomorrow-theme */
3 | /* Pretty printing styles. Used with prettify.js. */
4 | /* SPAN elements with the classes below are added by prettyprint. */
5 | /* plain text */
6 | .pln {
7 | color: #4d4d4c; }
8 |
9 | @media screen {
10 | /* string content */
11 | .str {
12 | color: hsl(104, 100%, 24%); }
13 |
14 | /* a keyword */
15 | .kwd {
16 | color: hsl(240, 100%, 50%); }
17 |
18 | /* a comment */
19 | .com {
20 | color: hsl(0, 0%, 60%); }
21 |
22 | /* a type name */
23 | .typ {
24 | color: hsl(240, 100%, 32%); }
25 |
26 | /* a literal value */
27 | .lit {
28 | color: hsl(240, 100%, 40%); }
29 |
30 | /* punctuation */
31 | .pun {
32 | color: #000000; }
33 |
34 | /* lisp open bracket */
35 | .opn {
36 | color: #000000; }
37 |
38 | /* lisp close bracket */
39 | .clo {
40 | color: #000000; }
41 |
42 | /* a markup tag name */
43 | .tag {
44 | color: #c82829; }
45 |
46 | /* a markup attribute name */
47 | .atn {
48 | color: #f5871f; }
49 |
50 | /* a markup attribute value */
51 | .atv {
52 | color: #3e999f; }
53 |
54 | /* a declaration */
55 | .dec {
56 | color: #f5871f; }
57 |
58 | /* a variable name */
59 | .var {
60 | color: #c82829; }
61 |
62 | /* a function name */
63 | .fun {
64 | color: #4271ae; } }
65 | /* Use higher contrast and text-weight for printable form. */
66 | @media print, projection {
67 | .str {
68 | color: #060; }
69 |
70 | .kwd {
71 | color: #006;
72 | font-weight: bold; }
73 |
74 | .com {
75 | color: #600;
76 | font-style: italic; }
77 |
78 | .typ {
79 | color: #404;
80 | font-weight: bold; }
81 |
82 | .lit {
83 | color: #044; }
84 |
85 | .pun, .opn, .clo {
86 | color: #440; }
87 |
88 | .tag {
89 | color: #006;
90 | font-weight: bold; }
91 |
92 | .atn {
93 | color: #404; }
94 |
95 | .atv {
96 | color: #060; } }
97 | /* Style */
98 | /*
99 | pre.prettyprint {
100 | background: white;
101 | font-family: Consolas, Monaco, 'Andale Mono', monospace;
102 | font-size: 12px;
103 | line-height: 1.5;
104 | border: 1px solid #ccc;
105 | padding: 10px; }
106 | */
107 |
108 | /* Specify class=linenums on a pre to get line numbering */
109 | ol.linenums {
110 | margin-top: 0;
111 | margin-bottom: 0; }
112 |
113 | /* IE indents via margin-left */
114 | li.L0,
115 | li.L1,
116 | li.L2,
117 | li.L3,
118 | li.L4,
119 | li.L5,
120 | li.L6,
121 | li.L7,
122 | li.L8,
123 | li.L9 {
124 | /* */ }
125 |
126 | /* Alternate shading for lines */
127 | li.L1,
128 | li.L3,
129 | li.L5,
130 | li.L7,
131 | li.L9 {
132 | /* */ }
133 |
--------------------------------------------------------------------------------
/examples/assets/img/clear.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Group 11 Copy 3
5 | Created with Sketch.
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/examples/assets/img/document.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
--------------------------------------------------------------------------------
/examples/assets/img/edit.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
--------------------------------------------------------------------------------
/examples/assets/img/eraser.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
10 |
11 |
--------------------------------------------------------------------------------
/examples/assets/img/pen.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
9 |
10 |
--------------------------------------------------------------------------------
/examples/assets/img/plus.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
5 |
6 |
8 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
--------------------------------------------------------------------------------
/examples/assets/img/redo.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/examples/assets/img/undo.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/examples/dev/debug.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 | Debug
12 |
13 |
14 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
113 |
114 |
115 |
--------------------------------------------------------------------------------
/examples/experimental/websocket_diagram_iink.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 | WEBSOCKET Diagram iink
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
34 |
35 | Convert
36 |
37 |
38 |
39 |
107 |
108 |
109 |
--------------------------------------------------------------------------------
/examples/experimental/websocket_text_document_iink.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 | WEBSOCKET Nebo iink
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 | Convert
28 |
29 |
30 |
31 |
106 |
107 |
108 |
--------------------------------------------------------------------------------
/examples/non-version-specific/handle_errors.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 | Handle error
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
24 |
51 |
52 |
53 |
--------------------------------------------------------------------------------
/examples/non-version-specific/on_demand_exports.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 | On-demand exports iink
12 |
13 |
14 |
15 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
42 |
43 | Export
44 |
45 |
46 |
47 |
111 |
112 |
113 |
114 |
--------------------------------------------------------------------------------
/examples/v4/diagram.css:
--------------------------------------------------------------------------------
1 | nav {
2 | background-color: white;
3 | border-top: none;
4 | flex-wrap: wrap-reverse;
5 | }
6 |
7 | .nav-group {
8 | overflow: auto;
9 | display: flex;
10 | align-items: center;
11 | }
12 |
13 | #progress {
14 | transition: width 10s;
15 | width: 0;
16 | height: 2px;
17 | background-color: #1A9FFF;
18 | opacity: 0;
19 | }
20 |
21 | .classic-btn {
22 | margin-left: 12px;
23 | display: flex;
24 | align-items: center;
25 | height: 38px;
26 | }
27 |
28 | .classic-btn > img {
29 | margin-right: 12px;
30 | height: 24px;
31 | }
32 |
33 | .delete {
34 | -webkit-touch-callout: none;
35 | -webkit-user-select: none;
36 | -moz-user-select: none;
37 | -ms-user-select: none;
38 | user-select: none;
39 | -moz-appearance: none;
40 | -webkit-appearance: none;
41 | background-color: rgba(10, 10, 10, 0.2);
42 | border: none;
43 | border-radius: 290486px;
44 | cursor: pointer;
45 | display: inline-block;
46 | -webkit-box-flex: 0;
47 | -ms-flex-positive: 0;
48 | flex-grow: 0;
49 | -ms-flex-negative: 0;
50 | flex-shrink: 0;
51 | font-size: 0;
52 | height: 20px;
53 | max-height: 20px;
54 | max-width: 20px;
55 | min-height: 20px;
56 | min-width: 20px;
57 | outline: none;
58 | vertical-align: top;
59 | width: 20px;
60 | position: absolute;
61 | right: 0.5rem;
62 | top: 0.5rem;
63 | }
64 |
65 | .delete:before, .delete:after {
66 | background-color: white;
67 | content: "";
68 | display: block;
69 | left: 50%;
70 | position: absolute;
71 | top: 50%;
72 | -webkit-transform: translateX(-50%) translateY(-50%) rotate(45deg);
73 | transform: translateX(-50%) translateY(-50%) rotate(45deg);
74 | -webkit-transform-origin: center center;
75 | transform-origin: center center;
76 | }
77 |
78 | .delete:before {
79 | height: 2px;
80 | width: 50%;
81 | }
82 |
83 | .delete:after {
84 | height: 50%;
85 | width: 2px;
86 | }
87 |
88 | .delete:hover, .delete:focus {
89 | background-color: rgba(10, 10, 10, 0.3);
90 | }
91 |
92 | .delete:active {
93 | background-color: rgba(10, 10, 10, 0.4);
94 | }
95 |
96 |
--------------------------------------------------------------------------------
/examples/v4/websocket_math_custom_resources_compiled.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 | Custom resources math (pre-compiled)
12 |
13 |
14 |
15 |
16 |
17 |
18 |
23 |
24 |
25 |
26 | This example use a math custom resource. The grammar restrict the recognition to only basic elementary school math (addition and substraction). The id of a pre-compiled grammar is sent.
27 |
28 |
29 |
30 |
41 |
42 | Convert
43 |
44 |
45 |
46 |
110 |
111 |
112 |
--------------------------------------------------------------------------------
/examples/v4/websocket_text_custom_lexicon.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 | Custom lexicon
12 |
13 |
14 |
15 |
16 |
17 |
18 |
23 |
24 |
25 |
26 |
27 |
28 |
36 |
37 |
38 | Send
39 |
40 |
41 |
42 |
105 |
106 |
107 |
--------------------------------------------------------------------------------
/examples/v4/websocket_text_custom_resources.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 | Custom pre-loaded resources
12 |
13 |
14 |
15 |
16 |
17 |
18 |
27 |
28 |
29 |
30 |
31 |
32 |
40 | Custom pre-loaded resources: Dinosaurs . Try to write some dinosaurs name!
41 |
42 |
43 |
44 |
92 |
93 |
94 |
--------------------------------------------------------------------------------
/examples/v4/websocket_text_customize_editor_css.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 | Styling editor style
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
34 |
90 |
91 |
92 |
--------------------------------------------------------------------------------
/examples/v4/websocket_text_iink.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 | WEBSOCKET Text iink
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
30 |
31 |
32 |
33 |
34 |
105 |
106 |
107 |
--------------------------------------------------------------------------------
/examples/v4/websocket_text_iink_import_jiix.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 | Import jiix
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
30 |
31 |
32 | Import
33 |
34 |
35 |
36 |
112 |
113 |
114 |
--------------------------------------------------------------------------------
/examples/v4/websocket_text_iink_no_guides.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 | No guides
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
33 |
80 |
81 |
82 |
--------------------------------------------------------------------------------
/examples/v4/websocket_text_import_content.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 | Import content
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
30 |
31 |
32 | Import
33 |
34 |
35 |
36 |
79 |
80 |
81 |
--------------------------------------------------------------------------------
/examples/v4/websocket_text_local_storage_text.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 | WEBSOCKET Text iink
12 |
13 |
14 |
15 |
16 |
17 |
30 |
31 |
32 |
33 |
34 |
35 |
43 | Clear local storage
44 |
45 |
46 |
47 |
102 |
103 |
104 |
--------------------------------------------------------------------------------
/examples/v4/websocket_text_multiple_inputs.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 | Multiple inputs
12 |
13 |
14 |
15 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 | Convert
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
--------------------------------------------------------------------------------
/examples/v4/websocket_text_pointer_events.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 | Pointer events
12 |
13 |
14 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
58 |
Process
59 |
60 |
90 |
91 |
92 |
93 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Title
6 |
7 |
8 |
9 |
10 |
13 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "iink-js",
3 | "version": "2.0.2",
4 | "main": "dist/iink.min.js",
5 | "module": "dist/iink.esm.js",
6 | "description": "iinkJS is the fastest way to integrate handwriting panel and recognition in your webapp",
7 | "keywords": [
8 | "myscript",
9 | "javascript",
10 | "developer",
11 | "handwriting",
12 | "recognition",
13 | "cloud",
14 | "iink"
15 | ],
16 | "files": [
17 | "dist"
18 | ],
19 | "license": "Apache-2.0",
20 | "homepage": "https://myscript.github.io/iinkJS/",
21 | "repository": {
22 | "type": "git",
23 | "url": "git://github.com/MyScript/iinkJS.git"
24 | },
25 | "dependencies": {
26 | "@babel/runtime": "^7.9.2",
27 | "clipboard": "^1.7.1",
28 | "crypto-js": "^3.3.0",
29 | "d3-selection": "^1.4.1",
30 | "json-css": "^1.5.6",
31 | "lodash.merge": "^4.6.2",
32 | "loglevel": "^1.6.8",
33 | "perfect-scrollbar": "^1.5.0",
34 | "uuid-js": "^0.7.5"
35 | },
36 | "devDependencies": {
37 | "@babel/cli": "^7.8.4",
38 | "@babel/core": "^7.9.0",
39 | "@babel/plugin-transform-runtime": "^7.9.0",
40 | "@babel/preset-env": "^7.9.5",
41 | "@babel/register": "^7.9.0",
42 | "@rollup/plugin-babel": "^5.0.0",
43 | "@rollup/plugin-json": "^4.0.2",
44 | "@rollup/plugin-node-resolve": "^6.1.0",
45 | "chai": "^4.3.4",
46 | "clean-css-cli": "^4.3.0",
47 | "eslint": "^6.8.0",
48 | "eslint-config-standard": "^14.1.1",
49 | "eslint-plugin-import": "^2.20.2",
50 | "eslint-plugin-node": "^11.1.0",
51 | "eslint-plugin-promise": "^4.2.1",
52 | "eslint-plugin-standard": "^4.0.1",
53 | "minami": "^1.2.3",
54 | "mocha": "^9.0.3",
55 | "mock-css-modules": "^2.0.0",
56 | "npm-run-all": "^4.1.5",
57 | "playwright": "1.16.0",
58 | "postcss-import": "^12.0.1",
59 | "rollup": "^2.18.0",
60 | "rollup-plugin-commonjs": "^10.1.0",
61 | "rollup-plugin-livereload": "^1.2.0",
62 | "rollup-plugin-postcss": "^2.6.2",
63 | "rollup-plugin-progress": "^1.1.1",
64 | "rollup-plugin-serve": "^1.0.1",
65 | "rollup-plugin-terser": "^7.0.1",
66 | "sinon": "^2.4.1",
67 | "taffydb": "^2.7.3"
68 | },
69 | "scripts": {
70 | "lint": "eslint --ext js src test examples",
71 | "lint:fix": "eslint --ext js src test examples --fix",
72 | "docs": "npx jsdoc -R README.md -P '' -d docs -t node_modules/minami -r src",
73 | "minify-css": "cleancss -o dist/iink.min.css src/*.css",
74 | "test:mocha": "mocha --require @babel/register,mock-css-modules --recursive test/mocha/ --reporter progress",
75 | "test:mocha-xunit": "mocha --require @babel/register --recursive test/mocha/ --reporter xunit --reporter-options output=./test/mocha/results/xunit.xml",
76 | "test:e2e": "mocha --config .mocharc.e2e.js",
77 | "test:chromium": "BROWSER=chromium mocha --config .mocharc.e2e.js",
78 | "test:webkit": "BROWSER=webkit mocha --config .mocharc.e2e.js",
79 | "test:firefox": "BROWSER=firefox mocha --config .mocharc.e2e.js",
80 | "build:js": "rollup -c config/rollup.config.build.js --sourcemap",
81 | "build": "npm-run-all test:mocha lint build:js minify-css docs",
82 | "dev:js": "rollup -c config/rollup.config.dev.js -w --sourcemap",
83 | "dev": "npm-run-all minify-css dev:js",
84 | "start": "npm run dev"
85 | }
86 | }
87 |
--------------------------------------------------------------------------------
/preview.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MyScript/iinkJS/5e55c677617f56edde2877cf4c31231cf970c790/preview.gif
--------------------------------------------------------------------------------
/src/EditorFacade.js:
--------------------------------------------------------------------------------
1 | import { editorLogger as logger } from './configuration/LoggerConfig'
2 | import { Editor } from './Editor'
3 |
4 | /**
5 | * Attach an Editor to a DOMElement
6 | * @param {Element} element DOM element to attach an editor
7 | * @param {Configuration} [configuration] Configuration to apply
8 | * @param {PenStyle} [penStyle] Pen style to apply
9 | * @param {Theme} [theme] Theme to apply
10 | * @param {Behaviors} [behaviors] Custom behaviors to apply
11 | * @param {String} [globalClassCSS] Replace global class css 'ms-editor' to customize style
12 | * @return {Editor} New editor
13 | */
14 | export function register (element, configuration, penStyle, theme, behaviors, globalClassCSS) {
15 | logger.debug('Registering a new editor')
16 | return new Editor(element, configuration, penStyle, theme, behaviors, globalClassCSS)
17 | }
18 |
19 | /**
20 | * Return the list of available recognition languages
21 | * @param {Configuration} [configuration] Configuration to get the languages
22 | * @return {JSON} A list of available languages
23 | */
24 | export async function getAvailableLanguageList (configuration) {
25 | try {
26 | if (configuration && configuration.recognitionParams &&
27 | configuration.recognitionParams.server && configuration.recognitionParams.server.host) {
28 | const serverConfig = configuration.recognitionParams.server
29 | const response = await fetch(`${serverConfig.scheme}://${serverConfig.host}/api/v4.0/iink/availableLanguageList`)
30 | if (response && response.ok) {
31 | return response.json()
32 | }
33 | } else {
34 | console.error('Cannot get languages ! Please check your configuration!')
35 | }
36 | } catch (error) {
37 | console.error(error)
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/src/configuration/Constants.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @typedef {Object} Constants
3 | */
4 |
5 | const Constants = {
6 | EventType: {
7 | IDLE: 'idle',
8 | CHANGED: 'changed',
9 | IMPORTED: 'imported',
10 | EXPORTED: 'exported',
11 | CONVERTED: 'converted',
12 | RENDERED: 'rendered', // Internal use only
13 | LOADED: 'loaded',
14 | UNDO: 'undo',
15 | REDO: 'redo',
16 | CLEAR: 'clear',
17 | IMPORT: 'import',
18 | SUPPORTED_IMPORT_MIMETYPES: 'supportedImportMimeTypes',
19 | EXPORT: 'export',
20 | CONVERT: 'convert',
21 | ERROR: 'error'
22 | },
23 | RecognitionType: {
24 | TEXT: 'TEXT',
25 | MATH: 'MATH',
26 | DIAGRAM: 'DIAGRAM',
27 | RAWCONTENT: 'Raw Content'
28 | },
29 | Protocol: {
30 | WEBSOCKET: 'WEBSOCKET',
31 | REST: 'REST'
32 | },
33 | ModelState: {
34 | INITIALIZING: 'INITIALIZING',
35 | INITIALIZED: 'INITIALIZED',
36 | EXPORTING: 'EXPORTING',
37 | EXPORTED: 'EXPORTED',
38 | PENDING: 'PENDING',
39 | MODIFIED: 'MODIFIED',
40 | ERROR: 'ERROR'
41 | },
42 | Trigger: {
43 | QUIET_PERIOD: 'QUIET_PERIOD',
44 | POINTER_UP: 'POINTER_UP',
45 | DEMAND: 'DEMAND'
46 | },
47 | Logger: {
48 | EDITOR: 'editor',
49 | MODEL: 'model',
50 | GRABBER: 'grabber',
51 | RENDERER: 'renderer',
52 | RECOGNIZER: 'recognizer',
53 | EVENT: 'event',
54 | UTIL: 'util',
55 | SMARTGUIDE: 'smartguide'
56 | },
57 | LogLevel: {
58 | TRACE: 'TRACE',
59 | DEBUG: 'DEBUG',
60 | INFO: 'INFO',
61 | WARN: 'WARN',
62 | ERROR: 'ERROR'
63 | },
64 | Languages: {
65 | zh_CN: 'Noto Sans CJK tc',
66 | zh_HK: 'Noto Sans CJK tc',
67 | zh_TW: 'Noto Sans CJK tc',
68 | ko_KR: 'Noto Sans CJK kr',
69 | ja_JP: 'Noto Sans CJK jp',
70 | default: 'MyScriptInter'
71 | },
72 | Error: {
73 | NOT_REACHABLE: 'MyScript recognition server is not reachable. Please reload once you are connected.',
74 | WRONG_CREDENTIALS: 'Application credentials are invalid. Please check or regenerate your application key and hmackey.',
75 | TOO_OLD: 'Session is too old. Max Session Duration Reached.',
76 | NO_ACTIVITY: 'Session closed due to no activity.',
77 | CANT_ESTABLISH: 'Unable to establish a connection to the server. Check the host and your connectivity',
78 | CLOSE: 'Connection closed'
79 | },
80 | Exports: {
81 | JIIX: 'application/vnd.myscript.jiix'
82 | }
83 | }
84 | export default Constants
85 |
--------------------------------------------------------------------------------
/src/configuration/DefaultBehaviors.js:
--------------------------------------------------------------------------------
1 | import { editorLogger as logger } from './LoggerConfig'
2 | import * as PointerEventGrabber from '../grabber/PointerEventGrabber'
3 | import * as CanvasRenderer from '../renderer/canvas/CanvasRenderer'
4 | import * as QuadraticCanvasStroker from '../renderer/canvas/stroker/QuadraticCanvasStroker'
5 | import * as SVGRenderer from '../renderer/svg/SVGRenderer'
6 | import * as QuadraticSVGStroker from '../renderer/svg/stroker/QuadraticSVGStroker'
7 | import * as iinkRestRecognizer from '../recognizer/rest/iinkRestRecognizer'
8 | import * as iinkWsRecognizer from '../recognizer/websocket/iinkWsRecognizer'
9 | import emit from '../event/Event'
10 |
11 | /**
12 | * Current behavior
13 | * @typedef {Object} Behavior
14 | * @property {Grabber} grabber Grabber to capture strokes
15 | * @property {Stroker} stroker Stroker to draw stroke
16 | * @property {Renderer} renderer Renderer to draw on the editor
17 | * @property {Recognizer} recognizer Recognizer to call the recognition service
18 | * @property {Array} events Functions to handle model changes
19 | */
20 |
21 | /**
22 | * Set of behaviors to be used by the {@link Editor}
23 | * @typedef {Object} Behaviors
24 | * @property {Grabber} grabber Grabber to capture strokes
25 | * @property {Array} strokerList List of stroker to draw stroke
26 | * @property {Array} rendererList List of renderer to draw on the editor
27 | * @property {Array} recognizerList Recognizers to call the recognition service
28 | * @property {function} getBehaviorFromConfiguration Get the current behavior to use regarding the current configuration
29 | * @property {Array} events Functions to handle model changes
30 | */
31 |
32 | /**
33 | * Default behaviors
34 | * @type {Behaviors}
35 | */
36 | export const defaultBehaviors = {
37 | grabber: PointerEventGrabber,
38 | strokerList: [QuadraticCanvasStroker, QuadraticSVGStroker],
39 | rendererList: [CanvasRenderer, SVGRenderer],
40 | recognizerList: [iinkRestRecognizer, iinkWsRecognizer],
41 | events: emit,
42 | getBehaviorFromConfiguration: (behaviors, configuration) => {
43 | const behavior = {}
44 | behavior.grabber = behaviors.grabber
45 | if (configuration) {
46 | if (configuration.recognitionParams.protocol === 'REST') {
47 | behavior.stroker = QuadraticCanvasStroker
48 | behavior.renderer = CanvasRenderer
49 | behavior.recognizer = iinkRestRecognizer
50 | } else {
51 | behavior.stroker = QuadraticSVGStroker
52 | behavior.renderer = SVGRenderer
53 | behavior.recognizer = iinkWsRecognizer
54 | }
55 | }
56 | behavior.events = behaviors.events
57 | return behavior
58 | }
59 | }
60 |
61 | /**
62 | * Generate behaviors
63 | * @param {Behaviors} behaviors Behaviors to be used
64 | * @return {Behaviors} Overridden behaviors
65 | */
66 | export function overrideDefaultBehaviors (behaviors) {
67 | if (behaviors) {
68 | const currentBehaviors = {
69 | grabber: behaviors.grabber || defaultBehaviors.grabber,
70 | rendererList: behaviors.rendererList || defaultBehaviors.rendererList,
71 | strokerList: behaviors.strokerList || defaultBehaviors.strokerList,
72 | recognizerList: behaviors.recognizerList || defaultBehaviors.recognizerList,
73 | events: behaviors.events || defaultBehaviors.events,
74 | getBehaviorFromConfiguration: behaviors.getBehaviorFromConfiguration || defaultBehaviors.getBehaviorFromConfiguration
75 | }
76 | logger.debug('Override default behaviors', currentBehaviors)
77 | return currentBehaviors
78 | }
79 | return defaultBehaviors
80 | }
81 |
82 | export default defaultBehaviors
83 |
--------------------------------------------------------------------------------
/src/configuration/DefaultPenStyle.js:
--------------------------------------------------------------------------------
1 | import JsonCSS from 'json-css'
2 | import merge from 'lodash.merge'
3 | import { editorLogger as logger } from './LoggerConfig'
4 |
5 | /**
6 | * @typedef {Object} PenStyle
7 | * @property {String} color=#000000 Color (supported formats rgb() rgba() hsl() hsla() #rgb #rgba #rrggbb #rrggbbaa)
8 | * @property {String} -myscript-pen-width=1 Width of strokes and primitives in mm (no other unit is supported yet)
9 | * @property {String} -myscript-pen-fill-style=none
10 | * @property {String} -myscript-pen-fill-color=#FFFFFF00 Color filled inside the area delimited by strokes and primitives
11 | */
12 |
13 | /**
14 | * Default style
15 | * @type {PenStyle}
16 | */
17 | const defaultPenStyle = undefined
18 | const parser = new JsonCSS()
19 |
20 | /**
21 | * Generate style
22 | * @param {PenStyle} style Custom style to be applied
23 | * @return {PenStyle} Overridden style
24 | */
25 | export function overrideDefaultPenStyle (style) {
26 | const currentStyle = merge({}, defaultPenStyle, style === undefined ? {} : style)
27 | logger.debug('Override default pen style', currentStyle)
28 | return currentStyle
29 | }
30 |
31 | export function toCSS (penStyle) { // FIXME Ugly hack to parse JSON to CSS inline
32 | const css = parser.toCSS({ css: penStyle })
33 | return css.substring(6, css.length - 3)
34 | }
35 |
36 | export function toJSON (penStyle) { // FIXME Ugly hack to parse CSS inline to JSON
37 | return parser.toJSON(`css {${penStyle}}`).css
38 | }
39 |
40 | export default defaultPenStyle
41 |
--------------------------------------------------------------------------------
/src/configuration/DefaultTheme.js:
--------------------------------------------------------------------------------
1 | import JsonCSS from 'json-css'
2 | import merge from 'lodash.merge'
3 | import { editorLogger as logger } from './LoggerConfig'
4 |
5 | /**
6 | * @typedef {PenStyle} InkTheme
7 | */
8 | /**
9 | * @typedef {Object} MathTheme
10 | * @property {String} font-family=STIXGeneral Font-family to be used
11 | */
12 | /**
13 | * @typedef {Object} GeneratedTheme
14 | * @property {String} font-family=STIXGeneral Font-family to be used
15 | * @property {String} color=#A8A8A8FF Color to be used
16 | */
17 | /**
18 | * @typedef {Object} TextTheme
19 | * @property {String} font-family=OpenSans Font-family to be used
20 | * @property {Number} font-size=10 Font-size to be used
21 | */
22 | /**
23 | * @typedef {Object} Theme
24 | * @property {InkTheme} ink General settings
25 | * @property {MathTheme} .math Math theme
26 | * @property {GeneratedTheme} .math-solver Theme to be used for generated items
27 | * @property {TextTheme} .text Text theme
28 | */
29 |
30 | /**
31 | * Default theme
32 | * @type {Theme}
33 | */
34 | const defaultTheme = {
35 | ink: {
36 | color: '#000000',
37 | '-myscript-pen-width': 1,
38 | '-myscript-pen-fill-style': 'none',
39 | '-myscript-pen-fill-color': '#FFFFFF00'
40 | },
41 | '.math': {
42 | 'font-family': 'STIXGeneral'
43 | },
44 | '.math-solved': {
45 | 'font-family': 'STIXGeneral',
46 | color: '#A8A8A8FF'
47 | },
48 | '.text': {
49 | 'font-family': 'MyScriptInter',
50 | 'font-size': 10
51 | }
52 | }
53 | const parser = new JsonCSS()
54 |
55 | /**
56 | * Generate theme
57 | * @param {Theme} theme Custom theme to be applied
58 | * @return {Theme} Overridden theme
59 | */
60 | export function overrideDefaultTheme (theme) {
61 | const currentTheme = merge({}, defaultTheme, theme === undefined ? {} : theme)
62 | logger.debug('Override default theme', currentTheme)
63 | return currentTheme
64 | }
65 |
66 | export function toCSS (theme) {
67 | return parser.toCSS(theme)
68 | }
69 |
70 | export function toJSON (theme) {
71 | return parser.toJSON(theme)
72 | }
73 |
74 | export default defaultTheme
75 |
--------------------------------------------------------------------------------
/src/configuration/LoggerConfig.js:
--------------------------------------------------------------------------------
1 | import * as loglevel from 'loglevel'
2 | import Constants from './Constants'
3 |
4 | /**
5 | * Main log instance
6 | * @type {Object}
7 | */
8 | const log = loglevel.noConflict()
9 | export default log
10 |
11 | /**
12 | * Log editor events
13 | * @type {Object}
14 | */
15 | export const editorLogger = log.getLogger(Constants.Logger.EDITOR)
16 | editorLogger.setDefaultLevel(Constants.LogLevel.ERROR)
17 |
18 | /**
19 | * Log editor events
20 | * @type {Object}
21 | */
22 | export const smartGuideLogger = log.getLogger(Constants.Logger.SMARTGUIDE)
23 | editorLogger.setDefaultLevel(Constants.LogLevel.ERROR)
24 |
25 | /**
26 | * Log model events
27 | * @type {Object}
28 | */
29 | export const modelLogger = log.getLogger(Constants.Logger.MODEL)
30 | modelLogger.setDefaultLevel(Constants.LogLevel.ERROR)
31 |
32 | /**
33 | * Log grabber events
34 | * @type {Object}
35 | */
36 | export const grabberLogger = log.getLogger(Constants.Logger.GRABBER)
37 | grabberLogger.setDefaultLevel(Constants.LogLevel.ERROR)
38 |
39 | /**
40 | * Log grabber events
41 | * @type {Object}
42 | */
43 | export const rendererLogger = log.getLogger(Constants.Logger.RENDERER)
44 | rendererLogger.setDefaultLevel(Constants.LogLevel.ERROR)
45 |
46 | /**
47 | * Log recognizer events
48 | * @type {Object}
49 | */
50 | export const recognizerLogger = log.getLogger(Constants.Logger.RECOGNIZER)
51 | recognizerLogger.setDefaultLevel(Constants.LogLevel.ERROR)
52 |
53 | /**
54 | * Log callback events
55 | * @type {Object}
56 | */
57 | export const eventLogger = log.getLogger(Constants.Logger.EVENT)
58 | eventLogger.setDefaultLevel(Constants.LogLevel.ERROR)
59 |
60 | /**
61 | * Log util events
62 | * @type {Object}
63 | */
64 | export const utilLogger = log.getLogger(Constants.Logger.UTIL)
65 | utilLogger.setDefaultLevel(Constants.LogLevel.ERROR)
66 |
67 | /**
68 | * Log tests events
69 | * @type {Object}
70 | */
71 | export const testLogger = log.getLogger('test')
72 | testLogger.setDefaultLevel(Constants.LogLevel.ERROR)
73 |
--------------------------------------------------------------------------------
/src/eastereggs/InkImporter.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable no-undef */
2 | import { editorLogger as logger } from '../configuration/LoggerConfig'
3 | import * as InkModel from '../model/InkModel'
4 |
5 | /**
6 | * Function to copy past to inject ink during tutorial.
7 | * @param editorParam
8 | * @param strokes
9 | * @param delayBetweenStrokes
10 | * @param lastOneDelay
11 | */
12 | export function inkImporter (editorParam, strokes, delayBetweenStrokes, lastOneDelay) {
13 | const editor = editorParam
14 | logger.debug('inkImporter start importing =>', strokes)
15 | const origGrabber = Object.assign({}, editor.behavior.grabber)
16 | origGrabber.detach = editor.behavior.grabber.detach
17 | editor.behavior.grabber = {}
18 | const actions = []
19 | strokes.forEach((stroke) => {
20 | if (stroke.convert) {
21 | actions.push({ action: 'convert', value: true })
22 | } else if (stroke.setDelay) {
23 | actions.push({ action: 'setDelay', value: stroke.setDelay })
24 | } else {
25 | if (stroke.color) {
26 | actions.push({ action: 'setColor', value: stroke.color })
27 | }
28 | stroke.X.forEach((x, idx) => {
29 | let action = 'move'
30 | if (idx === 0) {
31 | action = 'down'
32 | } else if (idx === (stroke.X.length - 1)) {
33 | action = 'up'
34 | }
35 | actions.push({ action, point: { x: stroke.X[idx], y: stroke.Y[idx] } })
36 | })
37 | }
38 | })
39 | logger.debug('Array of actions =>', actions)
40 | const play = (actionsArray, position, delay) => {
41 | if (position < actionsArray.length) {
42 | const currentAction = actionsArray[position]
43 | let nextDelay = delay
44 | if (currentAction.action === 'convert') {
45 | editor.convert()
46 | } else if (currentAction.action === 'setDelay') {
47 | nextDelay = currentAction.value
48 | } else if (currentAction.action === 'setColor') {
49 | editor.penStyle = {
50 | color: currentAction.value
51 | }
52 | } else {
53 | currentAction.point.t = new Date().getTime()
54 | if (currentAction.action === 'down') {
55 | editor.pointerDown(currentAction.point)
56 | } else if (currentAction.action === 'up') {
57 | editor.pointerUp(currentAction.point)
58 | } else if (currentAction.action === 'move') {
59 | editor.pointerMove(currentAction.point)
60 | }
61 | } if (lastOneDelay && position === actionsArray.map(x => x.action).lastIndexOf('down') - 1) {
62 | setTimeout(() => {
63 | play(actionsArray, position + 1, nextDelay)
64 | }, lastOneDelay)
65 | } else if (position === actionsArray.length - 1) {
66 | const event = new Event('drawEnded')
67 | document.dispatchEvent(event)
68 | editor.behavior.grabber = origGrabber
69 | } else {
70 | setTimeout(() => {
71 | play(actionsArray, position + 1, nextDelay)
72 | }, nextDelay)
73 | }
74 | }
75 | }
76 | play(actions, 0, delayBetweenStrokes)
77 | }
78 |
79 | export function importStrokeGroups (editorParam, strokeGroups) {
80 | strokeGroups.forEach((group) => {
81 | group.strokes.forEach((strokeFromGroup) => {
82 | InkModel.addStroke(editorParam.model, strokeFromGroup)
83 | InkModel.addStrokeToGroup(editorParam.model, strokeFromGroup, group.penStyle)
84 | })
85 | })
86 | editorParam.renderer.drawModel(editorParam.rendererContext, editorParam.model, editorParam.stroker)
87 | }
88 |
--------------------------------------------------------------------------------
/src/event/Event.js:
--------------------------------------------------------------------------------
1 | import { eventLogger as logger } from '../configuration/LoggerConfig'
2 |
3 | /**
4 | * Emits an event when the editor state change
5 | * @param {String} type
6 | * @param {Object} data
7 | * @emits {Event}
8 | */
9 | export default function emit (type, data) {
10 | logger.info(`emitting ${type} event`, data)
11 | // We are making usage of a browser provided class
12 | // eslint-disable-next-line no-undef
13 | this.dispatchEvent(new CustomEvent(type, Object.assign({ bubbles: true, composed: true }, data ? { detail: data } : undefined)))
14 | }
15 |
--------------------------------------------------------------------------------
/src/iink.js:
--------------------------------------------------------------------------------
1 | import Constants from './configuration/Constants'
2 | import LoggerConfig from './configuration/LoggerConfig'
3 | import DefaultConfiguration from './configuration/DefaultConfiguration'
4 | import DefaultPenStyle from './configuration/DefaultPenStyle'
5 | import DefaultTheme from './configuration/DefaultTheme'
6 | import DefaultBehaviors from './configuration/DefaultBehaviors'
7 | import * as InkModel from './model/InkModel'
8 | import { Editor } from './Editor'
9 | import { register, getAvailableLanguageList } from './EditorFacade'
10 | import * as RecognizerContext from './model/RecognizerContext'
11 |
12 | const iink = {
13 | Constants,
14 | // Default instantiations
15 | DefaultConfiguration,
16 | DefaultBehaviors,
17 | DefaultPenStyle,
18 | DefaultTheme,
19 | // Helper functions
20 | register,
21 | getAvailableLanguageList,
22 | // Internals
23 | LoggerConfig,
24 | Editor,
25 | InkModel,
26 | RecognizerContext
27 | }
28 |
29 | export {
30 | iink as default,
31 | Constants,
32 | // Default instantiations
33 | DefaultConfiguration,
34 | DefaultBehaviors,
35 | DefaultPenStyle,
36 | DefaultTheme,
37 | // Helper functions
38 | register,
39 | getAvailableLanguageList,
40 | // Internals
41 | LoggerConfig,
42 | Editor,
43 | InkModel,
44 | RecognizerContext
45 | }
46 |
--------------------------------------------------------------------------------
/src/model/Symbol.js:
--------------------------------------------------------------------------------
1 | function mergeBounds (boundsA, boundsB) {
2 | return {
3 | minX: Math.min(boundsA.minX, boundsB.minX),
4 | maxX: Math.max(boundsA.maxX, boundsB.maxX),
5 | minY: Math.min(boundsA.minY, boundsB.minY),
6 | maxY: Math.max(boundsA.maxY, boundsB.maxY)
7 | }
8 | }
9 |
10 | function getLineBounds (line) {
11 | return {
12 | minX: Math.min(line.firstPoint.x, line.lastPoint.x),
13 | maxX: Math.max(line.firstPoint.x, line.lastPoint.x),
14 | minY: Math.min(line.firstPoint.y, line.lastPoint.y),
15 | maxY: Math.max(line.firstPoint.y, line.lastPoint.y)
16 | }
17 | }
18 |
19 | function getEllipseBounds (ellipse) {
20 | const angleStep = 0.02 // angle delta between interpolated points on the arc, in radian
21 |
22 | let z1 = Math.cos(ellipse.orientation)
23 | let z3 = Math.sin(ellipse.orientation)
24 | let z2 = z1
25 | let z4 = z3
26 | z1 *= ellipse.maxRadius
27 | z2 *= ellipse.minRadius
28 | z3 *= ellipse.maxRadius
29 | z4 *= ellipse.minRadius
30 |
31 | const n = Math.abs(ellipse.sweepAngle) / angleStep
32 |
33 | const x = []
34 | const y = []
35 |
36 | for (let i = 0; i <= n; i++) {
37 | const angle = ellipse.startAngle + ((i / n) * ellipse.sweepAngle)
38 | const alpha = Math.atan2(Math.sin(angle) / ellipse.minRadius, Math.cos(angle) / ellipse.maxRadius)
39 |
40 | const cosAlpha = Math.cos(alpha)
41 | const sinAlpha = Math.sin(alpha)
42 |
43 | x.push(ellipse.center.x + ((z1 * cosAlpha) - (z4 * sinAlpha)))
44 | y.push(ellipse.center.y + ((z2 * sinAlpha) + (z3 * cosAlpha)))
45 | }
46 |
47 | return {
48 | minX: Math.min(...x),
49 | maxX: Math.max(...x),
50 | minY: Math.min(...y),
51 | maxY: Math.max(...y)
52 | }
53 | }
54 |
55 | function getTextLineBounds (textLine) {
56 | return {
57 | minX: textLine.data.topLeftPoint.x,
58 | maxX: textLine.data.topLeftPoint.x + textLine.data.width,
59 | minY: textLine.data.topLeftPoint.y,
60 | maxY: textLine.data.topLeftPoint.y + textLine.data.height
61 | }
62 | }
63 |
64 | function getClefBounds (clef) {
65 | return {
66 | minX: clef.boundingBox.x,
67 | maxX: clef.boundingBox.x + clef.boundingBox.width,
68 | minY: clef.boundingBox.y,
69 | maxY: clef.boundingBox.y + clef.boundingBox.height
70 | }
71 | }
72 |
73 | function getStrokeBounds (stroke) {
74 | return {
75 | minX: Math.min(...stroke.x),
76 | maxX: Math.max(...stroke.x),
77 | minY: Math.min(...stroke.y),
78 | maxY: Math.max(...stroke.y)
79 | }
80 | }
81 |
82 | /**
83 | * Get the box enclosing the given symbols
84 | * @param {Array} symbols Symbols to extract bounds from
85 | * @param {Bounds} [bounds] Starting bounds for recursion
86 | * @return {Bounds} Bounding box enclosing symbols
87 | */
88 | export function getSymbolsBounds (symbols, bounds = { minX: Number.MAX_VALUE, maxX: Number.MIN_VALUE, minY: Number.MAX_VALUE, maxY: Number.MIN_VALUE }) {
89 | let boundsRef = bounds
90 | boundsRef = symbols
91 | .filter(symbol => symbol.type === 'stroke')
92 | .map(getStrokeBounds)
93 | .reduce(mergeBounds, boundsRef)
94 | boundsRef = symbols
95 | .filter(symbol => symbol.type === 'clef')
96 | .map(getClefBounds)
97 | .reduce(mergeBounds, boundsRef)
98 | boundsRef = symbols
99 | .filter(symbol => symbol.type === 'line')
100 | .map(getLineBounds)
101 | .reduce(mergeBounds, boundsRef)
102 | boundsRef = symbols
103 | .filter(symbol => symbol.type === 'ellipse')
104 | .map(getEllipseBounds)
105 | .reduce(mergeBounds, boundsRef)
106 | boundsRef = symbols
107 | .filter(symbol => symbol.type === 'textLine')
108 | .map(getTextLineBounds)
109 | .reduce(mergeBounds, boundsRef)
110 | return boundsRef
111 | }
112 |
--------------------------------------------------------------------------------
/src/model/UndoRedoContext.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Undo/redo context
3 | * @typedef {Object} UndoRedoContext
4 | * @property {Array} stack=[] List of processed models.
5 | * @property {Number} currentPosition=-1 Current model index into the stack.
6 | * @property {Number} maxSize Max size of the stack.
7 | * @property {Boolean} canUndo=false
8 | * @property {Boolean} canRedo=false
9 | */
10 |
11 | /**
12 | * Create a new undo/redo context
13 | * @param {Configuration} configuration Current configuration
14 | * @return {UndoRedoContext} New undo/redo context
15 | */
16 | export function createUndoRedoContext (configuration) {
17 | return {
18 | stack: [],
19 | currentPosition: -1,
20 | maxSize: configuration.undoRedoMaxStackSize,
21 | canUndo: false,
22 | canRedo: false
23 | }
24 | }
25 |
26 | /**
27 | * Update the undo/redo state
28 | * @param {UndoRedoContext} undoRedoContext Current undo/redo context
29 | * @return {UndoRedoContext} Updated undo/redo context
30 | */
31 | export function updateUndoRedoState (undoRedoContext) {
32 | const undoRedoContextRef = undoRedoContext
33 | undoRedoContextRef.canUndo = undoRedoContext.currentPosition > 0
34 | undoRedoContextRef.canRedo = undoRedoContext.currentPosition < (undoRedoContext.stack.length - 1)
35 | return undoRedoContextRef
36 | }
37 |
--------------------------------------------------------------------------------
/src/model/UndoRedoManager.js:
--------------------------------------------------------------------------------
1 | import * as InkModel from '../model/InkModel'
2 | import * as UndoRedoContext from '../model/UndoRedoContext'
3 | import { modelLogger as logger } from '../configuration/LoggerConfig'
4 | import Constants from '../configuration/Constants'
5 |
6 | /**
7 | * Undo/redo manager
8 | * @typedef {Object} UndoRedoManager
9 | * @property {function} updateModel Push the current model into the undo/redo context.
10 | * @property {function} undo Undo.
11 | * @property {function} redo Redo.
12 | * @property {function} clear Clear.
13 | */
14 |
15 | /**
16 | * Get current model in stack
17 | * @param {UndoRedoContext} undoRedoContext Current undo/redo context
18 | * @param {Boolean} [clone=true] Whether or not to clone the model
19 | * @param {...String} types
20 | */
21 | export function getModel (undoRedoContext, clone = true, ...types) {
22 | const model = undoRedoContext.stack[undoRedoContext.currentPosition]
23 | const val = {
24 | res: clone ? InkModel.cloneModel(model) : model,
25 | types
26 | }
27 | return Promise.resolve(val)
28 | }
29 |
30 | /**
31 | * Mutate the undoRedo stack by adding a new model to it.
32 | * @param {UndoRedoContext} undoRedoContext Current undo/redo context.
33 | * @param {Model} model Current model.
34 | */
35 | export function updateModel (undoRedoContext, model) {
36 | // Used to update the model with the recognition result if relevant
37 | const modelIndex = undoRedoContext.stack.findIndex(item => (item.modificationTime === model.modificationTime) && (item.rawStrokes.length === model.rawStrokes.length))
38 |
39 | const modelReference = model
40 | modelReference.modificationTime = new Date().getTime()
41 |
42 | const types = []
43 | if (modelIndex > -1) {
44 | undoRedoContext.stack.splice(modelIndex, 1, InkModel.cloneModel(modelReference))
45 | logger.debug('model updated', modelReference)
46 | } else {
47 | const undoRedoContextReference = undoRedoContext
48 | undoRedoContextReference.currentPosition += 1
49 | undoRedoContextReference.stack = undoRedoContextReference.stack.slice(0, undoRedoContextReference.currentPosition)
50 | undoRedoContextReference.stack.push(InkModel.cloneModel(modelReference))
51 | if (undoRedoContextReference.stack.length > undoRedoContextReference.maxSize) {
52 | undoRedoContextReference.stack.shift()
53 | undoRedoContextReference.currentPosition--
54 | }
55 | logger.debug('model pushed', modelReference)
56 | types.push(Constants.EventType.CHANGED)
57 | }
58 | UndoRedoContext.updateUndoRedoState(undoRedoContext)
59 | logger.debug('undo/redo stack updated', undoRedoContext)
60 | return getModel(undoRedoContext, false, ...types)
61 | }
62 |
63 | /**
64 | * Undo
65 | * @param {UndoRedoContext} undoRedoContext Current undo/redo context.
66 | * @param {Model} model Current model.
67 | */
68 | export function undo (undoRedoContext, model) {
69 | const undoRedoContextReference = undoRedoContext
70 | if (undoRedoContextReference.currentPosition > 0) {
71 | undoRedoContextReference.currentPosition -= 1
72 | UndoRedoContext.updateUndoRedoState(undoRedoContext)
73 | logger.debug('undo index', undoRedoContextReference.currentPosition)
74 | }
75 | return getModel(undoRedoContext, true, Constants.EventType.CHANGED, Constants.EventType.EXPORTED)
76 | }
77 |
78 | /**
79 | * Redo
80 | * @param {UndoRedoContext} undoRedoContext Current undo/redo context.
81 | * @param {Model} model Current model.
82 | */
83 | export function redo (undoRedoContext, model) {
84 | const undoRedoContextReference = undoRedoContext
85 | if (undoRedoContextReference.currentPosition < undoRedoContextReference.stack.length - 1) {
86 | undoRedoContextReference.currentPosition += 1
87 | UndoRedoContext.updateUndoRedoState(undoRedoContext)
88 | logger.debug('redo index', undoRedoContextReference.currentPosition)
89 | }
90 | return getModel(undoRedoContext, true, Constants.EventType.CHANGED, Constants.EventType.EXPORTED)
91 | }
92 |
--------------------------------------------------------------------------------
/src/recognizer/CryptoHelper.js:
--------------------------------------------------------------------------------
1 | import Hex from 'crypto-js/enc-hex'
2 | import HmacSHA512 from 'crypto-js/hmac-sha512'
3 | import { recognizerLogger as logger } from '../configuration/LoggerConfig'
4 |
5 | /**
6 | * Compute HMAC signature for server authentication
7 | *
8 | * @param {Object} input Input data to compute HMAC
9 | * @param {String} applicationKey Current applicationKey
10 | * @param {String} hmacKey Current hmacKey
11 | * @return {String} Signature
12 | */
13 | export function computeHmac (input, applicationKey, hmacKey) {
14 | const jsonInput = (typeof input === 'object') ? JSON.stringify(input) : input
15 | logger.debug('The HmacSHA512 function is loaded', HmacSHA512)
16 | return new HmacSHA512(jsonInput, applicationKey + hmacKey).toString(Hex)
17 | }
18 |
--------------------------------------------------------------------------------
/src/recognizer/rest/networkInterface.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable no-unused-expressions */
2 | import * as CryptoHelper from '../CryptoHelper'
3 |
4 | /**
5 | * Post request
6 | * @param {RecognizerContext} recognizerContext Recognizer context
7 | * @param {String} url URL
8 | * @param {Object} data Data to be sent
9 | * @param {String} apiVersion api version
10 | * @param {String} mimeType MimeType to be used
11 | * @return {Promise}
12 | */
13 | export async function post (recognizerContext, url, data, mimeType) {
14 | const configuration = recognizerContext.editor.configuration
15 | const recognizerContextRef = recognizerContext
16 | if (recognizerContextRef) {
17 | recognizerContextRef.idle = true
18 | }
19 | try {
20 | const headers = new Headers()
21 | headers.append('Accept', 'application/json,' + mimeType)
22 | headers.append('applicationKey', configuration.recognitionParams.server.applicationKey)
23 | headers.append('hmac', CryptoHelper.computeHmac(JSON.stringify(data), configuration.recognitionParams.server.applicationKey, configuration.recognitionParams.server.hmacKey))
24 | headers.append('Content-Type', 'application/json')
25 | const reqInit = {
26 | method: 'POST',
27 | headers,
28 | body: JSON.stringify(data),
29 | credentials: 'omit'
30 | }
31 | const request = new Request(url, reqInit)
32 | const response = await fetch(request)
33 | const contentType = response.headers.get('content-type')
34 | let result = ''
35 | switch (contentType) {
36 | case 'application/vnd.openxmlformats-officedocument.presentationml.presentation':
37 | case 'image/png':
38 | case 'image/jpeg':
39 | result = await response.blob()
40 | break
41 | case 'application/json':
42 | result = await response.json()
43 | break
44 | case 'application/vnd.myscript.jiix':
45 | result = await response.clone().json().catch(async () => await response.text())
46 | break
47 | default:
48 | result = await response.text()
49 | break
50 | }
51 | return result
52 | } catch (error) {
53 | throw new Error({ msg: `Could not connect to ${url} connection error`, recoverable: false })
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/src/recognizer/websocket/networkWSInterface.js:
--------------------------------------------------------------------------------
1 | import { recognizerLogger as logger } from '../../configuration/LoggerConfig'
2 | import * as RecognizerContext from '../../model/RecognizerContext'
3 |
4 | function infinitePing (websocket) {
5 | const websocketRef = websocket
6 | websocketRef.pingLostCount++
7 | if (websocketRef.pingLostCount > websocketRef.maxPingLost) {
8 | websocket.close(1000, 'PING_LOST')
9 | } else if (websocketRef.readyState <= 1) {
10 | setTimeout(() => {
11 | if (websocketRef.readyState <= 1) {
12 | websocketRef.send(JSON.stringify({ type: 'ping' }))
13 | infinitePing(websocketRef)
14 | }
15 | }, websocketRef.pingDelay)
16 | }
17 | }
18 |
19 | /**
20 | * Attach all socket attributes helping managing server connexion
21 | * @param {WebSocket} websocket Current WebSocket
22 | * @param {RecognizerContext} recognizerContext
23 | */
24 | function addWebsocketAttributes (websocket, recognizerContext) {
25 | const websocketConfiguration = recognizerContext.editor.configuration.recognitionParams.server.websocket
26 | const socket = websocket
27 | socket.start = new Date()
28 | socket.autoReconnect = websocketConfiguration.autoReconnect
29 | socket.maxRetryCount = websocketConfiguration.maxRetryCount
30 | socket.pingEnabled = websocketConfiguration.pingEnabled
31 | socket.pingDelay = websocketConfiguration.pingDelay
32 | socket.maxPingLost = websocketConfiguration.maxPingLostCount
33 | socket.pingLostCount = 0
34 | socket.recognizerContext = recognizerContext
35 | }
36 |
37 | /**
38 | * @param {RecognizerContext} recognizerContext Recognizer context
39 | * @return {WebSocket} Opened WebSocket
40 | */
41 | export function openWebSocket (recognizerContext) {
42 | let socket
43 | try {
44 | // eslint-disable-next-line no-undef
45 | socket = new WebSocket(recognizerContext.url)
46 |
47 | addWebsocketAttributes(socket, recognizerContext)
48 |
49 | if (socket.pingEnabled) {
50 | infinitePing(socket)
51 | }
52 |
53 | socket.onopen = (e) => {
54 | logger.trace('onOpen')
55 | recognizerContext.websocketCallback(e)
56 | }
57 |
58 | socket.onclose = (e) => {
59 | logger.trace('onClose', new Date() - socket.start)
60 | recognizerContext.websocketCallback(e)
61 | }
62 |
63 | socket.onerror = (e) => {
64 | logger.trace('onError')
65 | recognizerContext.websocketCallback(e)
66 | }
67 |
68 | socket.onmessage = (e) => {
69 | logger.trace('onMessage')
70 | socket.pingLostCount = 0
71 | const parsedMessage = JSON.parse(e.data)
72 | if (parsedMessage.type !== 'pong') {
73 | const callBackParam = {
74 | type: e.type,
75 | data: JSON.parse(e.data)
76 | }
77 | recognizerContext.websocketCallback(callBackParam)
78 | }
79 | }
80 | } catch (error) {
81 | recognizerContext.recognitionContexts[0].initPromise.reject(error)
82 | logger.error('Unable to open websocket, Check the host and your connectivity')
83 | }
84 |
85 | return socket
86 | }
87 |
88 | /**
89 | * Send data message
90 | * @param {RecognizerContext} recognizerContext Current recognizer context
91 | * @param {Object} message Data message
92 | */
93 | export function send (recognizerContext, message) {
94 | const recognizerContextRef = recognizerContext
95 | recognizerContextRef.idle = false
96 |
97 | const websocket = recognizerContextRef.websocket
98 | if (websocket.readyState === 1) {
99 | websocket.send(JSON.stringify(message))
100 | logger.debug(`${message.type} message sent`, message)
101 | } else {
102 | throw RecognizerContext.LOST_CONNEXION_MESSAGE
103 | }
104 | }
105 |
106 | /**
107 | * Close the websocket
108 | * @param {RecognizerContext} recognizerContext Current recognizer context
109 | * @param {Number} code Exit code
110 | * @param {String} reason Exit reason
111 | */
112 | export function close (recognizerContext, code, reason) {
113 | const websocket = recognizerContext.websocket
114 | if (websocket && websocket.readyState < 2) {
115 | websocket.close(code, reason)
116 | }
117 | }
118 |
--------------------------------------------------------------------------------
/src/renderer/QuadraticUtils.js:
--------------------------------------------------------------------------------
1 | /** ===============================================================================================
2 | * Compute quadratics control points
3 | * ============================================================================================= */
4 |
5 | /**
6 | *
7 | * @param {{x: Number, y: Number, p: Number}} point
8 | * @param angle
9 | * @param width
10 | * @return {({x: number, y: *}|{x: *, y: number})}
11 | */
12 | export function computeLinksPoints (point, angle, width) {
13 | const radius = point.p * width
14 | return [{
15 | x: (point.x - (Math.sin(angle) * radius)),
16 | y: (point.y + (Math.cos(angle) * radius))
17 | }, {
18 | x: (point.x + (Math.sin(angle) * radius)),
19 | y: (point.y - (Math.cos(angle) * radius))
20 | }]
21 | }
22 |
23 | /**
24 | *
25 | * @param {{x: Number, y: Number, p: Number}} point1
26 | * @param {{x: Number, y: Number, p: Number}} point2
27 | * @return {{x: Number, y: Number, p: Number}}
28 | */
29 | export function computeMiddlePoint (point1, point2) {
30 | return {
31 | x: ((point2.x + point1.x) / 2),
32 | y: ((point2.y + point1.y) / 2),
33 | p: ((point2.p + point1.p) / 2)
34 | }
35 | }
36 |
37 | /**
38 | *
39 | * @param {{x: Number, y: Number}} begin
40 | * @param {{x: Number, y: Number}} end
41 | * @return {Number}
42 | */
43 | export function computeAxeAngle (begin, end) {
44 | return Math.atan2(end.y - begin.y, end.x - begin.x)
45 | }
46 |
--------------------------------------------------------------------------------
/src/renderer/canvas/ImageRenderer.js:
--------------------------------------------------------------------------------
1 | import { drawModel } from './CanvasRenderer'
2 | import * as InkModel from '../../model/InkModel'
3 |
4 | function createCanvas (borderCoordinates, margin = 10) {
5 | // eslint-disable-next-line no-undef
6 | const browserDocument = document
7 | const canvas = browserDocument.createElement('canvas')
8 | canvas.width = Math.abs(borderCoordinates.maxX - borderCoordinates.minX) + (2 * margin)
9 | canvas.style.width = `${canvas.width}px`
10 | canvas.height = Math.abs(borderCoordinates.maxY - borderCoordinates.minY) + (2 * margin)
11 | canvas.style.height = `${canvas.height}px`
12 | return canvas
13 | }
14 |
15 | /**
16 | * Generate a PNG image data url from the model
17 | * @param {Model} model Current model
18 | * @param {Stroker} stroker Current stroker
19 | * @param {Number} [margin=10] Margins to apply around the image
20 | * @return {String} Image data string result
21 | */
22 | export function getImage (model, stroker, margin = 10) {
23 | if (model.rawStrokes.length > 0) {
24 | const borderCoordinates = InkModel.getBorderCoordinates(model)
25 |
26 | const capturingCanvas = createCanvas(borderCoordinates, margin)
27 | const renderingCanvas = createCanvas(borderCoordinates, margin)
28 | const renderStructure = {
29 | renderingCanvas,
30 | renderingCanvasContext: renderingCanvas.getContext('2d'),
31 | capturingCanvas,
32 | capturingCanvasContext: capturingCanvas.getContext('2d')
33 | }
34 | // Change canvas origin
35 | renderStructure.renderingCanvasContext.translate(-borderCoordinates.minX + margin, -borderCoordinates.minY + margin)
36 | drawModel(renderStructure, model, stroker)
37 | return renderStructure.renderingCanvas.toDataURL('image/png')
38 | }
39 | return null
40 | }
41 |
--------------------------------------------------------------------------------
/src/renderer/canvas/symbols/MathSymbolCanvasRenderer.js:
--------------------------------------------------------------------------------
1 | import { rendererLogger as logger } from '../../../configuration/LoggerConfig'
2 | import { drawStroke } from './StrokeSymbolCanvasRenderer'
3 | import * as InkModel from '../../../model/InkModel'
4 |
5 | /**
6 | * @type {{inputCharacter: String, char: String, string: String, textLine: String}}
7 | */
8 | export const MathSymbols = {
9 | nonTerminalNode: 'nonTerminalNode',
10 | terminalNode: 'terminalNode',
11 | rule: 'rule'
12 | }
13 |
14 | function drawTerminalNode (context, terminalNode, model, stroker) {
15 | terminalNode.inkRanges.forEach((inkRange) => {
16 | InkModel.extractStrokesFromInkRange(model, inkRange.component, inkRange.component, inkRange.firstItem, inkRange.lastItem)
17 | .forEach(stroke => drawStroke(context, stroke, stroker))
18 | })
19 | }
20 |
21 | /**
22 | * Draw a math symbol
23 | * @param {Object} context Current rendering context
24 | * @param {Object} symbol Symbol to draw
25 | * @param {Model} model Current model
26 | * @param {Stroker} stroker Stroker to use to render a stroke
27 | */
28 | export function drawMathSymbol (context, symbol, model, stroker) {
29 | logger.debug(`draw ${symbol.type} text input`)
30 | const contextReference = context
31 | contextReference.save()
32 | try {
33 | contextReference.lineWidth = symbol.width
34 | contextReference.strokeStyle = symbol.color
35 |
36 | switch (symbol.type) {
37 | case MathSymbols.nonTerminalNode:
38 | drawMathSymbol(contextReference, symbol.candidates[symbol.selectedCandidate], model, stroker)
39 | break
40 | case MathSymbols.terminalNode:
41 | drawTerminalNode(contextReference, symbol, model, stroker)
42 | break
43 | case MathSymbols.rule:
44 | symbol.children.forEach(child => drawMathSymbol(contextReference, child, model, stroker))
45 | break
46 | default:
47 | logger.error(`${symbol.type} not implemented`)
48 | }
49 | } finally {
50 | contextReference.restore()
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/src/renderer/canvas/symbols/StrokeSymbolCanvasRenderer.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Draw a stroke symbol
3 | * @param {Object} context Current rendering context
4 | * @param {Stroke} stroke Stroke to be drawn
5 | * @param {Stroker} stroker Stroker to use to render a stroke
6 | */
7 | export function drawStroke (context, stroke, stroker) {
8 | if (stroker && (!stroke || stroke.pointerType !== 'ERASER')) {
9 | stroker.drawStroke(context, stroke)
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/src/renderer/canvas/symbols/TextSymbolCanvasRenderer.js:
--------------------------------------------------------------------------------
1 | import { rendererLogger as logger } from '../../../configuration/LoggerConfig'
2 | import { drawLine } from './ShapeSymbolCanvasRenderer'
3 |
4 | /**
5 | * @type {{inputCharacter: String, char: String, string: String, textLine: String}}
6 | */
7 | export const TextSymbols = {
8 | inputCharacter: 'inputCharacter',
9 | char: 'char',
10 | string: 'string',
11 | textLine: 'textLine'
12 | }
13 |
14 | function drawUnderline (context, underline, label, data) {
15 | const delta = data.width / label.length
16 | const p1 = {
17 | x: data.topLeftPoint.x + (underline.data.firstCharacter * delta),
18 | y: data.topLeftPoint.y + data.height
19 | }
20 | const p2 = {
21 | x: data.topLeftPoint.x + (underline.data.lastCharacter * delta),
22 | y: data.topLeftPoint.y + data.height
23 | }
24 | drawLine(context, p1, p2)
25 | }
26 |
27 | function drawText (context, label, data) {
28 | const contextReference = context
29 | contextReference.save()
30 | try {
31 | contextReference.font = `${data.textHeight}px serif`
32 | contextReference.textAlign = (data.justificationType === 'CENTER') ? 'center' : 'left'
33 | contextReference.textBaseline = 'bottom'
34 | contextReference.fillStyle = contextReference.strokeStyle
35 | contextReference.fillText(label, data.topLeftPoint.x, (data.topLeftPoint.y + data.height))
36 | } finally {
37 | contextReference.restore()
38 | }
39 | }
40 |
41 | function drawTextLine (context, textLine) {
42 | drawText(context, textLine.label, textLine.data)
43 | textLine.underlineList.forEach((underline) => {
44 | drawUnderline(context, underline, textLine.label, textLine.data)
45 | })
46 | }
47 |
48 | /**
49 | * Draw a text symbol
50 | * @param {Object} context Current rendering context
51 | * @param {Object} symbol Symbol to draw
52 | */
53 | export function drawTextSymbol (context, symbol) {
54 | logger.debug(`draw ${symbol.type} symbol`)
55 | const contextReference = context
56 | contextReference.save()
57 | try {
58 | contextReference.lineWidth = symbol.width
59 | contextReference.strokeStyle = symbol.color
60 |
61 | if (symbol.elementType) {
62 | switch (symbol.elementType) {
63 | case TextSymbols.textLine:
64 | drawTextLine(contextReference, symbol)
65 | break
66 | default:
67 | logger.error(`${symbol.elementType} not implemented`)
68 | break
69 | }
70 | } else {
71 | switch (symbol.type) {
72 | case TextSymbols.textLine:
73 | drawTextLine(contextReference, symbol)
74 | break
75 | default:
76 | logger.error(`${symbol.type} not implemented`)
77 | }
78 | }
79 | } finally {
80 | contextReference.restore()
81 | }
82 | }
83 |
--------------------------------------------------------------------------------
/src/renderer/svg/symbols/StrokeSymbolSVGRenderer.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Draw a stroke symbol
3 | * @param {Object} context Current rendering context
4 | * @param {Stroke} stroke Stroke to be drawn
5 | * @param {Stroker} stroker Stroker to use to render a stroke
6 | */
7 | export function drawStroke (context, stroke, stroker) {
8 | if (stroker) {
9 | if (stroke.pointerType === 'ERASER') {
10 | stroker.drawErasingStroke(context, stroke)
11 | } else {
12 | stroker.drawStroke(context, stroke)
13 | }
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/src/util/PromiseHelper.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @typedef {Object} DestructuredPromise
3 | * @property {Promise} promise
4 | * @property {function} resolve
5 | * @property {function} reject
6 | */
7 |
8 | /**
9 | * destructurePromise
10 | * @returns {{resolve: *, reject: *, promise: Promise}}
11 | */
12 | export function destructurePromise () {
13 | let resolveParam
14 | let rejectParam
15 | const initPromise = new Promise(
16 | (resolve, reject) => {
17 | resolveParam = async (v) => {
18 | initPromise.isFullfilled = true
19 | initPromise.isPending = false
20 | return resolve(v)
21 | }
22 | rejectParam = async (e) => {
23 | initPromise.isRejected = true
24 | initPromise.isPending = false
25 | reject(e)
26 | }
27 | })
28 |
29 | initPromise.isPending = true
30 |
31 | return { promise: initPromise, resolve: resolveParam, reject: rejectParam }
32 | }
33 |
34 | /**
35 | * @param time
36 | * @return {{timer: *, promise: Promise}}
37 | */
38 | export function delay (time) {
39 | let timer = null
40 | const promise = new Promise((resolve) => {
41 | timer = setTimeout(resolve, time)
42 | })
43 | return {
44 | promise,
45 | timer
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/test/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | env: {
3 | mocha: true
4 | },
5 | globals: {
6 | browser: true,
7 | page: true,
8 | expect: true
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/test/lib/configuration.js:
--------------------------------------------------------------------------------
1 |
2 | module.exports = [
3 | {
4 | type: 'MATH',
5 | protocol: 'WEBSOCKET',
6 | apiVersion: 'V4'
7 | }, {
8 | type: 'TEXT',
9 | protocol: 'WEBSOCKET',
10 | apiVersion: 'V4'
11 | }, {
12 | type: 'TEXT',
13 | protocol: 'REST',
14 | apiVersion: 'V4'
15 | }, {
16 | type: 'Raw Content',
17 | protocol: 'REST',
18 | apiVersion: 'V4'
19 | }
20 | ]
21 |
--------------------------------------------------------------------------------
/test/lib/inks/3times2.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "x": [
4 | 100, 104, 107, 110, 113, 116, 120, 124, 127, 133, 139, 144, 149, 152, 156,
5 | 160, 163, 166, 168, 165, 162, 159, 155, 150, 146, 143, 140, 137, 133, 130,
6 | 127, 124, 119, 123, 126, 130, 135, 139, 142, 146, 149, 153, 156, 162, 165,
7 | 168, 170, 173, 175, 175, 175, 175, 174, 169, 166, 162, 156, 152, 148, 141,
8 | 136, 133, 130, 127, 124, 120, 116, 113, 109, 110
9 | ],
10 | "y": [
11 | 143, 146, 146, 146, 146, 144, 143, 143, 143, 145, 146, 148, 150, 153, 156,
12 | 160, 162, 167, 174, 176, 177, 178, 179, 181, 182, 183, 183, 183, 183, 183,
13 | 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 185, 188,
14 | 190, 194, 197, 202, 206, 209, 212, 216, 222, 222, 222, 222, 222, 222, 222,
15 | 222, 222, 221, 220, 219, 217, 217, 216, 216, 220
16 | ]
17 | },
18 | {
19 | "x": [217, 222, 226, 229, 231, 236, 240, 243, 254, 257, 263],
20 | "y": [166, 166, 168, 169, 172, 175, 180, 182, 200, 205, 211]
21 | },
22 | {
23 | "x": [271, 268, 263, 261, 256, 252, 247, 241, 237, 232, 228, 227, 226],
24 | "y": [166, 170, 174, 178, 183, 187, 192, 198, 202, 207, 211, 215, 218]
25 | },
26 | {
27 | "x": [
28 | 327, 327, 325, 322, 322, 322, 326, 334, 337, 343, 348, 352, 355, 361, 364,
29 | 366, 366, 363, 362, 359, 356, 352, 348, 345, 325, 323, 320, 323, 326, 329,
30 | 332, 335, 338, 343, 352, 358, 361, 364, 367, 371, 374, 377, 380, 380
31 | ],
32 | "y": [
33 | 155, 152, 148, 145, 141, 137, 134, 130, 128, 128, 130, 134, 137, 149, 151,
34 | 158, 169, 181, 187, 189, 194, 198, 203, 205, 221, 224, 226, 227, 227, 227,
35 | 227, 226, 226, 225, 222, 220, 219, 219, 219, 219, 219, 219, 219, 222
36 | ]
37 | }
38 | ]
39 |
--------------------------------------------------------------------------------
/test/lib/inks/emphasized.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "x": [
4 | 226, 230, 235, 240, 243, 248, 253, 257, 258, 258, 258, 258, 255, 250, 247,
5 | 247, 247, 247, 247, 247, 247, 248, 249, 250, 251, 251, 252, 252, 253, 253,
6 | 253, 253, 254, 258, 261, 261, 261, 262, 265, 269, 273, 276, 277, 278, 277,
7 | 272, 271, 271, 274, 278, 283, 286, 289, 292, 295, 296, 298, 299, 300, 301,
8 | 301, 301, 301, 301, 297, 295, 295, 295, 295, 295, 295, 295, 296, 297, 298,
9 | 300, 302, 305, 309, 314, 318, 319, 320, 321, 322, 322, 322, 322, 320, 316,
10 | 315, 315, 315, 315, 315, 315, 315, 315, 318, 322, 325, 328, 332, 337, 340,
11 | 341, 341, 341, 339, 339, 339, 339, 340, 343, 346, 348, 348, 348, 345, 345,
12 | 349, 353, 357, 360, 363
13 | ],
14 | "y": [
15 | 246, 246, 246, 243, 241, 238, 234, 228, 225, 221, 216, 212, 210, 211, 215,
16 | 218, 221, 224, 227, 230, 234, 239, 242, 246, 249, 252, 255, 258, 261, 257,
17 | 254, 249, 245, 241, 243, 247, 250, 254, 256, 256, 255, 253, 250, 246, 242,
18 | 243, 247, 252, 256, 258, 256, 254, 252, 249, 247, 243, 239, 232, 229, 224,
19 | 221, 217, 213, 210, 209, 212, 216, 219, 222, 228, 232, 235, 238, 242, 246,
20 | 249, 254, 256, 256, 252, 245, 242, 238, 232, 229, 225, 221, 216, 211, 209,
21 | 213, 217, 223, 229, 234, 240, 244, 247, 250, 254, 256, 256, 253, 247, 243,
22 | 240, 236, 233, 237, 240, 243, 246, 250, 250, 247, 243, 237, 234, 233, 237,
23 | 237, 239, 240, 240, 239
24 | ]
25 | },
26 | {
27 | "x": [
28 | 397, 402, 408, 415, 419, 423, 425, 426, 426, 424, 420, 416, 416, 416, 416,
29 | 416, 416, 416, 416, 416, 416, 416, 416, 416, 416, 416, 416, 416, 417, 420,
30 | 423, 425, 428, 429, 429, 434, 439, 444, 447, 449, 450, 450, 449, 449, 449,
31 | 452, 456, 459, 460, 460, 460, 458, 454, 454, 458, 462, 466, 471, 477, 477,
32 | 476, 476, 476, 476, 479, 483, 487, 491, 493, 495, 495, 494, 494, 494, 494,
33 | 498, 501, 504, 507, 508, 508, 507, 503, 505, 509, 512
34 | ],
35 | "y": [
36 | 241, 241, 241, 241, 238, 233, 228, 220, 214, 210, 208, 210, 218, 221, 227,
37 | 230, 234, 237, 241, 245, 249, 254, 260, 265, 261, 258, 252, 247, 242, 242,
38 | 245, 248, 253, 256, 261, 262, 261, 259, 255, 247, 244, 240, 244, 247, 251,
39 | 254, 254, 251, 247, 243, 239, 236, 237, 241, 244, 245, 245, 245, 239, 242,
40 | 246, 249, 252, 255, 255, 255, 251, 248, 245, 240, 237, 240, 244, 247, 250,
41 | 252, 252, 248, 245, 241, 238, 235, 233, 236, 237, 234
42 | ]
43 | },
44 | {
45 | "x": [
46 | 398, 404, 408, 411, 415, 419, 423, 429, 434, 440, 443, 448, 454, 461, 465,
47 | 471, 475, 479, 487, 490, 498, 502, 510, 515, 521, 525, 529, 532
48 | ],
49 | "y": [
50 | 266, 266, 266, 266, 266, 266, 266, 266, 266, 266, 266, 266, 266, 266, 266,
51 | 266, 266, 266, 266, 266, 266, 266, 264, 264, 264, 264, 264, 264
52 | ]
53 | }
54 | ]
55 |
--------------------------------------------------------------------------------
/test/lib/inks/equation.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "x": [
4 | 530, 533, 537, 538, 540, 541, 542, 544, 545, 546, 548, 548, 552, 555, 558,
5 | 561, 565, 569, 573, 577, 582, 583, 585, 588, 589, 591, 593, 593, 593, 592,
6 | 591, 591, 590, 589, 588, 593, 598, 604, 613, 627, 646, 665, 682, 697, 713,
7 | 727, 738, 745, 751, 756, 762, 768, 772
8 | ],
9 | "y": [
10 | 93, 95, 99, 102, 107, 112, 117, 123, 127, 130, 133, 136, 138, 141, 143,
11 | 144, 144, 141, 136, 130, 124, 118, 112, 107, 101, 95, 90, 84, 79, 76, 72,
12 | 68, 64, 59, 55, 54, 54, 54, 54, 54, 52, 52, 49, 49, 49, 49, 49, 49, 49,
13 | 49, 49, 48, 48
14 | ]
15 | },
16 | {
17 | "x":
18 | [
19 | 669, 666, 665, 665, 668, 672, 676, 680, 684, 687, 689, 689, 690, 690, 688,
20 | 686, 684, 681, 678, 675, 670, 667, 664, 661, 658, 655, 654, 657, 661, 664,
21 | 668, 672, 677, 682, 689, 700, 712, 720, 723, 720
22 | ],
23 | "y": [
24 | 81, 80, 77, 74, 75, 76, 77, 79, 81, 84, 87, 90, 93, 97, 102, 105, 109,
25 | 112, 114, 118, 121, 123, 125, 127, 130, 133, 136, 137, 138, 138, 138, 138,
26 | 138, 137, 136, 136, 135, 133, 133, 132
27 | ]
28 | }
29 | ]
30 |
--------------------------------------------------------------------------------
/test/lib/inks/equation2.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "x": [
4 | 134, 137, 141, 145, 148, 153, 154, 157, 162, 165, 171, 173, 176, 178, 178,
5 | 178, 180, 182, 185, 187, 190, 191, 193, 195, 196, 196, 196, 196, 195, 194,
6 | 194, 193, 193, 192, 191, 191, 199, 213, 249, 271, 289, 321, 340, 360, 371,
7 | 428, 440, 465, 489, 528, 540, 560, 576, 598, 604, 620, 635, 651, 656, 661,
8 | 664, 667, 671
9 | ],
10 | "y": [
11 | 347, 347, 348, 352, 356, 366, 369, 374, 386, 393, 405, 409, 413, 416, 411,
12 | 400, 377, 365, 353, 341, 319, 312, 301, 290, 282, 265, 259, 248, 227, 223,
13 | 215, 194, 186, 180, 172, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169,
14 | 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169,
15 | 169, 169, 169
16 | ]
17 | },
18 | {
19 | "x": [
20 | 410, 406, 402, 399, 395, 394, 394, 394, 399, 408, 418, 429, 439, 443, 445,
21 | 445, 439, 432, 377, 365, 355, 350, 350, 354, 364, 387, 410, 426, 440, 452,
22 | 456, 459, 462
23 | ],
24 | "y": [
25 | 291, 290, 288, 286, 283, 280, 273, 269, 265, 264, 264, 264, 273, 289, 319,
26 | 332, 344, 352, 361, 362, 364, 365, 368, 370, 372, 374, 374, 374, 374, 374,
27 | 374, 374, 373
28 | ]
29 | },
30 | {
31 | "x": [
32 | 371, 434, 438, 441, 424, 413, 392, 322, 341, 353, 389, 402, 426, 498, 492,
33 | 448, 432, 418, 384, 359, 319, 324, 339, 411, 420, 439, 447, 457, 471, 474,
34 | 449, 433, 422, 402, 385, 371, 343, 356, 367, 391, 417, 445, 488, 382, 375,
35 | 370, 367, 395, 415, 465, 504, 530, 520, 503, 480, 467, 402, 393, 379, 368,
36 | 352, 349, 462, 465, 448, 428, 413, 409, 397, 380, 375, 372, 428, 438, 442,
37 | 448, 454, 465
38 | ],
39 | "y": [
40 | 238, 238, 239, 240, 249, 252, 254, 271, 272, 272, 272, 272, 272, 274, 277,
41 | 280, 280, 280, 280, 282, 289, 291, 291, 291, 291, 291, 291, 291, 292, 292,
42 | 301, 303, 303, 303, 304, 304, 310, 312, 313, 314, 314, 314, 314, 333, 335,
43 | 335, 337, 343, 345, 349, 349, 352, 353, 353, 353, 353, 353, 353, 355, 356,
44 | 359, 361, 364, 364, 373, 376, 378, 379, 381, 383, 383, 383, 387, 387, 387,
45 | 387, 387, 387
46 | ]
47 | },
48 | {
49 | "x": [
50 | 186, 202, 210, 228, 252, 419, 435, 468, 498, 524, 559, 567, 580, 586, 596,
51 | 606, 609, 612, 607, 392, 307, 280, 236, 198, 32, 28, 33, 60, 86, 130, 176,
52 | 397, 433, 442, 471, 490, 516, 520, 524, 303, 266, 209, 177, 159, 148, 145,
53 | 137, 126, 102, 143, 195, 263, 328, 583, 611, 616, 374, 290, 208, 143, 109,
54 | 90, 75, 72, 68, 62, 252, 336, 409, 442, 494, 520, 358, 289, 256, 193, 158,
55 | 148, 146, 278, 367, 416, 500, 557, 601, 504, 450, 417, 352, 281, 185, 180,
56 | 463, 512, 535, 543, 546, 497, 448, 382, 312, 245, 196, 158, 154, 151, 152,
57 | 192, 208, 254, 306, 364, 377, 396, 412, 423, 378, 347, 315, 289, 262, 211,
58 | 188, 168, 156, 151, 185, 220, 274, 439, 450, 453, 456, 449, 284, 261, 246,
59 | 234, 225, 211, 208, 204, 196, 189, 164, 184, 194, 219, 252, 296, 336, 350,
60 | 377, 399, 439, 458, 468
61 | ],
62 | "y": [
63 | 69, 67, 67, 67, 66, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 65,
64 | 66, 93, 93, 93, 93, 93, 115, 117, 120, 123, 123, 123, 123, 124, 124, 124,
65 | 124, 124, 128, 129, 129, 141, 147, 155, 161, 164, 168, 168, 170, 171, 177,
66 | 180, 180, 180, 180, 174, 174, 174, 191, 191, 194, 205, 210, 213, 217, 217,
67 | 220, 220, 228, 228, 228, 228, 228, 228, 242, 245, 247, 257, 264, 268, 271,
68 | 280, 280, 280, 280, 280, 281, 295, 295, 295, 295, 297, 304, 306, 309, 309,
69 | 309, 309, 308, 316, 321, 322, 322, 328, 334, 340, 341, 342, 352, 355, 355,
70 | 355, 355, 355, 355, 355, 355, 355, 357, 359, 364, 369, 372, 372, 372, 372,
71 | 374, 375, 381, 386, 391, 396, 396, 396, 396, 396, 396, 396, 396, 396, 397,
72 | 397, 397, 397, 397, 397, 398, 401, 402, 405, 410, 414, 418, 419, 422, 422,
73 | 422, 422, 422
74 | ]
75 | }
76 | ]
77 |
--------------------------------------------------------------------------------
/test/lib/inks/equation3.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "x": [
4 | 73, 74, 74, 75, 75, 76, 77, 82, 85, 89, 93, 95, 95, 95, 96, 96, 96, 96,
5 | 95, 94, 93, 92, 92, 92, 92, 92, 92, 92, 92, 92, 91, 87, 83, 78, 73, 69,
6 | 69, 69, 70, 72, 77, 81, 84, 90, 94, 97, 101, 107, 110, 113, 119, 122
7 | ],
8 | "y": [
9 | 168, 173, 176, 179, 182, 185, 188, 189, 188, 185, 182, 177, 173, 170, 165,
10 | 161, 164, 168, 172, 176, 181, 188, 191, 198, 205, 214, 224, 233, 240, 244,
11 | 249, 252, 252, 250, 246, 242, 238, 234, 231, 228, 223, 220, 219, 217, 214,
12 | 213, 211, 209, 208, 206, 202, 201
13 | ]
14 | },
15 | {
16 | "x": [176, 184, 189, 195, 199],
17 | "y": [171, 170, 168, 167, 166]
18 | },
19 | {
20 | "x": [170, 176, 185, 194, 199, 202, 205, 211, 214],
21 | "y": [189, 189, 189, 188, 187, 187, 187, 187, 187]
22 | },
23 | {
24 | "x": [
25 | 270, 276, 279, 282, 285, 286, 286, 286, 286, 283, 278, 274, 271, 267, 271,
26 | 274, 276, 277, 278, 278, 272, 269, 265, 262, 259, 255
27 | ],
28 | "y": [
29 | 145, 145, 145, 146, 149, 152, 156, 161, 164, 167, 168, 169, 170, 170, 170,
30 | 172, 175, 178, 181, 184, 187, 189, 191, 191, 192, 192
31 | ]
32 | },
33 | {
34 | "x": [
35 | 311, 314, 317, 321, 324, 327, 331, 335, 337, 337, 337, 334, 331, 326, 323,
36 | 320, 315
37 | ],
38 | "y": [
39 | 180, 175, 171, 168, 167, 167, 168, 171, 174, 177, 181, 183, 185, 186, 186,
40 | 187, 187
41 | ]
42 | },
43 | {
44 | "x": [349, 344, 341, 338, 336, 335, 335, 337, 340, 343, 347, 351, 354, 357],
45 | "y": [170, 170, 170, 172, 175, 178, 181, 184, 185, 186, 186, 186, 186, 186]
46 | },
47 | {
48 | "x": [381, 386, 393, 400, 403, 407, 410, 415, 419],
49 | "y": [181, 181, 181, 181, 180, 180, 180, 180, 180]
50 | },
51 | {
52 | "x": [402, 401, 401, 401, 401, 401, 401, 401],
53 | "y": [169, 175, 178, 181, 186, 190, 193, 196]
54 | },
55 | {
56 | "x": [
57 | 461, 457, 456, 455, 455, 461, 465, 469, 473, 476, 478, 479, 480, 481, 481,
58 | 481, 480, 478, 476, 473, 470, 465, 460, 457, 455, 462, 470, 476, 480, 485,
59 | 491, 495, 498
60 | ],
61 | "y": [
62 | 165, 160, 157, 152, 149, 147, 147, 147, 147, 147, 150, 154, 158, 162, 165,
63 | 168, 171, 174, 177, 181, 184, 184, 186, 187, 191, 193, 193, 193, 193, 193,
64 | 193, 193, 193
65 | ]
66 | }
67 | ]
68 |
--------------------------------------------------------------------------------
/test/lib/inks/fence.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "x": [
4 | 234, 237, 238, 239, 242, 244, 246, 247, 249, 250, 252, 253, 253, 253, 253,
5 | 253, 253, 253, 253, 253, 253, 253, 254, 255, 257, 258, 258, 262, 266, 273,
6 | 283, 297, 313, 325, 338, 345, 349, 353
7 | ],
8 | "y": [
9 | 90, 94, 98, 103, 110, 114, 118, 122, 125, 128, 131, 127, 123, 119, 114,
10 | 109, 105, 101, 98, 95, 91, 87, 82, 77, 71, 68, 65, 64, 64, 64, 64, 64, 64,
11 | 64, 64, 64, 64, 64
12 | ]
13 | },
14 | {
15 | "x": [
16 | 297, 301, 305, 310, 313, 317, 322, 324, 324, 324, 322, 319, 315, 311, 314,
17 | 318, 322, 326, 326, 326, 322, 317, 312, 307, 302, 298, 294, 290
18 | ],
19 | "y": [
20 | 82, 82, 82, 81, 81, 82, 83, 86, 89, 93, 96, 98, 98, 98, 100, 102, 105,
21 | 107, 110, 113, 114, 117, 118, 119, 121, 121, 121, 120
22 | ]
23 | },
24 | {
25 | "x": [
26 | 250, 250, 253, 255, 258, 260, 263, 266, 268, 270, 273, 274, 277, 277, 277,
27 | 277, 277, 278, 279, 280, 280, 280, 280, 286, 294, 302, 312, 326, 344, 365,
28 | 385, 401, 413, 416
29 | ],
30 | "y": [
31 | 198, 202, 209, 215, 222, 229, 237, 243, 250, 256, 262, 265, 260, 254, 246,
32 | 236, 223, 208, 198, 194, 189, 185, 182, 181, 181, 181, 181, 181, 181, 181,
33 | 181, 181, 181, 181
34 | ]
35 | },
36 | {
37 | "x": [
38 | 361, 358, 353, 349, 346, 342, 336, 334, 330, 328, 326, 324, 323, 323, 323,
39 | 323, 323, 326, 327, 330, 334, 337, 341, 345, 348, 350, 350, 347, 343, 340,
40 | 332, 327, 324, 319
41 | ],
42 | "y": [
43 | 212, 210, 209, 208, 208, 208, 211, 214, 218, 221, 225, 230, 234, 242, 247,
44 | 254, 258, 264, 267, 269, 270, 270, 268, 266, 262, 259, 254, 253, 251, 250,
45 | 248, 248, 248, 248
46 | ]
47 | },
48 | {
49 | "x": [
50 | 229, 226, 223, 220, 216, 213, 210, 206, 205, 202, 201, 200, 199, 199, 199,
51 | 199, 199, 199, 199, 199, 199, 199, 198, 196, 194, 190, 186, 182, 176, 169,
52 | 163, 156, 151, 146, 143, 139, 136, 134, 133, 133, 135, 138, 142, 146, 150,
53 | 151, 154, 156, 157, 157, 157, 157, 157, 157, 157, 154, 151, 148, 145, 140,
54 | 135, 134, 134, 134, 134, 135, 136, 136, 138, 144, 151, 162, 174, 183, 193,
55 | 202, 214, 222, 228, 233, 236, 241, 244
56 | ],
57 | "y": [
58 | 35, 34, 33, 33, 33, 33, 33, 35, 38, 41, 45, 48, 54, 64, 76, 87, 98, 107,
59 | 116, 130, 142, 155, 171, 186, 202, 219, 236, 252, 268, 278, 288, 295, 297,
60 | 295, 293, 291, 289, 286, 283, 278, 275, 275, 277, 283, 288, 294, 302, 312,
61 | 317, 321, 326, 331, 337, 342, 350, 358, 370, 385, 402, 432, 453, 470, 487,
62 | 503, 515, 528, 532, 535, 538, 539, 540, 540, 540, 540, 540, 539, 537, 535,
63 | 532, 530, 527, 525, 522
64 | ]
65 | }
66 | ]
67 |
--------------------------------------------------------------------------------
/test/lib/inks/hello.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "x": [
4 | 397, 400, 404, 408, 411, 414, 417, 418, 418, 419, 419, 418, 414, 412, 412,
5 | 412, 412, 412, 412, 412, 412, 412, 412, 412, 412, 412, 412, 413, 417, 418,
6 | 419, 419, 421, 425, 426
7 | ],
8 | "y": [
9 | 254, 249, 247, 242, 240, 235, 231, 227, 224, 220, 216, 211, 210, 213, 217,
10 | 221, 226, 230, 234, 238, 243, 247, 251, 255, 252, 248, 244, 241, 240, 243,
11 | 247, 251, 254, 253, 250
12 | ]
13 | },
14 | {
15 | "x": [427, 433, 437, 440, 439, 435, 430, 428, 428, 429, 432, 436, 440, 443],
16 | "y": [244, 248, 245, 242, 238, 238, 240, 245, 249, 253, 253, 253, 251, 251]
17 | },
18 | {
19 | "x": [
20 | 443, 450, 453, 456, 459, 460, 461, 461, 461, 460, 456, 452, 450, 450, 450,
21 | 450, 450, 450, 450, 450, 449, 449, 451, 455, 460, 463, 464
22 | ],
23 | "y": [
24 | 242, 242, 242, 238, 236, 233, 230, 226, 222, 217, 214, 214, 219, 223, 227,
25 | 231, 234, 238, 242, 246, 249, 253, 257, 257, 255, 252, 249
26 | ]
27 | },
28 | {
29 | "x": [
30 | 464, 466, 469, 471, 472, 474, 475, 473, 469, 465, 465, 465, 465, 465, 465,
31 | 465, 465, 465, 466, 467, 470, 474, 480, 482
32 | ],
33 | "y": [
34 | 243, 236, 234, 230, 227, 223, 219, 216, 215, 216, 220, 225, 229, 233, 237,
35 | 240, 244, 248, 252, 255, 256, 254, 250, 247
36 | ]
37 | },
38 | {
39 | "x": [
40 | 489, 483, 482, 482, 482, 485, 489, 494, 495, 495, 492, 490, 494, 497, 501,
41 | 504
42 | ],
43 | "y": [
44 | 238, 242, 245, 249, 253, 254, 252, 248, 244, 239, 238, 242, 242, 239, 238,
45 | 236
46 | ]
47 | }
48 | ]
49 |
--------------------------------------------------------------------------------
/test/lib/inks/one.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "x": [
4 | 368, 370, 374, 381, 388, 395, 401, 405, 409, 414, 418, 422, 425, 428, 433,
5 | 436, 437, 439, 440, 441, 442, 443, 443, 443, 443, 443, 443, 443, 443, 443,
6 | 443, 443, 443, 443, 443, 443, 443, 443, 443, 443, 443, 443, 443, 443, 443,
7 | 443, 443, 443, 443, 443, 443, 443, 443, 443, 443, 443, 443
8 | ],
9 | "y": [
10 | 134, 130, 126, 121, 115, 110, 106, 103, 98, 93, 90, 86, 82, 80, 73, 69,
11 | 66, 63, 60, 63, 68, 72, 76, 80, 83, 86, 91, 94, 98, 103, 107, 111, 117,
12 | 120, 124, 127, 131, 134, 138, 142, 147, 151, 154, 158, 163, 168, 172, 175,
13 | 179, 182, 185, 188, 191, 195, 199, 202, 205
14 | ]
15 | }
16 | ]
17 |
--------------------------------------------------------------------------------
/test/lib/inks/rabText.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "x": [
4 | 319, 316, 313, 310, 307, 304, 303, 302, 307, 315, 320, 327, 331, 334, 336,
5 | 333, 328, 323, 320, 317, 318, 323, 328, 331, 332, 331, 329, 336, 344, 348,
6 | 350, 349, 351, 354, 357, 360, 360, 364, 370, 380, 386, 394, 403, 411, 419,
7 | 421, 417, 409, 404, 400, 396, 391, 388, 387, 386, 386, 386, 386, 386, 386,
8 | 386, 386, 383, 377, 373, 370, 368, 371, 376, 383, 392, 409, 417, 421, 423,
9 | 420, 417, 414, 413, 415, 419, 423, 429, 439, 445, 450, 455, 459, 462, 462,
10 | 461, 458, 452, 449, 445, 442, 438, 435, 433, 433, 433, 435, 438, 440, 442,
11 | 442, 442, 442, 441, 440, 438, 436, 431, 430, 436, 443, 450, 461, 467, 471,
12 | 473, 474, 471, 468, 463, 461, 467, 473, 479, 484
13 | ],
14 | "y": [
15 | 329, 328, 328, 328, 329, 330, 333, 337, 341, 343, 343, 342, 341, 340, 336,
16 | 334, 335, 337, 339, 343, 347, 348, 346, 344, 341, 337, 334, 334, 332, 331,
17 | 334, 339, 342, 342, 341, 339, 335, 335, 335, 334, 333, 330, 327, 324, 318,
18 | 315, 308, 304, 303, 303, 304, 308, 312, 317, 322, 328, 338, 344, 350, 357,
19 | 362, 366, 366, 359, 354, 350, 346, 345, 345, 345, 344, 342, 340, 338, 335,
20 | 331, 330, 332, 337, 340, 343, 343, 343, 340, 337, 333, 328, 324, 318, 314,
21 | 310, 306, 304, 303, 304, 305, 308, 314, 320, 326, 334, 345, 351, 358, 363,
22 | 367, 371, 374, 370, 365, 359, 352, 345, 342, 341, 342, 342, 340, 338, 336,
23 | 333, 330, 328, 329, 331, 337, 342, 344, 345, 345
24 | ]
25 | }
26 | ]
27 |
--------------------------------------------------------------------------------
/test/lib/inks/rc_fr_simple.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "x": [77, 78, 80, 82, 82, 83, 84, 86, 86, 86, 86, 82],
4 | "y": [117, 121, 126, 131, 136, 144, 155, 166, 170, 175, 182, 175]
5 | },
6 | {
7 | "x": [
8 | 75, 79, 84, 107, 140, 173, 208, 240, 262, 275, 284, 292, 298, 304, 310,
9 | 316, 324, 331, 338, 348, 361, 371, 376, 380, 383, 386, 389, 392, 393, 393,
10 | 394, 394, 394, 394, 393, 393, 393, 392, 392, 390, 385, 367, 334, 312, 292,
11 | 280, 268, 261, 253, 245, 238, 229, 217, 211, 190, 180, 172, 166, 163, 153,
12 | 147, 140, 133, 128, 120, 113, 108, 104, 100, 97, 93, 90, 87, 88
13 | ],
14 | "y": [
15 | 112, 115, 116, 117, 118, 120, 119, 116, 114, 113, 111, 111, 110, 108, 107,
16 | 107, 106, 106, 106, 107, 109, 110, 112, 113, 115, 116, 117, 118, 121, 128,
17 | 136, 144, 151, 155, 160, 163, 166, 169, 172, 176, 178, 176, 173, 170, 170,
18 | 169, 168, 168, 168, 168, 168, 169, 171, 172, 173, 173, 173, 173, 173, 173,
19 | 173, 174, 174, 174, 174, 173, 171, 171, 170, 169, 169, 168, 168, 174
20 | ]
21 | },
22 | {
23 | "x": [
24 | 78, 81, 85, 88, 91, 95, 101, 109, 123, 133, 141, 148, 149, 149, 149, 149,
25 | 148, 149, 149, 149, 149, 149, 149, 149, 149, 148, 148, 146, 144, 143, 143,
26 | 147, 148, 151, 154, 157, 160, 164, 167, 171, 175, 178, 184, 188, 192, 194,
27 | 195, 194, 191, 188, 187, 186, 184, 183, 183, 184, 188, 191, 194, 200, 208,
28 | 214, 220, 225, 226, 225, 222, 221, 220, 216, 212, 205, 203, 201, 203, 206,
29 | 212, 220, 223, 228, 235, 239, 244, 246, 249, 250, 251, 251, 250, 248, 245,
30 | 242, 239, 237, 237, 238, 241, 244, 246, 248, 251, 255, 260, 263, 265, 267,
31 | 267, 266, 263, 260, 260, 258, 257, 259, 262, 265, 268, 269, 270, 270, 269,
32 | 268, 263, 262, 268, 273, 276, 280, 283, 287
33 | ],
34 | "y": [
35 | 252, 253, 253, 254, 254, 254, 252, 249, 243, 234, 224, 217, 212, 208, 204,
36 | 201, 206, 216, 224, 236, 248, 251, 256, 259, 263, 269, 272, 278, 275, 272,
37 | 267, 259, 256, 254, 253, 256, 259, 266, 270, 272, 272, 271, 264, 260, 255,
38 | 250, 246, 241, 239, 238, 246, 251, 257, 262, 265, 270, 275, 277, 278, 277,
39 | 271, 265, 256, 245, 232, 220, 212, 206, 201, 202, 209, 219, 227, 241, 252,
40 | 264, 272, 279, 280, 282, 279, 274, 266, 256, 243, 231, 218, 210, 204, 200,
41 | 199, 210, 224, 239, 250, 261, 270, 277, 283, 288, 291, 292, 286, 279, 272,
42 | 265, 261, 257, 255, 260, 264, 271, 277, 285, 288, 290, 288, 280, 272, 269,
43 | 261, 256, 254, 260, 262, 260, 258, 256, 255, 256
44 | ]
45 | }
46 | ]
47 |
--------------------------------------------------------------------------------
/test/lib/inks/shape.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "x": [
4 | 132, 129, 123, 119, 114, 111, 110, 110, 110, 110, 110, 110, 111, 113, 114,
5 | 117, 119, 123, 124, 131, 140, 149, 156, 161, 168, 192, 203, 209, 214, 219,
6 | 222, 223, 227, 228, 231, 231, 231, 231, 231, 231, 230, 229, 221, 219, 214,
7 | 211, 194, 191, 179, 173, 170, 164, 159, 154, 149, 140, 136, 133, 127, 123,
8 | 119, 117, 114, 113, 112, 111, 109
9 | ],
10 | "y": [
11 | 222, 222, 226, 227, 230, 233, 240, 244, 248, 252, 256, 262, 267, 272, 278,
12 | 283, 288, 293, 296, 301, 307, 310, 311, 311, 312, 310, 307, 302, 294, 287,
13 | 284, 281, 273, 269, 262, 258, 246, 241, 237, 233, 229, 226, 212, 209, 204,
14 | 201, 197, 196, 196, 196, 196, 197, 198, 198, 200, 204, 207, 207, 211, 214,
15 | 217, 220, 224, 228, 231, 236, 239
16 | ]
17 | },
18 | {
19 | "x": [
20 | 233, 239, 251, 254, 262, 276, 296, 323, 333, 342, 351, 387, 410, 418, 431,
21 | 444, 452, 459, 470, 494, 508, 511, 511, 511, 511, 511, 511, 510, 509, 509,
22 | 508, 507, 507, 507, 507, 507, 507, 506, 506, 506, 504, 503, 502, 502, 502,
23 | 502, 502, 502, 502, 502, 502, 502, 502, 502, 502, 502, 502, 503
24 | ],
25 | "y": [
26 | 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251,
27 | 251, 251, 251, 251, 251, 251, 252, 257, 261, 267, 272, 276, 280, 287, 291,
28 | 298, 304, 308, 313, 320, 324, 329, 333, 339, 346, 350, 360, 370, 377, 382,
29 | 388, 393, 398, 404, 408, 411, 422, 428, 431, 441, 447, 450, 454
30 | ]
31 | },
32 | {
33 | "x": [
34 | 439, 443, 446, 456, 460, 466, 473, 476, 491, 498, 503, 507, 518, 523, 526,
35 | 532, 554, 561, 567, 571, 574, 578, 586, 586, 586, 586, 586, 586, 586, 586,
36 | 586, 586, 586, 586, 586, 586, 586, 586, 586, 586, 586, 584, 582, 582, 579,
37 | 576, 570, 567, 564, 561, 552, 547, 543, 537, 531, 522, 510, 507, 503, 497,
38 | 492, 486, 483, 480, 477, 473, 470, 467, 464, 461, 457, 453, 449, 444, 441,
39 | 439, 439, 438, 437, 437, 436, 436, 434, 434, 434, 434, 437, 439, 441, 442,
40 | 442, 443, 443, 443, 443, 442, 442, 440
41 | ],
42 | "y": [
43 | 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 457,
44 | 457, 457, 457, 456, 456, 456, 456, 456, 461, 466, 469, 474, 478, 481, 488,
45 | 493, 496, 499, 503, 510, 513, 516, 521, 524, 527, 532, 538, 542, 547, 548,
46 | 548, 548, 548, 547, 546, 544, 544, 544, 544, 544, 544, 544, 544, 544, 546,
47 | 546, 546, 546, 546, 546, 546, 546, 547, 547, 547, 547, 546, 546, 544, 544,
48 | 540, 534, 531, 527, 524, 521, 518, 510, 506, 503, 499, 497, 492, 489, 486,
49 | 483, 480, 477, 474, 471, 468, 464, 461
50 | ]
51 | }
52 | ]
53 |
--------------------------------------------------------------------------------
/test/mocha/partial/00-configuration/Constants.spec.babel.js:
--------------------------------------------------------------------------------
1 | import { describe, it } from 'mocha'
2 | import { expect } from 'chai'
3 | import Constants from '../../../../src/configuration/Constants'
4 | import { testLogger } from '../../../../src/configuration/LoggerConfig'
5 |
6 | describe('Check constants', () => {
7 | testLogger.debug(Constants)
8 |
9 | const recognitionTypes = ['TEXT', 'MATH', 'DIAGRAM', 'RAWCONTENT']
10 | recognitionTypes.forEach((recognitionType) => {
11 | it(`Should have ${recognitionType} recognition type declared`, () => {
12 | let result = recognitionType
13 | if (recognitionType === 'RAWCONTENT') {
14 | result = 'Raw Content'
15 | }
16 | expect(Constants.RecognitionType[recognitionType]).to.equal(result)
17 | })
18 | })
19 |
20 | const protocols = ['REST', 'WEBSOCKET']
21 | protocols.forEach((protocol) => {
22 | it(`Should have ${protocol} protocol declared`, () => {
23 | expect(Constants.Protocol[protocol]).to.equal(protocol)
24 | })
25 | })
26 |
27 | const triggers = ['QUIET_PERIOD', 'POINTER_UP', 'DEMAND']
28 | triggers.forEach((trigger) => {
29 | it(`Should have ${trigger} trigger declared`, () => {
30 | expect(Constants.Trigger[trigger]).to.equal(trigger)
31 | })
32 | })
33 | })
34 |
--------------------------------------------------------------------------------
/test/mocha/partial/00-configuration/DefaultBehaviors.spec.babel.js:
--------------------------------------------------------------------------------
1 | import { describe, it } from 'mocha'
2 | import { assert } from 'chai'
3 | import configurations from '../../../lib/configuration'
4 | import * as DefaultConfiguration from '../../../../src/configuration/DefaultConfiguration'
5 | import * as DefaultBehaviors from '../../../../src/configuration/DefaultBehaviors'
6 |
7 | const defaultBehaviors = DefaultBehaviors.overrideDefaultBehaviors()
8 |
9 | configurations.forEach((configuration) => {
10 | const currentConfiguration = DefaultConfiguration.overrideDefaultConfiguration({ recognitionParams: configuration })
11 |
12 | describe(`Check behaviors for API ${currentConfiguration.recognitionParams.apiVersion} ${currentConfiguration.recognitionParams.type} ${currentConfiguration.recognitionParams.protocol}`, () => {
13 | const behavior = defaultBehaviors.getBehaviorFromConfiguration(defaultBehaviors, currentConfiguration)
14 |
15 | it('grabber', () => {
16 | assert.isDefined(behavior.grabber, 'grabber should be defined')
17 | })
18 |
19 | it('stroker', () => {
20 | assert.isDefined(behavior.stroker, 'stroker should be defined')
21 | const strokerType = currentConfiguration.recognitionParams.protocol === 'WEBSOCKET' ? 'svg' : 'canvas'
22 | assert.strictEqual(behavior.stroker.getInfo().type, strokerType)
23 | })
24 |
25 | it('renderer', () => {
26 | assert.isDefined(behavior.renderer, 'renderer should be defined')
27 | const rendererType = currentConfiguration.recognitionParams.protocol === 'WEBSOCKET' ? 'svg' : 'canvas'
28 | assert.strictEqual(behavior.renderer.getInfo().type, rendererType)
29 | })
30 |
31 | it('recognizer', () => {
32 | assert.isDefined(behavior.recognizer, 'recognizer should be defined')
33 | assert.include(behavior.recognizer.getInfo().types, currentConfiguration.recognitionParams.type)
34 | assert.strictEqual(behavior.recognizer.getInfo().protocol, currentConfiguration.recognitionParams.protocol)
35 | // assert.strictEqual(defaultBehaviors.optimizedParameters.exportContentTriggerOn, trigger, `${trigger} should be the default value for ${behavior} exportContentTriggerOn`);
36 | })
37 |
38 | it('events', () => {
39 | assert.isDefined(behavior.events, 'events should be defined')
40 | })
41 | })
42 | })
43 |
--------------------------------------------------------------------------------
/test/mocha/partial/00-configuration/DefaultConfiguration.spec.babel.js:
--------------------------------------------------------------------------------
1 | import { describe, it } from 'mocha'
2 | import { assert } from 'chai'
3 | import configurations from '../../../lib/configuration'
4 | import * as DefaultConfiguration from '../../../../src/configuration/DefaultConfiguration'
5 |
6 | configurations.forEach((configuration) => {
7 | describe(`Check configuration for API ${configuration.apiVersion} ${configuration.type} ${configuration.protocol}`, () => {
8 | const watcher = {
9 | update: (value) => {
10 | assert.equal('ja_JP', value)
11 | },
12 | prop: 'lang'
13 | }
14 | const currentConfiguration = DefaultConfiguration.overrideDefaultConfiguration({ recognitionParams: configuration }, watcher)
15 |
16 | it('type', () => {
17 | assert.isDefined(currentConfiguration.recognitionParams.type, 'type should be defined')
18 | assert.strictEqual(currentConfiguration.recognitionParams.type, configuration.type, `${currentConfiguration.recognitionParams.type} should be the default value for type`)
19 | })
20 |
21 | it('protocol', () => {
22 | assert.isDefined(currentConfiguration.recognitionParams.protocol, 'protocol should be defined')
23 | assert.strictEqual(currentConfiguration.recognitionParams.protocol, configuration.protocol, `${currentConfiguration.recognitionParams.protocol} should be the default value for protocol`)
24 | })
25 |
26 | it('apiVersion', () => {
27 | assert.isDefined(currentConfiguration.recognitionParams.apiVersion, 'apiVersion should be defined')
28 | assert.strictEqual(currentConfiguration.recognitionParams.apiVersion, configuration.apiVersion, `${currentConfiguration.recognitionParams.apiVersion} should be the default value for apiVersion`)
29 | })
30 |
31 | it('server', () => {
32 | assert.isDefined(currentConfiguration.recognitionParams.server, 'recognitionParams.server should keep its default value')
33 | })
34 |
35 | it('should notify language change', () => {
36 | currentConfiguration.recognitionParams.iink.lang = 'ja_JP'
37 | })
38 | })
39 | })
40 |
--------------------------------------------------------------------------------
/test/mocha/partial/00-configuration/LoggerConfig.spec.babel.js:
--------------------------------------------------------------------------------
1 | import { describe, it } from 'mocha'
2 | import { assert } from 'chai'
3 | import * as loggers from '../../../../src/configuration/LoggerConfig'
4 |
5 | describe('Check loggers definition and initialization', () => {
6 | it('module is define', () => {
7 | assert.notEqual(loggers, undefined)
8 | })
9 |
10 | const loggerList = ['grabber', 'editor', 'renderer', 'model', 'recognizer', 'test', 'util']
11 | loggerList.forEach((loggerName) => {
12 | const logger = loggers[`${loggerName}Logger`]
13 |
14 | it(`${loggerName}Logger is define`, () => {
15 | assert.notEqual(logger, undefined)
16 | })
17 | })
18 | })
19 |
--------------------------------------------------------------------------------
/test/mocha/partial/01-model/InkModel.spec.babel.js:
--------------------------------------------------------------------------------
1 | import { beforeEach, describe, it } from 'mocha'
2 | import { assert } from 'chai'
3 | import { testLogger as logger } from '../../../../src/configuration/LoggerConfig'
4 | import * as InkModel from '../../../../src/model/InkModel'
5 |
6 | describe('Testing InkModel', () => {
7 | describe('constructor', () => {
8 | let param
9 | let model
10 |
11 | beforeEach(() => {
12 | model = InkModel.createModel(param)
13 | })
14 |
15 | it('Check mandatory properties', () => {
16 | assert.property(model, 'currentStroke')
17 | assert.property(model, 'rawStrokes')
18 | assert.property(model, 'lastPositions')
19 | assert.nestedProperty(model, 'lastPositions.lastSentPosition')
20 | assert.nestedProperty(model, 'lastPositions.lastReceivedPosition')
21 | assert.property(model, 'defaultSymbols')
22 | assert.property(model, 'recognizedSymbols')
23 | assert.nestedProperty(model, 'rawResults.convert')
24 | assert.nestedProperty(model, 'rawResults.exports')
25 | assert.property(model, 'creationTime')
26 | assert.property(model, 'modificationTime')
27 | })
28 | })
29 | describe('workflow', () => {
30 | const model = InkModel.createModel()
31 |
32 | it('Creating a model and update pending strokes', () => {
33 | const updatedModel1 = InkModel.initPendingStroke(model, { x: 1, y: 1 })
34 | const updatedModel2 = InkModel.appendToPendingStroke(updatedModel1, { x: 2, y: 2 })
35 | const updatedModel3 = InkModel.appendToPendingStroke(updatedModel2, { x: 3, y: 3 })
36 | const updatedModel4 = InkModel.endPendingStroke(updatedModel3, { x: 4, y: 4 })
37 | logger.debug('Last model is ', updatedModel4)
38 | assert.deepEqual(model, updatedModel4)
39 | })
40 |
41 | it('Should clone model', () => {
42 | const copy = InkModel.cloneModel(model)
43 | assert.equal(model.currentStroke, copy.currentStroke)
44 | assert.sameDeepMembers(model.rawStrokes, copy.rawStrokes)
45 | assert.equal(model.lastPositions.lastReceivedPosition, copy.lastPositions.lastReceivedPosition)
46 | assert.equal(model.lastPositions.lastSentPosition, copy.lastPositions.lastSentPosition)
47 | assert.sameDeepMembers(model.defaultSymbols, copy.defaultSymbols)
48 | assert.equal(model.recognizedSymbols, copy.recognizedSymbols)
49 | assert.equal(model.rawResults.exports, copy.rawResults.exports)
50 | assert.equal(model.rawResults.convert, copy.rawResults.convert)
51 | assert.equal(model.creationTime, copy.creationTime)
52 | })
53 |
54 | it('Should merge models', () => {
55 | const modelToMerge = InkModel.cloneModel(model)
56 | modelToMerge.currentStroke = { x: 1, y: 1 }
57 |
58 | const mergedModel = InkModel.mergeModels(modelToMerge, model)
59 | assert.equal(mergedModel.recognizedSymbols, modelToMerge.recognizedSymbols)
60 | })
61 |
62 | // TODO Test all other function
63 | })
64 | })
65 |
--------------------------------------------------------------------------------
/test/mocha/partial/01-model/StrokeComponent.spec.babel.js:
--------------------------------------------------------------------------------
1 | import { beforeEach, describe, it } from 'mocha'
2 | import { assert } from 'chai'
3 | import * as StrokeComponent from '../../../../src/model/StrokeComponent'
4 |
5 | describe('Check StrokeComponent', () => {
6 | describe('constructor', () => {
7 | const obj = { color: '#000F55', width: 3, x: [10, 20], y: [30, 40], t: [50, 60] }
8 | Object.keys(obj).forEach((key) => {
9 | it(`Check custom constructor param ${key}`, () => {
10 | assert.property(stroke, key)
11 | assert.propertyVal(stroke, key, obj[key])
12 | })
13 | })
14 | let stroke
15 |
16 | beforeEach(() => {
17 | stroke = StrokeComponent.createStrokeComponent(obj)
18 | })
19 |
20 | it('Check mandatory properties', () => {
21 | assert.property(stroke, 'type')
22 | assert.propertyVal(stroke, 'type', 'stroke')
23 | assert.property(stroke, 'x')
24 | assert.property(stroke, 'y')
25 | assert.property(stroke, 't')
26 | assert.property(stroke, 'p')
27 | assert.property(stroke, 'l')
28 | assert.property(stroke, 'width')
29 | })
30 | it('Check toJSON function', () => {
31 | assert.deepEqual({ x: obj.x, y: obj.y, t: obj.t, pointerType: undefined }, StrokeComponent.toJSON(stroke))
32 | })
33 | })
34 |
35 | describe('workflow', () => {
36 | const stroke = StrokeComponent.createStrokeComponent()
37 |
38 | const pointsNb = 10
39 | const filledStroke = { type: 'stroke', x: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], y: [0, 2, 4, 6, 8, 10, 12, 14, 16, 18], t: [0, 3, 6, 9, 12, 15, 18, 21, 24, 27], p: [0.5, 0.8504651218778779, 0.6372367375521082, 0.6372367375521082, 0.6372367375521082, 0.6372367375521082, 0.6372367375521082, 0.6372367375521082, 0.6372367375521082, 0.6372367375521082], l: [0, 2.23606797749979, 4.47213595499958, 6.708203932499369, 8.94427190999916, 11.180339887498949, 13.416407864998739, 15.652475842498529, 17.88854381999832, 20.12461179749811], width: 0 }
40 | it(`Check addPoint (adding ${pointsNb} points)`, () => {
41 | for (let i = 0; i < pointsNb; i++) {
42 | StrokeComponent.addPoint(stroke, { x: i, y: i * 2, t: i * 3 })
43 | }
44 | assert.deepEqual(filledStroke, stroke)
45 | })
46 |
47 | const point = { x: 5, y: 10, t: 15, p: 0.6372367375521082, l: 11.180339887498949 }
48 | it('Check getPointByIndex', () => {
49 | assert.deepEqual(point, StrokeComponent.getPointByIndex(stroke, 5))
50 | })
51 |
52 | const slicedStroke = { type: 'stroke', x: [5, 6, 7, 8, 9], y: [10, 12, 14, 16, 18], t: [15, 18, 21, 24, 27], p: [0.5, 0.8504651218778779, 0.6372367375521082, 0.6372367375521082, 0.6372367375521082], l: [0, 2.23606797749979, 4.47213595499958, 6.708203932499369, 8.94427190999916], width: 0, color: undefined }
53 | it('Check slice', () => {
54 | assert.deepEqual(slicedStroke, StrokeComponent.slice(stroke, 5))
55 | })
56 | })
57 |
58 | // TODO Test all other function
59 | })
60 |
--------------------------------------------------------------------------------
/test/mocha/partial/01-model/UndoRedoManager.spec.babel.js:
--------------------------------------------------------------------------------
1 | import { describe, it } from 'mocha'
2 | import { assert } from 'chai'
3 | import * as InkModel from '../../../../src/model/InkModel'
4 | import * as UndoRedoContext from '../../../../src/model/UndoRedoContext'
5 | import * as UndoRedoManager from '../../../../src/model/UndoRedoManager'
6 | import * as DefaultConfiguration from '../../../../src/configuration/DefaultConfiguration'
7 |
8 | describe('Check undo/redo manager', () => {
9 | const configuration = DefaultConfiguration.overrideDefaultConfiguration()
10 | const undoRedoContext = UndoRedoContext.createUndoRedoContext(configuration)
11 | const maxSize = undoRedoContext.maxSize
12 |
13 | it('Should be empty', () => {
14 | assert.lengthOf(undoRedoContext.stack, 0)
15 | assert.equal(undoRedoContext.currentPosition, -1)
16 | assert.equal(undoRedoContext.maxSize, configuration.undoRedoMaxStackSize)
17 | })
18 |
19 | const count = maxSize
20 | it(`Should add ${count} models in stack`, (done) => {
21 | for (let i = 0; i < count; i++) {
22 | UndoRedoManager.updateModel(undoRedoContext, InkModel.createModel(configuration))
23 | }
24 | assert.lengthOf(undoRedoContext.stack, maxSize)
25 | assert.equal(undoRedoContext.currentPosition, maxSize - 1)
26 | UndoRedoManager.getModel(undoRedoContext)
27 | .then(({ res, types }) => {
28 | assert.isTrue(undoRedoContext.canUndo, 'Wrong canUndo state')
29 | assert.isFalse(undoRedoContext.canRedo, 'Wrong canRedo state')
30 | done()
31 | })
32 | })
33 |
34 | it(`Should undo and update current index to ${maxSize - 2}`, (done) => {
35 | UndoRedoManager.undo(undoRedoContext, undefined)
36 | .then(({ res, types }) => {
37 | assert.lengthOf(undoRedoContext.stack, maxSize)
38 | assert.equal(undoRedoContext.currentPosition, maxSize - 2)
39 | assert.isTrue(undoRedoContext.canUndo, 'Wrong canUndo state')
40 | assert.isTrue(undoRedoContext.canRedo, 'Wrong canRedo state')
41 | done()
42 | })
43 | })
44 |
45 | it(`Should redo and update current index to ${maxSize - 1}`, (done) => {
46 | UndoRedoManager.redo(undoRedoContext, undefined)
47 | .then(({ res, types }) => {
48 | assert.lengthOf(undoRedoContext.stack, maxSize)
49 | assert.equal(undoRedoContext.currentPosition, maxSize - 1)
50 | assert.isTrue(undoRedoContext.canUndo, 'Wrong canUndo state')
51 | assert.isFalse(undoRedoContext.canRedo, 'Wrong canRedo state')
52 | done()
53 | })
54 | })
55 | })
56 |
--------------------------------------------------------------------------------
/test/mocha/partial/02-behaviors/PointerEventGrabber.spec.babel.js:
--------------------------------------------------------------------------------
1 | import { describe, it } from 'mocha'
2 | import { assert } from 'chai'
3 | import * as sinon from 'sinon'
4 | import { testLogger as logger } from '../../../../src/configuration/LoggerConfig'
5 | import * as grabber from '../../../../src/grabber/PointerEventGrabber'
6 |
7 | describe('Testing the Grabber', () => {
8 | beforeEach(() => {
9 | global.document = { documentElement: { addEventListener: () => {} } }
10 | })
11 |
12 | after(() => {
13 | global.document = undefined
14 | })
15 |
16 | it('Test event registration', () => {
17 | const spiedEditor = { pointerUp: sinon.spy(), configuration: { capture: true } }
18 | const spiedDomDocument = { addEventListener: sinon.spy() }
19 | logger.debug('Attaching document to spied element')
20 | grabber.attach(spiedDomDocument, spiedEditor)
21 |
22 | assert.strictEqual(spiedDomDocument.addEventListener.callCount, 7, 'Not all events have been registered')
23 | })
24 |
25 | // TODO Add some tests sending events and checking that grabber behave as expected
26 | })
27 |
--------------------------------------------------------------------------------
/test/mocha/partial/CryptoHelper.spec.babel.js:
--------------------------------------------------------------------------------
1 | import { describe, it } from 'mocha'
2 | import { assert } from 'chai'
3 | import * as CryptoHelper from '../../../src/recognizer/CryptoHelper'
4 |
5 | describe('Hmac computation test', () => {
6 | it('nominal case', () => {
7 | const computedHmac = CryptoHelper.computeHmac('Message', 'Key')
8 | assert.equal(computedHmac, '7a2d9a9ad584ccbb3c110bf4e94d8dfef284eb258da89b2aeb01c43fa7e9d719a2b765af3f208f62ed36723d9562b9fe68a9f7e38b49e2ae6558deadcb274d8f')
9 | })
10 | })
11 |
--------------------------------------------------------------------------------
/test/playwright/02-ws-math-import.spec.js:
--------------------------------------------------------------------------------
1 | const { expect } = require('chai')
2 | const { exported, isEditorInitialized, playStrokes } = require('./helper')
3 | const { one } = require('../lib/inksDatas')
4 |
5 | describe(`${process.env.BROWSER}:v4/websocket_math_import_jiix.html`, () => {
6 | it('should test import', async () => {
7 | const editorEl = await page.waitForSelector('#editor')
8 | const isInit = await isEditorInitialized(editorEl)
9 | expect(isInit).to.equal(true)
10 |
11 | await playStrokes(page, one.strokes, 100, 100)
12 | await page.evaluate(exported)
13 |
14 | const latex = await editorEl.evaluate(node => node.editor.model.exports['application/x-latex'])
15 | expect(latex).to.equal(one.exports.LATEX[one.exports.LATEX.length - 1])
16 |
17 | const editorEl2 = await page.waitForSelector('#editor2')
18 | const isInit2 = await isEditorInitialized(editorEl2)
19 | expect(isInit2).to.equal(true)
20 |
21 | await page.click('#import')
22 | await page.evaluate(`(async () => {
23 | return new Promise((resolve, reject) => {
24 | document.getElementById('editor2').addEventListener('exported', (e) => {
25 | resolve('exported');
26 | });
27 | });
28 | })()`)
29 |
30 | const jiix = await editorEl2.evaluate(node => node.editor.model.exports['application/vnd.myscript.jiix'])
31 | const jiixParsed = JSON.parse(jiix)
32 | expect(one.exports.LATEX[one.exports.LATEX.length - 1]).to.equal(jiixParsed.expressions[0].label)
33 | })
34 | })
35 |
--------------------------------------------------------------------------------
/test/playwright/03-ws-text.spec.js:
--------------------------------------------------------------------------------
1 | const { expect } = require('chai')
2 | const { exported, isEditorInitialized, playStrokes } = require('./helper')
3 |
4 | const { helloHow, helloStrike } = require('../lib/inksDatas')
5 |
6 | describe(`${process.env.BROWSER}:v4/websocket_text_iink.html`, () => {
7 | it('should check smartguide', async () => {
8 | const editorEl = await page.waitForSelector('#editor')
9 | expect(await isEditorInitialized(editorEl)).to.equal(true)
10 |
11 | await playStrokes(page, helloHow.strokes, 0, 0)
12 | await page.evaluate(exported)
13 |
14 | const smartguide = await page.waitForSelector('.smartguide')
15 | const randomString = await smartguide.evaluate(node => node.id.replace('smartguide', ''))
16 |
17 | const prompterText = await page.waitForSelector('.prompter-text')
18 | let textContent = await prompterText.evaluate(node => node.textContent)
19 | const labelsWithNbsp = helloHow.exports.TEXT[helloHow.exports.TEXT.length - 1]
20 | .replace(/\s/g, '\u00A0')
21 |
22 | expect(labelsWithNbsp).to.equal(textContent)
23 |
24 | const ellipsis = await page.locator('.ellipsis')
25 | await ellipsis.click()
26 | const moreMenu = await page.locator('.more-menu')
27 | const convert = await moreMenu.locator('button:text("Convert")')
28 | await convert.click()
29 |
30 | await page.evaluate(exported)
31 |
32 | textContent = await prompterText.evaluate(node => node.textContent)
33 | expect(labelsWithNbsp).to.equal(textContent)
34 |
35 | const words = labelsWithNbsp.toString().split('\u00A0')
36 | // a random word in the smartGuide
37 | const wordIdx = Math.floor(Math.random() * words.length)
38 | await page.click('#word-' + (wordIdx * 2) + randomString)
39 |
40 | await page.waitForSelector(`#candidates${randomString}`)
41 | const candidates = await page.waitForSelector(`#candidates${randomString}`)
42 | const nbCand = await candidates.evaluate(node => node.getElementsByTagName('span').length)
43 |
44 | // a random candidate in the smartGuide
45 | const candIdx = Math.floor(Math.random() * nbCand)
46 | const candidateEl = await page.waitForSelector(`#cdt-${candIdx}${randomString}`)
47 | const candidateTextContent = await candidateEl.evaluate(node => node.textContent)
48 |
49 | const exportedPromise = page.evaluate(exported)
50 | await page.click(`#cdt-${candIdx}${randomString}`)
51 | await exportedPromise
52 |
53 | textContent = await prompterText.evaluate(node => node.textContent)
54 | expect(textContent.indexOf(candidateTextContent)).to.greaterThan(-1)
55 | })
56 |
57 | it('should check gesture works', async () => {
58 | const editorEl = await page.waitForSelector('#editor')
59 | expect(await isEditorInitialized(editorEl)).to.equal(true)
60 | await editorEl.evaluate(node => {
61 | const conf = JSON.parse(JSON.stringify(node.editor.configuration))
62 | conf.recognitionParams.iink.gesture = { enable: true }
63 | node.editor.configuration = conf
64 | })
65 | expect(await isEditorInitialized(editorEl)).to.equal(true)
66 |
67 | let exportPromise = page.evaluate(exported)
68 | await playStrokes(page, helloStrike.strokes, 0, 0)
69 | await exportPromise
70 | await page.waitForTimeout(1000)
71 | let result = await editorEl.evaluate(node => node.editor.model.exports['text/plain'])
72 | expect(result).to.equal('')
73 |
74 | await editorEl.evaluate(node => {
75 | const conf = JSON.parse(JSON.stringify(node.editor.configuration))
76 | conf.recognitionParams.iink.gesture = { enable: false }
77 | node.editor.configuration = conf
78 | })
79 | expect(await isEditorInitialized(editorEl)).to.equal(true)
80 |
81 | exportPromise = page.evaluate(exported)
82 | await playStrokes(page, helloStrike.strokes, 0, 0)
83 | await exportPromise
84 |
85 | result = await editorEl.evaluate(node => node.editor.model.exports['text/plain'])
86 | expect(result).not.equal('')
87 | })
88 | })
89 |
--------------------------------------------------------------------------------
/test/playwright/04-ws-text-hightlight-word.spec.js:
--------------------------------------------------------------------------------
1 | const { expect } = require('chai')
2 | const { exported, isEditorInitialized, playStrokes, findValuesByKey } = require('./helper')
3 |
4 | const { helloHowDecoHighlighted } = require('../lib/inksDatas')
5 |
6 | describe(`${process.env.BROWSER}:v4/websocket_text_highlight_words.html`, () => {
7 | it('should check text decorations', async () => {
8 | const editorEl = await page.waitForSelector('#editor')
9 | const isInit = await isEditorInitialized(editorEl)
10 | expect(isInit).to.equal(true)
11 |
12 | await playStrokes(page, helloHowDecoHighlighted.strokes, 100, 100)
13 | await page.evaluate(exported)
14 |
15 | const plainText = await editorEl.evaluate(node => node.editor.model.exports['text/plain'])
16 | expect(plainText).to.equal(helloHowDecoHighlighted.exports.TEXT[helloHowDecoHighlighted.exports.TEXT.length - 1])
17 |
18 | const smartguide = await page.waitForSelector('.smartguide')
19 | const randomString = await smartguide.evaluate(node => node.id.replace('smartguide', ''))
20 |
21 | await page.click(`#ellipsis${randomString}`)
22 | await page.click(`#convert${randomString}`)
23 |
24 | const jiix = await editorEl.evaluate(node => node.editor.model.exports['application/vnd.myscript.jiix'])
25 | const spanList = findValuesByKey(jiix, 'spans')
26 | expect(spanList.length).to.equal(2)
27 |
28 | const span0 = spanList[0]
29 | expect(span0['first-char']).to.equal(0)
30 | expect(span0['last-char']).to.equal(4)
31 | expect(span0.class).to.equal('text')
32 |
33 | const span1 = spanList[1]
34 | expect(span1['first-char']).to.equal(6)
35 | expect(span1['last-char']).to.equal(8)
36 | if (helloHowDecoHighlighted.name.includes('Highlighted')) {
37 | expect(span1.class).to.equal('text text-highlight')
38 | } else {
39 | expect(span1.class).to.equal('text text-emphasis1')
40 | }
41 | })
42 | })
43 |
--------------------------------------------------------------------------------
/test/playwright/05-rest-text.spec.js:
--------------------------------------------------------------------------------
1 | const { expect } = require('chai')
2 | const { exported, isEditorInitialized, playStrokes } = require('./helper')
3 |
4 | const { hellov4rest } = require('../lib/inksDatas')
5 |
6 | describe(`${process.env.BROWSER}:v4/rest_text_iink.html`, () => {
7 | it('should test labels', async () => {
8 | const editorEl = await page.waitForSelector('#editor')
9 |
10 | expect(await isEditorInitialized(editorEl)).to.equal(true)
11 |
12 | await playStrokes(page, hellov4rest.strokes, 100, 100)
13 | await page.evaluate(exported)
14 | await page.waitForTimeout(1000)
15 | const plainText = await editorEl.evaluate(node => node.editor.model.exports['text/plain'])
16 | expect(plainText).to.equal(hellov4rest.exports.TEXT[hellov4rest.exports.TEXT.length - 1])
17 | })
18 |
19 | it('should test undo/redo with REST', async () => {
20 | const editorEl = await page.waitForSelector('#editor')
21 | const isInit = await isEditorInitialized(editorEl)
22 | expect(isInit).to.equal(true)
23 |
24 | await playStrokes(page, hellov4rest.strokes, 100, 100)
25 | await page.evaluate(exported)
26 | await page.waitForTimeout(1000)
27 | let raw = await editorEl.evaluate(node => node.editor.model.rawStrokes)
28 | expect(raw.length).to.equal(hellov4rest.strokes.length)
29 | const plain = await editorEl.evaluate(node => node.editor.model.exports['text/plain'])
30 | expect(plain).to.equal(hellov4rest.exports.TEXT[hellov4rest.exports.TEXT.length - 1])
31 |
32 | const clearClick = page.click('#clear')
33 | let exportedEvent = page.evaluate(exported)
34 | await Promise.all([clearClick, exportedEvent])
35 | const exports = await editorEl.evaluate(node => node.editor.model.exports)
36 | expect(exports).to.equal(undefined)
37 |
38 | let undoClick = page.click('#undo')
39 | exportedEvent = page.evaluate(exported)
40 | await Promise.all([undoClick, exportedEvent])
41 | raw = await editorEl.evaluate(node => node.editor.model.rawStrokes)
42 | expect(raw.length).to.equal(hellov4rest.strokes.length)
43 |
44 | undoClick = page.click('#undo')
45 | exportedEvent = page.evaluate(exported)
46 | await Promise.all([undoClick, exportedEvent])
47 | raw = await editorEl.evaluate(node => node.editor.model.rawStrokes)
48 | expect(raw.length).to.equal(hellov4rest.strokes.length - 1)
49 |
50 | undoClick = page.click('#undo')
51 | exportedEvent = page.evaluate(exported)
52 | await Promise.all([undoClick, exportedEvent])
53 | raw = await editorEl.evaluate(node => node.editor.model.rawStrokes)
54 | expect(raw.length).to.equal(hellov4rest.strokes.length - 2)
55 |
56 | undoClick = page.click('#redo')
57 | exportedEvent = page.evaluate(exported)
58 | await Promise.all([undoClick, exportedEvent])
59 | raw = await editorEl.evaluate(node => node.editor.model.rawStrokes)
60 | expect(raw.length).to.equal(hellov4rest.strokes.length - 1)
61 | })
62 | })
63 |
--------------------------------------------------------------------------------
/test/playwright/06-ws-math-custom-resources.spec.js:
--------------------------------------------------------------------------------
1 | const { expect } = require('chai')
2 | const { exported, isEditorInitialized, playStrokes } = require('./helper')
3 |
4 | const equation = require('../lib/inksDatas')['3times2']
5 |
6 | describe(`${process.env.BROWSER}:v4/websocket_math_custom_resources.html`, () => {
7 | it('should test recognition asset builder', async () => {
8 | const editorEl = await page.waitForSelector('#editor')
9 | const isInit = await isEditorInitialized(editorEl)
10 | expect(isInit).to.equal(true)
11 |
12 | await playStrokes(page, equation.strokes, 200, 200)
13 | await page.evaluate(exported)
14 |
15 | const latex = await editorEl.evaluate(node => node.editor.model.exports['application/x-latex'])
16 | expect(latex).to.equal(equation.exports.LATEX[equation.exports.LATEX.length - 1])
17 | })
18 | })
19 |
--------------------------------------------------------------------------------
/test/playwright/07-ws-text-custom-lexicon.spec.js:
--------------------------------------------------------------------------------
1 | const { expect } = require('chai')
2 | const { exported, isEditorInitialized, playStrokes } = require('./helper')
3 |
4 | const { rabText } = require('../lib/inksDatas')
5 |
6 | describe(`${process.env.BROWSER}:v4/websocket_text_custom_lexicon.html`, () => {
7 | it('should test recognition asset builder lexicon', async () => {
8 | const editorEl = await page.waitForSelector('#editor')
9 | const isInit = await isEditorInitialized(editorEl)
10 | expect(isInit).to.equal(true)
11 |
12 | await page.waitForSelector('#lexicon')
13 | await page.type('#lexicon', 'covfefe')
14 | await page.click('#reinit')
15 | await playStrokes(page, rabText.strokes, 100, 100)
16 | await page.evaluate(exported)
17 |
18 | const prompterText = await page.waitForSelector('.prompter-text')
19 | const textContent = await prompterText.evaluate(node => node.textContent)
20 | expect(textContent).to.equal(rabText.exports.TEXT[0])
21 | })
22 | })
23 |
--------------------------------------------------------------------------------
/test/playwright/08-rest-raw-content.spec.js:
--------------------------------------------------------------------------------
1 | const { expect } = require('chai')
2 | const { exported, isEditorInitialized, playStrokes } = require('./helper')
3 |
4 | const { rawContentFr } = require('../lib/inksDatas')
5 |
6 | describe(`${process.env.BROWSER}:v4/rest_raw_content_iink.html`, () => {
7 | it('should test raw content on rest text', async () => {
8 | const editorEl = await page.waitForSelector('#editor')
9 | const isInit = await isEditorInitialized(editorEl)
10 | expect(isInit).to.equal(true)
11 |
12 | await playStrokes(page, rawContentFr.strokes, 200, 200)
13 | await page.evaluate(exported)
14 |
15 | const jiix = await editorEl.evaluate(node => node.editor.model.exports['application/vnd.myscript.jiix'])
16 | const parsed = JSON.parse(JSON.stringify(jiix))
17 |
18 | expect(parsed.type).to.equal('Raw Content')
19 | expect(parsed.elements.length > 0).to.equal(true)
20 |
21 | let nonTextFound = false
22 | let textFound = ''
23 | parsed.elements.forEach((element) => {
24 | if (element.type === 'Raw Content' && element.kind === 'non-text') {
25 | nonTextFound = true
26 | }
27 | if (element.type === 'Text') {
28 | textFound = element.label
29 | }
30 | })
31 |
32 | expect(nonTextFound).to.equal(true)
33 | expect(textFound.length > 0).to.equal(true)
34 | })
35 | })
36 |
--------------------------------------------------------------------------------
/test/playwright/09-change-configuration.spec.js:
--------------------------------------------------------------------------------
1 | const { expect } = require('chai')
2 | const { exported, isEditorInitialized, playStrokes, getStrokesFromJIIX } = require('./helper')
3 |
4 | const { hello, equation3 } = require('../lib/inksDatas')
5 |
6 | async function openCard (title) {
7 | return await page.click(`text=${title}`)
8 | }
9 |
10 | describe(`${process.env.BROWSER}:non-version-specific/change_configuration.html`, () => {
11 | it('should test default configuration', async () => {
12 | const editorEl = await page.waitForSelector('#editor')
13 | const isInit = await isEditorInitialized(editorEl)
14 | expect(isInit).to.equal(true)
15 |
16 | // expect(await page.inputValue('#server-scheme')).to.equal('https')
17 | // expect(await page.inputValue('#server-host')).to.equal('webdemoapi.myscript.com')
18 | expect(await page.inputValue('#recognition-type')).to.equal('TEXT')
19 | expect(await page.inputValue('#recognition-type')).to.equal('TEXT')
20 | expect(await page.inputValue('#recognition-protocol')).to.equal('WEBSOCKET')
21 | expect(await page.inputValue('#iink-language')).to.equal('en_US')
22 | expect(await page.isChecked('#iink-smartGuide')).to.equal(true)
23 | expect(await page.isChecked('#iink-guides')).to.equal(true)
24 | expect(await page.inputValue('#triggers-delay')).to.equal('2000')
25 | expect(await page.inputValue('#triggers-exportContent')).to.equal('POINTER_UP')
26 |
27 | await playStrokes(page, hello.strokes, 0, 0)
28 | await page.evaluate(exported)
29 | // ugly but useful for webkit & firefox
30 | await page.waitForTimeout(500)
31 |
32 | const plainText = await editorEl.evaluate(node => node.editor.model.exports['text/plain'])
33 | expect(plainText).to.equal(hello.exports.TEXT[hello.exports.TEXT.length - 1])
34 | })
35 |
36 | it('should test WEBSOCKET MATH config', async () => {
37 | let editorEl = await page.waitForSelector('#editor')
38 | let isInit = await isEditorInitialized(editorEl)
39 | expect(isInit).to.equal(true)
40 |
41 | await openCard('Recognition params')
42 | await page.selectOption('#recognition-type', 'MATH')
43 |
44 | expect(await page.inputValue('#recognition-type')).to.equal('MATH')
45 | expect(await page.inputValue('#recognition-protocol')).to.equal('WEBSOCKET')
46 | expect(await page.inputValue('#iink-language')).to.equal('en_US')
47 | expect(await page.isChecked('#iink-smartGuide')).to.equal(true)
48 | expect(await page.isChecked('#iink-guides')).to.equal(true)
49 | expect(await page.inputValue('#triggers-delay')).to.equal('2000')
50 | expect(await page.inputValue('#triggers-exportContent')).to.equal('POINTER_UP')
51 |
52 | await page.click('#valid-btn')
53 |
54 | editorEl = await page.waitForSelector('#editor')
55 | isInit = await isEditorInitialized(editorEl)
56 | expect(isInit).to.equal(true)
57 |
58 | await playStrokes(page, equation3.strokes, 0, 0)
59 | await page.evaluate(exported)
60 |
61 | const jiix = await editorEl.evaluate(node => node.editor.model.exports['application/vnd.myscript.jiix'])
62 | expect(getStrokesFromJIIX(jiix).length).to.equal(equation3.strokes.length)
63 |
64 | const latex = await editorEl.evaluate(node => node.editor.model.exports['application/x-latex'])
65 | expect(latex).to.equal(equation3.exports.LATEX[equation3.exports.LATEX.length - 1])
66 | })
67 | })
68 |
--------------------------------------------------------------------------------
/test/playwright/10-ws-text-big-text.spec.js:
--------------------------------------------------------------------------------
1 | const { expect } = require('chai')
2 | const { exported, isEditorInitialized, playStrokes } = require('./helper')
3 |
4 | const { bigText } = require('../lib/inksDatas')
5 |
6 | describe(`${process.env.BROWSER}:v4/websocket_text_iink_no_guides.html`, () => {
7 | it('should get the correct number of strokes', async () => {
8 | const editorEl = await page.waitForSelector('#editor')
9 | const isInit = await isEditorInitialized(editorEl)
10 | expect(isInit).to.equal(true)
11 |
12 | await playStrokes(page, bigText.strokes, 100, 100)
13 | await page.evaluate(exported)
14 |
15 | const nbStrokes = bigText.strokes.length
16 | const modelLocator = await page.locator('(//*[@data-layer="MODEL"])')
17 | const pathModelLocator = await modelLocator.locator('path')
18 | expect(await pathModelLocator.count()).to.equal(nbStrokes)
19 | }).timeout(90000)
20 | })
21 |
--------------------------------------------------------------------------------
/test/playwright/helper/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | *
3 | * @param obj
4 | * @param key
5 | * @param list
6 | * @returns {*}
7 | */
8 | function findValuesHelper (obj, key, list) {
9 | let listRef = list
10 | if (!obj) return listRef
11 | if (obj instanceof Array) {
12 | Object.keys(obj).forEach((k) => {
13 | listRef = listRef.concat(findValuesHelper(obj[k], key, []))
14 | })
15 | return listRef
16 | }
17 | if (obj[key]) {
18 | if (obj[key] instanceof Array) {
19 | Object.keys(obj[key]).forEach((l) => {
20 | listRef.push(obj[key][l])
21 | })
22 | } else {
23 | listRef.push(obj[key])
24 | }
25 | }
26 |
27 | if (typeof obj === 'object') {
28 | const children = Object.keys(obj)
29 | if (children.length > 0) {
30 | children.forEach((child) => {
31 | listRef = listRef.concat(findValuesHelper(obj[child], key, []))
32 | })
33 | }
34 | }
35 | return listRef
36 | }
37 |
38 | /**
39 | *
40 | * @param obj
41 | * @param key
42 | * @returns {*}
43 | */
44 | function findValuesByKey (obj, key) {
45 | return findValuesHelper(JSON.parse(obj), key, [])
46 | }
47 |
48 | /**
49 | *
50 | * @param jiix
51 | * @returns {*}
52 | */
53 | function getStrokesFromJIIX (jiix) {
54 | const itemsList = findValuesByKey(jiix, 'items')
55 | return itemsList.filter(item => item.type === 'stroke')
56 | }
57 |
58 | /**
59 | *
60 | * @param page
61 | * @param strokes
62 | * @param offsetX
63 | * @param offsetY
64 | * @returns {Promise}
65 | */
66 | async function playStrokes (page, strokes, offsetX, offsetY) {
67 | const offsetXRef = offsetX || 0
68 | const offsetYRef = offsetY || 0
69 |
70 | for (const { x, y, t } of strokes) {
71 | const hasTimeStamp = t && t.length === x.length
72 | await page.mouse.move(offsetXRef + x[0], offsetYRef + y[0])
73 | await page.mouse.down()
74 |
75 | let oldTimestamp = hasTimeStamp ? t[0] : null
76 | for (let p = 0; p < x.length; p++) {
77 | let waitTime = 0
78 | if (hasTimeStamp) {
79 | waitTime = t[p] - oldTimestamp
80 | oldTimestamp = t[p]
81 | }
82 | await page.waitForTimeout(waitTime)
83 | await page.mouse.move(offsetXRef + x[p], offsetYRef + y[p])
84 | }
85 | await page.mouse.up()
86 | await page.waitForTimeout(100)
87 | }
88 | }
89 |
90 | /**
91 | * @param page
92 | * @returns {Promise}
93 | */
94 | async function isEditorInitialized (editorEl) {
95 | await editorEl.evaluate(node => node.editor.recognizerContext.initPromise)
96 | return await editorEl.evaluate(node => node.editor.initialized)
97 | }
98 |
99 | const exported = `(async () => {
100 | return new Promise((resolve, reject) => {
101 | document.getElementById('editor').addEventListener('exported', (e) => {
102 | resolve('exported');
103 | });
104 | });
105 | })()`
106 |
107 | module.exports = {
108 | getStrokesFromJIIX,
109 | playStrokes,
110 | findValuesByKey,
111 | isEditorInitialized,
112 | exported
113 | }
114 |
--------------------------------------------------------------------------------
/test/playwright/helper/mochaHooks.js:
--------------------------------------------------------------------------------
1 | const { chromium, webkit, firefox } = require('playwright')
2 |
3 | exports.mochaHooks = {
4 | async beforeAll () {
5 | const browserName = process.env.BROWSER || 'firefox'
6 | let args = []
7 | if (browserName === 'chromium') {
8 | args = ['--shm-size=5gb', '--disable-dev-shm-usage', '--no-sandbox', '--disable-setuid-sandbox']
9 | }
10 | global.browser = await { chromium, webkit, firefox }[browserName].launch({ headless: JSON.parse(process.env.HEADLESS), args })
11 | const context = await browser.newContext()
12 | global.page = await context.newPage()
13 | return Promise.resolve()
14 | },
15 | async beforeEach () {
16 | const exampleFilePath = this.currentTest.parent.title.split(':')[1]
17 | return await global.page.goto(`${process.env.LAUNCH_URL}/examples/${exampleFilePath}`)
18 | },
19 | async afterEach () {
20 | if (process.env.SCREEN_SHOT) {
21 | const [browserName, exampleFilePath] = this.currentTest.parent.title.split(':')
22 | return await global.page.screenshot({ fullPage: false, path: 'test/playwright/screenshots/' + browserName + '/' + exampleFilePath + '.png' })
23 | }
24 | },
25 | async afterAll () {
26 | return await global.browser.close()
27 | }
28 | }
29 |
--------------------------------------------------------------------------------