├── .env.template ├── docs ├── logo.png ├── fonts │ ├── OpenSans-Bold-webfont.eot │ ├── OpenSans-Bold-webfont.woff │ ├── OpenSans-Light-webfont.eot │ ├── OpenSans-Italic-webfont.eot │ ├── OpenSans-Italic-webfont.woff │ ├── OpenSans-Light-webfont.woff │ ├── OpenSans-Regular-webfont.eot │ ├── OpenSans-Regular-webfont.woff │ ├── Montserrat │ │ ├── Montserrat-Bold.eot │ │ ├── Montserrat-Bold.ttf │ │ ├── Montserrat-Bold.woff │ │ ├── Montserrat-Bold.woff2 │ │ ├── Montserrat-Regular.eot │ │ ├── Montserrat-Regular.ttf │ │ ├── Montserrat-Regular.woff │ │ └── Montserrat-Regular.woff2 │ ├── OpenSans-BoldItalic-webfont.eot │ ├── OpenSans-BoldItalic-webfont.woff │ ├── OpenSans-LightItalic-webfont.eot │ ├── OpenSans-LightItalic-webfont.woff │ └── Source-Sans-Pro │ │ ├── sourcesanspro-light-webfont.eot │ │ ├── sourcesanspro-light-webfont.ttf │ │ ├── sourcesanspro-light-webfont.woff │ │ ├── sourcesanspro-light-webfont.woff2 │ │ ├── sourcesanspro-regular-webfont.eot │ │ ├── sourcesanspro-regular-webfont.ttf │ │ ├── sourcesanspro-regular-webfont.woff │ │ └── sourcesanspro-regular-webfont.woff2 ├── scripts │ ├── polyfill.js │ ├── nav.js │ ├── linenumber.js │ ├── collapse.js │ ├── prettify │ │ └── lang-css.js │ └── search.js ├── styles │ ├── prettify.css │ ├── prettify-jsdoc.css │ ├── prettify-tomorrow.css │ └── jsdoc-default.css ├── tutorial-tutorial.html └── SideeX_file.html ├── src ├── global.d.ts ├── common │ ├── seleniumError.ts │ ├── storage.js │ └── patternMatcher.ts ├── utils │ ├── timeoutError.ts │ ├── Point.ts │ ├── Rectangle.ts │ └── windowListeners.ts ├── background │ ├── storage-initialization.js │ └── background.js ├── page │ ├── runScript.js │ ├── onsubmit.js │ ├── autoWait.js │ ├── keys.js │ └── prompt.js ├── content │ ├── content-initialization.js │ ├── message-controller.js │ ├── pageScript-injecter.js │ ├── prompt-injecter.js │ ├── targetSelecter.ts │ └── command-receiver-for-api.js ├── panel │ └── js │ │ └── background │ │ ├── log.js │ │ ├── initial.js │ │ ├── setting.js │ │ ├── preRecorder.js │ │ ├── variable-controller.js │ │ └── preprocessor.js └── manifest.json ├── static └── logo.png ├── custom-template ├── tmpl │ ├── example.tmpl │ ├── source.tmpl │ ├── type.tmpl │ ├── modifies.tmpl │ ├── augments.tmpl │ ├── examples.tmpl │ ├── returns.tmpl │ ├── mainpage.tmpl │ ├── tutorial.tmpl │ ├── exceptions.tmpl │ ├── members.tmpl │ ├── properties.tmpl │ ├── params.tmpl │ ├── layout.tmpl │ ├── method.tmpl │ ├── details.tmpl │ └── container.tmpl ├── static │ ├── fonts │ │ ├── Montserrat │ │ │ ├── Montserrat-Bold.eot │ │ │ ├── Montserrat-Bold.ttf │ │ │ ├── Montserrat-Bold.woff │ │ │ ├── Montserrat-Bold.woff2 │ │ │ ├── Montserrat-Regular.eot │ │ │ ├── Montserrat-Regular.ttf │ │ │ ├── Montserrat-Regular.woff │ │ │ └── Montserrat-Regular.woff2 │ │ └── Source-Sans-Pro │ │ │ ├── sourcesanspro-light-webfont.eot │ │ │ ├── sourcesanspro-light-webfont.ttf │ │ │ ├── sourcesanspro-light-webfont.woff │ │ │ ├── sourcesanspro-light-webfont.woff2 │ │ │ ├── sourcesanspro-regular-webfont.eot │ │ │ ├── sourcesanspro-regular-webfont.ttf │ │ │ ├── sourcesanspro-regular-webfont.woff │ │ │ └── sourcesanspro-regular-webfont.woff2 │ ├── scripts │ │ ├── polyfill.js │ │ ├── nav.js │ │ ├── linenumber.js │ │ ├── collapse.js │ │ ├── prettify │ │ │ └── lang-css.js │ │ └── search.js │ └── styles │ │ └── prettify.css ├── package.json ├── CHANGELOG.md ├── LICENSE.md └── README.md ├── postcss.config.js ├── webpack.api.config.js ├── tutorials └── tutorial.md ├── jsdoc.json ├── webpack.browser.config.js ├── .gitignore ├── .babelrc ├── .eslintrc.json ├── webpack.common.js ├── package.json ├── README.md └── tsconfig.json /.env.template: -------------------------------------------------------------------------------- 1 | NODE_ENV="development" 2 | #NODE_ENV="production" -------------------------------------------------------------------------------- /docs/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SideeX/sideex-api/HEAD/docs/logo.png -------------------------------------------------------------------------------- /src/global.d.ts: -------------------------------------------------------------------------------- 1 | declare const browser: import("webextension-polyfill-ts").Browser -------------------------------------------------------------------------------- /static/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SideeX/sideex-api/HEAD/static/logo.png -------------------------------------------------------------------------------- /custom-template/tmpl/example.tmpl: -------------------------------------------------------------------------------- 1 | 2 |
3 | -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: [ 3 | require('autoprefixer') 4 | ] 5 | }; 6 | -------------------------------------------------------------------------------- /docs/fonts/OpenSans-Bold-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SideeX/sideex-api/HEAD/docs/fonts/OpenSans-Bold-webfont.eot -------------------------------------------------------------------------------- /docs/fonts/OpenSans-Bold-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SideeX/sideex-api/HEAD/docs/fonts/OpenSans-Bold-webfont.woff -------------------------------------------------------------------------------- /docs/fonts/OpenSans-Light-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SideeX/sideex-api/HEAD/docs/fonts/OpenSans-Light-webfont.eot -------------------------------------------------------------------------------- /docs/fonts/OpenSans-Italic-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SideeX/sideex-api/HEAD/docs/fonts/OpenSans-Italic-webfont.eot -------------------------------------------------------------------------------- /docs/fonts/OpenSans-Italic-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SideeX/sideex-api/HEAD/docs/fonts/OpenSans-Italic-webfont.woff -------------------------------------------------------------------------------- /docs/fonts/OpenSans-Light-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SideeX/sideex-api/HEAD/docs/fonts/OpenSans-Light-webfont.woff -------------------------------------------------------------------------------- /docs/fonts/OpenSans-Regular-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SideeX/sideex-api/HEAD/docs/fonts/OpenSans-Regular-webfont.eot -------------------------------------------------------------------------------- /docs/fonts/OpenSans-Regular-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SideeX/sideex-api/HEAD/docs/fonts/OpenSans-Regular-webfont.woff -------------------------------------------------------------------------------- /docs/fonts/Montserrat/Montserrat-Bold.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SideeX/sideex-api/HEAD/docs/fonts/Montserrat/Montserrat-Bold.eot -------------------------------------------------------------------------------- /docs/fonts/Montserrat/Montserrat-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SideeX/sideex-api/HEAD/docs/fonts/Montserrat/Montserrat-Bold.ttf -------------------------------------------------------------------------------- /docs/fonts/Montserrat/Montserrat-Bold.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SideeX/sideex-api/HEAD/docs/fonts/Montserrat/Montserrat-Bold.woff -------------------------------------------------------------------------------- /docs/fonts/OpenSans-BoldItalic-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SideeX/sideex-api/HEAD/docs/fonts/OpenSans-BoldItalic-webfont.eot -------------------------------------------------------------------------------- /docs/fonts/Montserrat/Montserrat-Bold.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SideeX/sideex-api/HEAD/docs/fonts/Montserrat/Montserrat-Bold.woff2 -------------------------------------------------------------------------------- /docs/fonts/Montserrat/Montserrat-Regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SideeX/sideex-api/HEAD/docs/fonts/Montserrat/Montserrat-Regular.eot -------------------------------------------------------------------------------- /docs/fonts/Montserrat/Montserrat-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SideeX/sideex-api/HEAD/docs/fonts/Montserrat/Montserrat-Regular.ttf -------------------------------------------------------------------------------- /docs/fonts/Montserrat/Montserrat-Regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SideeX/sideex-api/HEAD/docs/fonts/Montserrat/Montserrat-Regular.woff -------------------------------------------------------------------------------- /docs/fonts/OpenSans-BoldItalic-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SideeX/sideex-api/HEAD/docs/fonts/OpenSans-BoldItalic-webfont.woff -------------------------------------------------------------------------------- /docs/fonts/OpenSans-LightItalic-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SideeX/sideex-api/HEAD/docs/fonts/OpenSans-LightItalic-webfont.eot -------------------------------------------------------------------------------- /docs/fonts/OpenSans-LightItalic-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SideeX/sideex-api/HEAD/docs/fonts/OpenSans-LightItalic-webfont.woff -------------------------------------------------------------------------------- /docs/fonts/Montserrat/Montserrat-Regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SideeX/sideex-api/HEAD/docs/fonts/Montserrat/Montserrat-Regular.woff2 -------------------------------------------------------------------------------- /custom-template/static/fonts/Montserrat/Montserrat-Bold.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SideeX/sideex-api/HEAD/custom-template/static/fonts/Montserrat/Montserrat-Bold.eot -------------------------------------------------------------------------------- /custom-template/static/fonts/Montserrat/Montserrat-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SideeX/sideex-api/HEAD/custom-template/static/fonts/Montserrat/Montserrat-Bold.ttf -------------------------------------------------------------------------------- /custom-template/static/fonts/Montserrat/Montserrat-Bold.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SideeX/sideex-api/HEAD/custom-template/static/fonts/Montserrat/Montserrat-Bold.woff -------------------------------------------------------------------------------- /docs/fonts/Source-Sans-Pro/sourcesanspro-light-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SideeX/sideex-api/HEAD/docs/fonts/Source-Sans-Pro/sourcesanspro-light-webfont.eot -------------------------------------------------------------------------------- /docs/fonts/Source-Sans-Pro/sourcesanspro-light-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SideeX/sideex-api/HEAD/docs/fonts/Source-Sans-Pro/sourcesanspro-light-webfont.ttf -------------------------------------------------------------------------------- /docs/fonts/Source-Sans-Pro/sourcesanspro-light-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SideeX/sideex-api/HEAD/docs/fonts/Source-Sans-Pro/sourcesanspro-light-webfont.woff -------------------------------------------------------------------------------- /docs/fonts/Source-Sans-Pro/sourcesanspro-light-webfont.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SideeX/sideex-api/HEAD/docs/fonts/Source-Sans-Pro/sourcesanspro-light-webfont.woff2 -------------------------------------------------------------------------------- /docs/fonts/Source-Sans-Pro/sourcesanspro-regular-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SideeX/sideex-api/HEAD/docs/fonts/Source-Sans-Pro/sourcesanspro-regular-webfont.eot -------------------------------------------------------------------------------- /docs/fonts/Source-Sans-Pro/sourcesanspro-regular-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SideeX/sideex-api/HEAD/docs/fonts/Source-Sans-Pro/sourcesanspro-regular-webfont.ttf -------------------------------------------------------------------------------- /custom-template/static/fonts/Montserrat/Montserrat-Bold.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SideeX/sideex-api/HEAD/custom-template/static/fonts/Montserrat/Montserrat-Bold.woff2 -------------------------------------------------------------------------------- /custom-template/static/fonts/Montserrat/Montserrat-Regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SideeX/sideex-api/HEAD/custom-template/static/fonts/Montserrat/Montserrat-Regular.eot -------------------------------------------------------------------------------- /custom-template/static/fonts/Montserrat/Montserrat-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SideeX/sideex-api/HEAD/custom-template/static/fonts/Montserrat/Montserrat-Regular.ttf -------------------------------------------------------------------------------- /docs/fonts/Source-Sans-Pro/sourcesanspro-regular-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SideeX/sideex-api/HEAD/docs/fonts/Source-Sans-Pro/sourcesanspro-regular-webfont.woff -------------------------------------------------------------------------------- /docs/fonts/Source-Sans-Pro/sourcesanspro-regular-webfont.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SideeX/sideex-api/HEAD/docs/fonts/Source-Sans-Pro/sourcesanspro-regular-webfont.woff2 -------------------------------------------------------------------------------- /custom-template/static/fonts/Montserrat/Montserrat-Regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SideeX/sideex-api/HEAD/custom-template/static/fonts/Montserrat/Montserrat-Regular.woff -------------------------------------------------------------------------------- /custom-template/static/fonts/Montserrat/Montserrat-Regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SideeX/sideex-api/HEAD/custom-template/static/fonts/Montserrat/Montserrat-Regular.woff2 -------------------------------------------------------------------------------- /src/common/seleniumError.ts: -------------------------------------------------------------------------------- 1 | export class SeleniumError extends Error { 2 | isSeleniumError: boolean = true 3 | constructor(message: string) { 4 | super(message); 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /src/utils/timeoutError.ts: -------------------------------------------------------------------------------- 1 | export class TimeoutError extends Error { 2 | constructor(message?: string) { 3 | super(message); 4 | this.name = 'TimeoutError'; 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /custom-template/static/fonts/Source-Sans-Pro/sourcesanspro-light-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SideeX/sideex-api/HEAD/custom-template/static/fonts/Source-Sans-Pro/sourcesanspro-light-webfont.eot -------------------------------------------------------------------------------- /custom-template/static/fonts/Source-Sans-Pro/sourcesanspro-light-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SideeX/sideex-api/HEAD/custom-template/static/fonts/Source-Sans-Pro/sourcesanspro-light-webfont.ttf -------------------------------------------------------------------------------- /custom-template/static/fonts/Source-Sans-Pro/sourcesanspro-light-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SideeX/sideex-api/HEAD/custom-template/static/fonts/Source-Sans-Pro/sourcesanspro-light-webfont.woff -------------------------------------------------------------------------------- /custom-template/static/fonts/Source-Sans-Pro/sourcesanspro-light-webfont.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SideeX/sideex-api/HEAD/custom-template/static/fonts/Source-Sans-Pro/sourcesanspro-light-webfont.woff2 -------------------------------------------------------------------------------- /custom-template/static/fonts/Source-Sans-Pro/sourcesanspro-regular-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SideeX/sideex-api/HEAD/custom-template/static/fonts/Source-Sans-Pro/sourcesanspro-regular-webfont.eot -------------------------------------------------------------------------------- /custom-template/static/fonts/Source-Sans-Pro/sourcesanspro-regular-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SideeX/sideex-api/HEAD/custom-template/static/fonts/Source-Sans-Pro/sourcesanspro-regular-webfont.ttf -------------------------------------------------------------------------------- /custom-template/static/fonts/Source-Sans-Pro/sourcesanspro-regular-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SideeX/sideex-api/HEAD/custom-template/static/fonts/Source-Sans-Pro/sourcesanspro-regular-webfont.woff -------------------------------------------------------------------------------- /custom-template/static/fonts/Source-Sans-Pro/sourcesanspro-regular-webfont.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SideeX/sideex-api/HEAD/custom-template/static/fonts/Source-Sans-Pro/sourcesanspro-regular-webfont.woff2 -------------------------------------------------------------------------------- /custom-template/tmpl/source.tmpl: -------------------------------------------------------------------------------- 1 | 4 |
5 |
6 |
7 |
8 |
-------------------------------------------------------------------------------- /docs/scripts/polyfill.js: -------------------------------------------------------------------------------- 1 | //IE Fix, src: https://www.reddit.com/r/programminghorror/comments/6abmcr/nodelist_lacks_foreach_in_internet_explorer/ 2 | if (typeof(NodeList.prototype.forEach)!==typeof(alert)){ 3 | NodeList.prototype.forEach=Array.prototype.forEach; 4 | } -------------------------------------------------------------------------------- /custom-template/tmpl/type.tmpl: -------------------------------------------------------------------------------- 1 | 5 | 6 | | 7 | -------------------------------------------------------------------------------- /custom-template/static/scripts/polyfill.js: -------------------------------------------------------------------------------- 1 | //IE Fix, src: https://www.reddit.com/r/programminghorror/comments/6abmcr/nodelist_lacks_foreach_in_internet_explorer/ 2 | if (typeof(NodeList.prototype.forEach)!==typeof(alert)){ 3 | NodeList.prototype.forEach=Array.prototype.forEach; 4 | } -------------------------------------------------------------------------------- /custom-template/tmpl/modifies.tmpl: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 |
7 |
8 | Type 9 |
10 |
11 | 12 |
13 |
14 | -------------------------------------------------------------------------------- /custom-template/tmpl/augments.tmpl: -------------------------------------------------------------------------------- 1 | 5 | 6 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /custom-template/tmpl/examples.tmpl: -------------------------------------------------------------------------------- 1 | 8 |

9 | 10 |
11 | -------------------------------------------------------------------------------- /webpack.api.config.js: -------------------------------------------------------------------------------- 1 | var merge = require('webpack-merge'); 2 | var nodeExternals = require('webpack-node-externals'); 3 | var browserConfig = require('./webpack.browser.config'); 4 | 5 | module.exports = merge(browserConfig, { 6 | externals: [nodeExternals()], 7 | output: { 8 | libraryTarget: 'commonjs', 9 | filename: '[name].bundle.js' 10 | } 11 | }); 12 | -------------------------------------------------------------------------------- /custom-template/tmpl/returns.tmpl: -------------------------------------------------------------------------------- 1 | 5 |
6 | 7 |
8 | 9 | 10 | 11 |
12 |
13 | Type 14 |
15 |
16 | 17 |
18 |
19 | -------------------------------------------------------------------------------- /src/utils/Point.ts: -------------------------------------------------------------------------------- 1 | export class Point { 2 | x: number 3 | y: number 4 | constructor(x: number = 0, y: number = 0) { 5 | this.x = x; 6 | this.y = y; 7 | } 8 | public add(that: Point): Point { 9 | return new Point(this.x + that.x, this.y + that.y); 10 | } 11 | public sub(that: Point): Point { 12 | return new Point(this.x - that.x, this.y - that.y); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /custom-template/tmpl/mainpage.tmpl: -------------------------------------------------------------------------------- 1 | 5 | 6 | 7 |
8 |

9 |
10 | 11 | 12 |
13 |
14 |
15 | 16 | -------------------------------------------------------------------------------- /docs/scripts/nav.js: -------------------------------------------------------------------------------- 1 | function scrollToNavItem() { 2 | var path = window.location.href.split('/').pop().replace(/\.html/, ''); 3 | document.querySelectorAll('nav a').forEach(function(link) { 4 | var href = link.attributes.href.value.replace(/\.html/, ''); 5 | if (path === href) { 6 | link.scrollIntoView({block: 'center'}); 7 | return; 8 | } 9 | }) 10 | } 11 | 12 | scrollToNavItem(); 13 | -------------------------------------------------------------------------------- /custom-template/tmpl/tutorial.tmpl: -------------------------------------------------------------------------------- 1 |
2 | 3 |
4 | 0) { ?> 5 | 10 | 11 | 12 |

13 |
14 | 15 |
16 | 17 |
18 | 19 |
20 | -------------------------------------------------------------------------------- /custom-template/static/scripts/nav.js: -------------------------------------------------------------------------------- 1 | function scrollToNavItem() { 2 | var path = window.location.href.split('/').pop().replace(/\.html/, ''); 3 | document.querySelectorAll('nav a').forEach(function(link) { 4 | var href = link.attributes.href.value.replace(/\.html/, ''); 5 | if (path === href) { 6 | link.scrollIntoView({block: 'center'}); 7 | return; 8 | } 9 | }) 10 | } 11 | 12 | scrollToNavItem(); 13 | -------------------------------------------------------------------------------- /tutorials/tutorial.md: -------------------------------------------------------------------------------- 1 | 6 | # SideeX-API 7 | 8 | SideeX JavaScript API is a JS library running on a webpage for recording and playing web browsing behavior. As opposed to acting as a browser web extension, the API can be directly embedded and used within a webpage via JavaScript. 9 | 10 | # Installation 11 | ```javascript 12 | npm i sideex-api 13 | ``` 14 | 15 | 16 | # Import 17 | ```javascript 18 | var {SideeX} = require('sideex-api') 19 | var sideex = new SideeX(); 20 | ``` 21 | 22 | ```javascript 23 | import {SideeX} from "sideex-api" 24 | var sideex = new SideeX(); 25 | ``` 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /jsdoc.json: -------------------------------------------------------------------------------- 1 | { 2 | "source": { 3 | "include": ["./src/sideex-api/index.js"], 4 | "includePattern": ".js$", 5 | "excludePattern": "{node_modules/|docs}" 6 | }, 7 | "plugins": ["plugins/markdown"], 8 | "templates": { 9 | "default": { 10 | "outputSourceFiles": false, 11 | "staticFiles": { 12 | "include": [ 13 | "./static" 14 | ] 15 | } 16 | }, 17 | "cleverLinks": true, 18 | "monospaceLinks": true 19 | }, 20 | "opts": { 21 | "recurse": true, 22 | "destination": "./docs/", 23 | "template": "./custom-template", 24 | "tutorials": "./tutorials" 25 | } 26 | 27 | 28 | } -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /custom-template/static/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/collapse.js: -------------------------------------------------------------------------------- 1 | function hideAllButCurrent(){ 2 | //by default all submenut items are hidden 3 | //but we need to rehide them for search 4 | document.querySelectorAll("nav > ul > li > ul li").forEach(function(parent) { 5 | parent.style.display = "none"; 6 | }); 7 | 8 | //only current page (if it exists) should be opened 9 | var file = window.location.pathname.split("/").pop().replace(/\.html/, ''); 10 | document.querySelectorAll("nav > ul > li > a").forEach(function(parent) { 11 | var href = parent.attributes.href.value.replace(/\.html/, ''); 12 | if (file === href) { 13 | parent.parentNode.querySelectorAll("ul li").forEach(function(elem) { 14 | elem.style.display = "block"; 15 | }); 16 | } 17 | }); 18 | } 19 | 20 | hideAllButCurrent(); -------------------------------------------------------------------------------- /custom-template/static/scripts/collapse.js: -------------------------------------------------------------------------------- 1 | function hideAllButCurrent(){ 2 | //by default all submenut items are hidden 3 | //but we need to rehide them for search 4 | document.querySelectorAll("nav > ul > li > ul li").forEach(function(parent) { 5 | parent.style.display = "none"; 6 | }); 7 | 8 | //only current page (if it exists) should be opened 9 | var file = window.location.pathname.split("/").pop().replace(/\.html/, ''); 10 | document.querySelectorAll("nav > ul > li > a").forEach(function(parent) { 11 | var href = parent.attributes.href.value.replace(/\.html/, ''); 12 | if (file === href) { 13 | parent.parentNode.querySelectorAll("ul li").forEach(function(elem) { 14 | elem.style.display = "block"; 15 | }); 16 | } 17 | }); 18 | } 19 | 20 | hideAllButCurrent(); -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /custom-template/static/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 | -------------------------------------------------------------------------------- /custom-template/tmpl/exceptions.tmpl: -------------------------------------------------------------------------------- 1 | 4 | 5 |
6 |
7 |
8 | 9 |
10 |
11 |
12 |
13 |
14 |
15 | Type 16 |
17 |
18 | 19 |
20 |
21 |
22 |
23 |
24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |
32 | 33 | -------------------------------------------------------------------------------- /src/background/storage-initialization.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright SideeX committers 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | browser.storage.sync.set({ 19 | // basic 20 | delay: 0, 21 | networkSpeed: -1, 22 | theme: 0, // it should be constant 23 | 24 | // periodical 25 | isPeriodical: false, 26 | period: 0.1 27 | }); 28 | -------------------------------------------------------------------------------- /src/page/runScript.js: -------------------------------------------------------------------------------- 1 | window.addEventListener("message", async function (event) { 2 | if (event.source == window && event.data && event.data.direction == "from-content-runscript") { 3 | let doc = window.document; 4 | let scriptTag = doc.createElement("script"); 5 | scriptTag.type = "text/javascript"; 6 | scriptTag.text = event.data.script; 7 | var AsyncFunction = Object.getPrototypeOf(async function () { }).constructor; 8 | try { 9 | await new AsyncFunction(scriptTag.text)(); 10 | } catch (e) { 11 | console.log(e); 12 | window.postMessage({ 13 | direction: "from-page-runscript", 14 | result: e.toString() 15 | }, "*"); 16 | return; 17 | } 18 | window.postMessage({ 19 | direction: "from-page-runscript", 20 | result: "No error!!!!" 21 | }, "*"); 22 | } 23 | }); 24 | -------------------------------------------------------------------------------- /src/content/content-initialization.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright SideeX committers 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | import { Recorder } from "./recorder"; 18 | import { BrowserBot } from "./browserbot"; 19 | import { Sideex } from "./sideex-api"; 20 | import { LocatorBuilders } from "./locatorBuilders"; 21 | export const recorder = new Recorder(window); 22 | export const browserBot = new BrowserBot(); 23 | export const sideex = new Sideex(browserBot); 24 | export const locatorBuilders = new LocatorBuilders(window); 25 | -------------------------------------------------------------------------------- /custom-template/tmpl/members.tmpl: -------------------------------------------------------------------------------- 1 | 5 |

6 | 7 | 8 |

9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 |
17 | 18 | 19 | 20 |
Type:
21 | 26 | 27 | 28 | 29 |
Fires:
30 | 33 | 34 | 35 | 36 |
Example 1? 's':'' ?>
37 | 38 | 39 | -------------------------------------------------------------------------------- /src/content/message-controller.js: -------------------------------------------------------------------------------- 1 | import "../utils/windowListeners"; 2 | 3 | export class MessageController { 4 | static tabSendMessage(message, tabId, options) { 5 | if (this.isExtension) { 6 | return browser.tabs.sendMessage(tabId, message, options); 7 | } else { 8 | return window.postMessageAsync("message", message); 9 | } 10 | } 11 | 12 | static runtimeSendMessage(message, extensionId, options) { 13 | if (this.isExtension) { 14 | return browser.runtime.sendMessage(extensionId, message, options); 15 | } else { 16 | return window.postMessageAsync("message", message); 17 | } 18 | } 19 | 20 | static addListener(callback) { 21 | if (this.isExtension) { 22 | return browser.runtime.onMessage.addListener(callback); 23 | } else { 24 | return window.addAsyncMessageListener("message", callback); 25 | } 26 | } 27 | } 28 | 29 | // #!if isExt === false 30 | MessageController.isExtension = false; 31 | // #!else 32 | MessageController.isExtension = true; 33 | // #!endif 34 | -------------------------------------------------------------------------------- /docs/styles/prettify.css: -------------------------------------------------------------------------------- 1 | .pln { 2 | color: #ddd; 3 | } 4 | 5 | /* string content */ 6 | .str { 7 | color: #61ce3c; 8 | } 9 | 10 | /* a keyword */ 11 | .kwd { 12 | color: #fbde2d; 13 | } 14 | 15 | /* a comment */ 16 | .com { 17 | color: #aeaeae; 18 | } 19 | 20 | /* a type name */ 21 | .typ { 22 | color: #8da6ce; 23 | } 24 | 25 | /* a literal value */ 26 | .lit { 27 | color: #fbde2d; 28 | } 29 | 30 | /* punctuation */ 31 | .pun { 32 | color: #ddd; 33 | } 34 | 35 | /* lisp open bracket */ 36 | .opn { 37 | color: #000000; 38 | } 39 | 40 | /* lisp close bracket */ 41 | .clo { 42 | color: #000000; 43 | } 44 | 45 | /* a markup tag name */ 46 | .tag { 47 | color: #8da6ce; 48 | } 49 | 50 | /* a markup attribute name */ 51 | .atn { 52 | color: #fbde2d; 53 | } 54 | 55 | /* a markup attribute value */ 56 | .atv { 57 | color: #ddd; 58 | } 59 | 60 | /* a declaration */ 61 | .dec { 62 | color: #EF5050; 63 | } 64 | 65 | /* a variable name */ 66 | .var { 67 | color: #c82829; 68 | } 69 | 70 | /* a function name */ 71 | .fun { 72 | color: #4271ae; 73 | } 74 | 75 | /* Specify class=linenums on a pre to get line numbering */ 76 | ol.linenums { 77 | margin-top: 0; 78 | margin-bottom: 0; 79 | } 80 | -------------------------------------------------------------------------------- /custom-template/static/styles/prettify.css: -------------------------------------------------------------------------------- 1 | .pln { 2 | color: #ddd; 3 | } 4 | 5 | /* string content */ 6 | .str { 7 | color: #61ce3c; 8 | } 9 | 10 | /* a keyword */ 11 | .kwd { 12 | color: #fbde2d; 13 | } 14 | 15 | /* a comment */ 16 | .com { 17 | color: #aeaeae; 18 | } 19 | 20 | /* a type name */ 21 | .typ { 22 | color: #8da6ce; 23 | } 24 | 25 | /* a literal value */ 26 | .lit { 27 | color: #fbde2d; 28 | } 29 | 30 | /* punctuation */ 31 | .pun { 32 | color: #ddd; 33 | } 34 | 35 | /* lisp open bracket */ 36 | .opn { 37 | color: #000000; 38 | } 39 | 40 | /* lisp close bracket */ 41 | .clo { 42 | color: #000000; 43 | } 44 | 45 | /* a markup tag name */ 46 | .tag { 47 | color: #8da6ce; 48 | } 49 | 50 | /* a markup attribute name */ 51 | .atn { 52 | color: #fbde2d; 53 | } 54 | 55 | /* a markup attribute value */ 56 | .atv { 57 | color: #ddd; 58 | } 59 | 60 | /* a declaration */ 61 | .dec { 62 | color: #EF5050; 63 | } 64 | 65 | /* a variable name */ 66 | .var { 67 | color: #c82829; 68 | } 69 | 70 | /* a function name */ 71 | .fun { 72 | color: #4271ae; 73 | } 74 | 75 | /* Specify class=linenums on a pre to get line numbering */ 76 | ol.linenums { 77 | margin-top: 0; 78 | margin-bottom: 0; 79 | } 80 | -------------------------------------------------------------------------------- /src/content/pageScript-injecter.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright SideeX committers 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | import { sideex } from "./content-initialization"; 18 | 19 | const script = document.createElement("script"); 20 | script.onload = script.remove; 21 | script.src = browser.runtime.getURL("page_script.js"); 22 | 23 | window.addEventListener("message", function (event) { 24 | if (event.source.top == window && event.data) { 25 | if (event.data.direction == "from-page-runscript") { 26 | sideex.browserbot.runScriptResponse = true; 27 | sideex.browserbot.runScriptMessage = event.data.result; 28 | } 29 | } 30 | }); 31 | -------------------------------------------------------------------------------- /webpack.browser.config.js: -------------------------------------------------------------------------------- 1 | var path = require('path'); 2 | var merge = require('webpack-merge'); 3 | var common = require('./webpack.common'); 4 | 5 | module.exports = merge(common, { 6 | entry: { 7 | index: ['./src/sideex-api/index.js'], 8 | // test: ['./src/sideex-api/test.js'] 9 | }, 10 | output: { 11 | path: path.resolve(__dirname, 'dist'), 12 | libraryTarget: 'umd', 13 | filename: '[name].bundle.mjs' 14 | }, 15 | devServer: { 16 | host: 'localhost', 17 | port: 8080, 18 | contentBase: path.join(__dirname, 'dist'), 19 | hot: true, 20 | open: true, 21 | openPage: 'index.html' 22 | }, 23 | module: { 24 | rules: [ 25 | { 26 | test: /\.[jt]sx?$/, 27 | exclude: /node_modules/, 28 | use: [ 29 | { 30 | loader: "webpack-preprocessor-loader", 31 | options: { 32 | params: { 33 | isExt: false 34 | } 35 | } 36 | } 37 | ] 38 | } 39 | ] 40 | } 41 | }); 42 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | _MACOSX/ 2 | .DS_Store 3 | 4 | # Logs 5 | logs 6 | *.log 7 | npm-debug.log* 8 | yarn-debug.log* 9 | yarn-error.log* 10 | 11 | # Runtime data 12 | pids 13 | *.pid 14 | *.seed 15 | *.pid.lock 16 | 17 | # Directory for instrumented libs generated by jscoverage/JSCover 18 | lib-cov 19 | 20 | # Coverage directory used by tools like istanbul 21 | coverage 22 | 23 | # nyc test coverage 24 | .nyc_output 25 | 26 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 27 | .grunt 28 | 29 | # Bower dependency directory (https://bower.io/) 30 | bower_components 31 | 32 | # node-waf configuration 33 | .lock-wscript 34 | 35 | # Compiled binary addons (https://nodejs.org/api/addons.html) 36 | build/Release 37 | 38 | # Dependency directories 39 | package-lock.json 40 | node_modules/ 41 | jspm_packages/ 42 | 43 | # Typescript v1 declaration files 44 | typings/ 45 | 46 | # Optional npm cache directory 47 | .npm 48 | 49 | # Optional eslint cache 50 | .eslintcache 51 | 52 | # Optional REPL history 53 | .node_repl_history 54 | 55 | # Output of 'npm pack' 56 | *.tgz 57 | 58 | # Yarn Integrity file 59 | .yarn-integrity 60 | 61 | # dotenv environment variables file 62 | .env 63 | 64 | #intelliJ 65 | .idea 66 | 67 | # vscode 68 | .vscode/ 69 | 70 | # build files 71 | build/ 72 | builds/ 73 | dist/ 74 | 75 | #source map 76 | *.js.map 77 | 78 | -------------------------------------------------------------------------------- /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | [ 4 | "@babel/preset-env", 5 | { 6 | "useBuiltIns": "usage", 7 | "modules": "commonjs", 8 | "corjs": "3" 9 | } 10 | ], 11 | "@babel/preset-typescript" 12 | ], 13 | "plugins": [ 14 | "@babel/plugin-syntax-dynamic-import", 15 | "@babel/plugin-syntax-import-meta", 16 | [ 17 | "@babel/plugin-proposal-decorators", 18 | { 19 | "legacy": true 20 | } 21 | ], 22 | [ 23 | "@babel/plugin-proposal-class-properties", 24 | { 25 | "loose": true 26 | } 27 | ], 28 | "@babel/plugin-proposal-json-strings", 29 | "@babel/plugin-proposal-function-sent", 30 | "@babel/plugin-proposal-export-namespace-from", 31 | "@babel/plugin-proposal-numeric-separator", 32 | "@babel/plugin-proposal-throw-expressions", 33 | "@babel/plugin-proposal-object-rest-spread", 34 | "@babel/plugin-proposal-nullish-coalescing-operator", 35 | "@babel/plugin-proposal-optional-chaining" 36 | ], 37 | "env": { 38 | "production": { 39 | "plugins": [ 40 | "transform-remove-console" 41 | ] 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/panel/js/background/log.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright SideeX committers 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | export class Log { 18 | constructor(root) { 19 | this.root = root; 20 | 21 | this.logs = []; 22 | this.logTypeMap = { 23 | debug: [], 24 | info: [], 25 | error: [], 26 | warn: [] 27 | }; 28 | } 29 | 30 | pushLog(type, str) { 31 | this.logTypeMap[type].push(this.logs.length); 32 | this.logs.push({ 33 | type: type, 34 | message: str 35 | }); 36 | console.log(type, str); 37 | } 38 | 39 | clearLog() { 40 | this.logs = []; 41 | this.logTypeMap = { 42 | debug: [], 43 | info: [], 44 | error: [], 45 | warn: [] 46 | }; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "description": "SideeX (Extended Selenium IDE) - A smart record-playback automated web application testing tool", 3 | "manifest_version": 2, 4 | "name": "SideeX Recorder Mini", 5 | "version": "3.0.0", 6 | "homepage_url": "http://sideex.org/", 7 | "icons": { 8 | "48": "icons/logo-48.png", 9 | "96": "icons/logo-96.png" 10 | }, 11 | "permissions": [ 12 | "tabs", 13 | "activeTab", 14 | "webRequest", 15 | "contextMenus", 16 | "downloads", 17 | "webNavigation", 18 | "notifications", 19 | "storage", 20 | "" 21 | ], 22 | "content_security_policy": "script-src 'self' 'unsafe-eval'; object-src 'self'", 23 | "content_scripts": [ 24 | { 25 | "matches": [ 26 | "" 27 | ], 28 | "js": [ 29 | "document_end.js" 30 | ], 31 | "match_about_blank": true, 32 | "all_frames": true, 33 | "run_at": "document_end" 34 | }, 35 | { 36 | "matches": [ 37 | "" 38 | ], 39 | "js": [ 40 | "document_start.js" 41 | ], 42 | "match_about_blank": true, 43 | "all_frames": true, 44 | "run_at": "document_start" 45 | } 46 | ], 47 | "background": { 48 | "scripts": [ 49 | "background_script.js" 50 | ] 51 | }, 52 | "browser_action": { 53 | "default_icon": "icons/logo-48.png", 54 | "default_title": "SideeX" 55 | }, 56 | "options_ui": { 57 | "page": "option/index.html" 58 | } 59 | } -------------------------------------------------------------------------------- /src/panel/js/background/initial.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright SideeX committers 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | import { MessageController } from '../../../content/message-controller'; 19 | import { FileController } from '../IO/file-controller'; 20 | import { BackgroundRecorder } from './recorder'; 21 | import { Playback } from './playback'; 22 | import { VariableController } from './variable-controller'; 23 | import { Setting } from "./setting"; 24 | import { Log } from './log'; 25 | export const root = { isDOMBased: true, isExt: true }; 26 | 27 | 28 | root.messageController = new MessageController(root); 29 | root.fileController = new FileController(root); 30 | root.recorder = new BackgroundRecorder(root); 31 | root.playback = new Playback(root); 32 | root.variables = new VariableController(root); 33 | root.log = new Log(root); 34 | root.setting = new Setting(root); 35 | // export const sideex = new SideeX(root); 36 | 37 | MessageController.addListener(root.recorder.contentWindowIdListener); 38 | MessageController.addListener(root.recorder.handleFormatCommand); 39 | 40 | window.addEventListener("beforeunload", (event) => { 41 | (event || window.event).returnValue = false; 42 | }); 43 | -------------------------------------------------------------------------------- /custom-template/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "_from": "docdash", 3 | "_id": "docdash@1.2.0", 4 | "_inBundle": false, 5 | "_integrity": "sha512-IYZbgYthPTspgqYeciRJNPhSwL51yer7HAwDXhF5p+H7mTDbPvY3PCk/QDjNxdPCpWkaJVFC4t7iCNB/t9E5Kw==", 6 | "_location": "/docdash", 7 | "_phantomChildren": {}, 8 | "_requested": { 9 | "type": "tag", 10 | "registry": true, 11 | "raw": "docdash", 12 | "name": "docdash", 13 | "escapedName": "docdash", 14 | "rawSpec": "", 15 | "saveSpec": null, 16 | "fetchSpec": "latest" 17 | }, 18 | "_requiredBy": [ 19 | "#USER", 20 | "/" 21 | ], 22 | "_resolved": "https://registry.npmjs.org/docdash/-/docdash-1.2.0.tgz", 23 | "_shasum": "f99dde5b8a89aa4ed083a3150383e042d06c7f49", 24 | "_spec": "docdash", 25 | "_where": "C:\\Users\\user\\Desktop\\api", 26 | "author": { 27 | "name": "Clement Moron", 28 | "email": "clement.moron@gmail.com" 29 | }, 30 | "bugs": { 31 | "url": "https://github.com/clenemt/docdash/issues" 32 | }, 33 | "bundleDependencies": false, 34 | "deprecated": false, 35 | "description": "A clean, responsive documentation template theme for JSDoc 3 inspired by lodash and minami", 36 | "devDependencies": { 37 | "browser-sync": "latest", 38 | "jsdoc": "latest", 39 | "watch-run": "latest" 40 | }, 41 | "files": [ 42 | "publish.js", 43 | "static", 44 | "tmpl" 45 | ], 46 | "homepage": "https://github.com/clenemt/docdash#readme", 47 | "keywords": [ 48 | "jsdoc", 49 | "template" 50 | ], 51 | "license": "Apache-2.0", 52 | "main": "publish.js", 53 | "name": "docdash", 54 | "repository": { 55 | "type": "git", 56 | "url": "git+https://github.com/clenemt/docdash.git" 57 | }, 58 | "scripts": { 59 | "sync": "browser-sync start -s ../fixtures-doc -f ../fixtures-doc --reload-delay 1000 --no-ui --no-notify", 60 | "test": "jsdoc -c fixtures/fixtures.conf.json", 61 | "watch": "watch-run -d 1000 -p tmpl/**,static/** \"npm run test\"" 62 | }, 63 | "version": "1.2.0" 64 | } 65 | -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": [ 3 | "promise" 4 | ], 5 | "extends": [ 6 | "eslint:recommended", 7 | "plugin:promise/recommended" 8 | ], 9 | "parserOptions": { 10 | "ecmaVersion": 2018, 11 | "sourceType": "module", 12 | "jsx": true, 13 | "experimentalObjectRestSpread": true 14 | }, 15 | "env": { 16 | "browser": true, 17 | "node": true, 18 | "jquery": true, 19 | "webextensions": true, 20 | "es6": true, 21 | "jest": true 22 | }, 23 | "rules": { 24 | "indent": [ 25 | "error", 26 | 4, { 27 | "SwitchCase": 1 28 | } 29 | ], 30 | "semi": [ 31 | "error", 32 | "always" 33 | ], 34 | "comma-dangle": [ 35 | "error", 36 | "never" 37 | ], 38 | "no-trailing-spaces": [ 39 | "error" 40 | ], 41 | "brace-style": [ 42 | "error", 43 | "1tbs", 44 | { "allowSingleLine": true } 45 | ], 46 | "space-before-blocks": [ 47 | "error" 48 | ], 49 | "keyword-spacing": [ 50 | "error" 51 | ], 52 | "comma-spacing": [ 53 | "error" 54 | ], 55 | "eol-last": [ 56 | "error" 57 | ], 58 | "space-infix-ops": [ 59 | "error" 60 | ], 61 | // Below overwrite "eslint:recommended". 62 | "no-console": [ 63 | "warn" 64 | ], 65 | "no-constant-condition": [ 66 | "error", 67 | { "checkLoops": false } 68 | ], 69 | "no-undef": [ 70 | // Temporarily turns off. 71 | "off" 72 | ], 73 | "no-unused-vars": [ 74 | "warn" 75 | ], 76 | // Below overwrite "plugin:promise/recommended". 77 | "promise/avoid-new": [ 78 | "off" 79 | ] 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /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: #006400; 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 | -------------------------------------------------------------------------------- /src/utils/Rectangle.ts: -------------------------------------------------------------------------------- 1 | import { Point } from "./Point"; 2 | export class Rectangle { 3 | begin: Point 4 | end: Point 5 | constructor(left: number = 0, top: number = 0, right?: number, bottom?: number) { 6 | this.begin = new Point(left, top); 7 | this.end = new Point(right ? right : left, bottom ? bottom : top); 8 | } 9 | static fromRect(rect: ClientRect | DOMRect): Rectangle { 10 | return new Rectangle(rect.left, rect.top, rect.right, rect.bottom); 11 | } 12 | static isIntersection(a: Rectangle, b: Rectangle): boolean { 13 | const left = Math.max(a.left, b.left); 14 | const top = Math.max(a.top, b.top); 15 | const right = Math.min(a.right, b.right); 16 | const bottom = Math.min(a.bottom, b.bottom); 17 | return left < right && top < bottom; 18 | } 19 | static union(a: Rectangle, b: Rectangle): Rectangle { 20 | return new Rectangle( 21 | Math.min(a.left, b.left), 22 | Math.min(a.top, b.top), 23 | Math.max(a.right, b.right), 24 | Math.max(a.bottom, b.bottom) 25 | ); 26 | } 27 | get left() { return this.begin.x; } 28 | set left(left: number) { this.begin.x = left; } 29 | get top() { return this.begin.y; } 30 | set top(top: number) { this.begin.y = top; } 31 | get right() { return this.end.x; } 32 | set right(right: number) { this.end.x = right; } 33 | get bottom() { return this.end.y; } 34 | set bottom(bottom: number) { this.end.y = bottom; } 35 | get width() { return Math.max(0, this.right - this.left); } 36 | get height() { return Math.max(0, this.bottom - this.top); } 37 | public addRectangle(that: Rectangle): Rectangle { 38 | return new Rectangle( 39 | this.left + that.left, 40 | this.top + that.top, 41 | this.right + that.right, 42 | this.bottom + that.bottom 43 | ); 44 | } 45 | public addPointOffset(that: Point): Rectangle { 46 | return new Rectangle( 47 | this.left + that.x, 48 | this.top + that.y, 49 | this.right + that.x, 50 | this.bottom + that.y 51 | ); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /custom-template/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## Version 1.2.0 2 | 3 | * [feature] host fonts locally 4 | * [feature] separate styles for headers inside user markdown 5 | * [feature] hide static/private method depending of the config 6 | * [fix] fix empty source code lines in some browsers 7 | * [fix] improved viewing theme on smaller screens 8 | 9 | ## Version 1.1.1 10 | 11 | * [feature] scroll to currently opened method on page load 12 | * [fix] fixed searching in IE11 13 | * [fix] hiding/showing find exact match to open only single relevant section 14 | 15 | ## Version 1.1.0 16 | 17 | * [scripts] remove jQuery as dependency 18 | * [feature] allow aliasing event names 19 | 20 | ## Version 1.0.3 21 | 22 | * [style] break headers into multiple lines 23 | * [style] break links in descriptions into multiple lines 24 | * [fix] fix ancestor check when there are none, like including tutorials 25 | * [fix] remove unnecessary files from published package 26 | * [fix] stop crashing on incorrect params JSDoc comments 27 | * [feature] add displaying version from package.json when it is provided 28 | * [feature] add support for yield 29 | * [feature] add support for namepsaces that are functions 30 | * [feature] add support for interfaces 31 | * [feature] add support for modifies 32 | 33 | ## Version 1.0.2 34 | 35 | * [styles] increase space between custom menu items 36 | * [option] Added `wrap` option to wrap long names instead of trimming them 37 | * [option] Added `navLevel` option to control depth level to show in navbar, starting at 0 38 | * [option] Added `private` option to show/hide @private in navbar 39 | 40 | ## Version 1.0.1 41 | 42 | * Allow adding custom menu items 43 | * Remove line-height: 160% 44 | 45 | ## Version 1.0.0 46 | 47 | * Add option to add disqus comments to each page 48 | * Add option to filter through navigation items 49 | * Add option to have menus collapsed by default and only open the one for the current page 50 | * Add option to provide custom site title 51 | * Add option to provide meta information for the website 52 | * Add option to provide opengraph information for the website 53 | * Add viewport meta data 54 | * Added global table styles 55 | * Added support for @hidecontainer (jsdoc 3.5.0) 56 | * Added support for useLongnameInNav 57 | * Allow including typedefs in the menu 58 | * Allow inclusion of custom CSS 59 | * Allow injecting external or local copied scripts into HTML 60 | * Allow removing single and double quotes from pathnames 61 | * Fix crash when @example is empty or undefined 62 | * Fix issue with node 8.5 63 | * Fixing copyFile problem on some systems or nodejs versions 64 | * Removes arbitrary width property 65 | * Support ordering of the main navbar sections -------------------------------------------------------------------------------- /webpack.common.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | if (!fs.existsSync("./.env")) { 3 | fs.copyFileSync("./.env.template", "./.env"); 4 | } 5 | const env = require('dotenv').config().parsed; 6 | module.exports = { 7 | mode: env.NODE_ENV, 8 | performance: { 9 | hints: false 10 | }, 11 | stats: { 12 | excludeAssets: (assetName) => { 13 | return !assetName.match(/\.[jt]sx?$/); 14 | }, 15 | modules: false 16 | }, 17 | target: 'web', 18 | watch: true, 19 | watchOptions: { 20 | ignored: /node_modules/ 21 | }, 22 | devtool: false, 23 | resolve: { 24 | extensions: ['.ts', '.tsx', '.js', '.jsx', '.json'] 25 | }, 26 | module: { 27 | rules: [ 28 | { 29 | test: /\.[jt]sx?$/, 30 | exclude: /node_modules/, 31 | use: { 32 | loader: "babel-loader", 33 | options: { 34 | presets: ['@babel/env'] 35 | } 36 | } 37 | }, 38 | { 39 | test: /\.html$/, 40 | use: [ 41 | { 42 | loader: "html-loader", 43 | options: { minimize: false } 44 | } 45 | ] 46 | }, 47 | { 48 | test: /\.(scss|css)$/, 49 | use: [ 50 | 'style-loader', 51 | { 52 | loader: "css-loader", 53 | options: { 54 | modules: true, 55 | localIdentName: '[name]__[local]___[hash:base64:5]' 56 | } 57 | }, 58 | 'postcss-loader', 59 | 'sass-loader' 60 | ] 61 | }, 62 | { 63 | test: /\.(jpe?g|png|gif)$/i, 64 | use: [ 65 | { 66 | loader: 'url-loader', 67 | options: { 68 | limit: 8192 69 | } 70 | } 71 | ] 72 | }, 73 | { 74 | test: /\.(png|jpg|gif|svg)$/, 75 | use: [ 76 | { 77 | loader: 'file-loader', 78 | options: { 79 | name: '[name].[ext]', 80 | outputPath: 'assets' 81 | } 82 | } 83 | ] 84 | } 85 | ] 86 | } 87 | }; 88 | -------------------------------------------------------------------------------- /src/panel/js/background/setting.js: -------------------------------------------------------------------------------- 1 | import { boundMethod } from "autobind-decorator"; 2 | export class Setting { 3 | constructor(root) { 4 | this.root = root; 5 | 6 | this.params = { 7 | // basic 8 | delay: 0, 9 | tac: false, 10 | networkSpeed: -1, 11 | theme: 0, // it should be constant 12 | 13 | // periodical 14 | isPeriodical: false, 15 | period: 0.1 16 | }; 17 | 18 | if (this.root.isDOMBased) { 19 | this.init(); 20 | } 21 | } 22 | 23 | async init() { 24 | browser.storage.onChanged.addListener(this.syncHandler); 25 | let results = await browser.storage.sync.get(); 26 | this.params = { ...this.params, ...results }; 27 | } 28 | 29 | /** 30 | * Set value to specific key in storage 31 | * @param {String} key A specific key 32 | * @param {String} value A single value 33 | */ 34 | set(obj) { 35 | for (let key in obj) { 36 | if (!(key in this.params)) { 37 | console.error(`key "${key}" is not in settings`); 38 | } 39 | this.params[key] = obj[key]; 40 | } 41 | 42 | if (this.root.isDOMBased) { 43 | this.syncStorage(obj); 44 | } 45 | } 46 | 47 | /** 48 | * Retrieve single value from storage 49 | * @param {String} key A key to retrieve data from storage 50 | * @return {*} A single value 51 | */ 52 | get(key) { 53 | if (key in this.params) { 54 | return this.params[key]; 55 | } 56 | return undefined; 57 | } 58 | 59 | async syncStorage(obj) { 60 | let result = await browser.storage.sync.set(obj); 61 | // console.log(result); 62 | } 63 | 64 | async retrieve(keys) { 65 | let results = await browser.storage.sync.get(keys); 66 | for (key in results) { 67 | this.params[key] = results[key]; 68 | } 69 | } 70 | 71 | importSetting(path) { 72 | 73 | } 74 | 75 | exportSetting(path) { 76 | 77 | } 78 | 79 | /** 80 | * A Handler for synchronizing with any changes in storage 81 | * @param {Object} changes All changes in a short interval 82 | */ 83 | @boundMethod 84 | syncHandler(changes) { 85 | for (let item in changes) { 86 | if ("newValue" in changes[item]) { 87 | // The data has been updated or created 88 | this.params[item] = changes[item].newValue; 89 | } else { 90 | // The data has been removed 91 | delete this.params[item]; 92 | } 93 | } 94 | } 95 | } -------------------------------------------------------------------------------- /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: #718c00; } 13 | 14 | /* a keyword */ 15 | .kwd { 16 | color: #8959a8; } 17 | 18 | /* a comment */ 19 | .com { 20 | color: #8e908c; } 21 | 22 | /* a type name */ 23 | .typ { 24 | color: #4271ae; } 25 | 26 | /* a literal value */ 27 | .lit { 28 | color: #f5871f; } 29 | 30 | /* punctuation */ 31 | .pun { 32 | color: #4d4d4c; } 33 | 34 | /* lisp open bracket */ 35 | .opn { 36 | color: #4d4d4c; } 37 | 38 | /* lisp close bracket */ 39 | .clo { 40 | color: #4d4d4c; } 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 | -------------------------------------------------------------------------------- /custom-template/LICENSE.md: -------------------------------------------------------------------------------- 1 | # License 2 | 3 | Docdash is free software, licensed under the Apache License, Version 2.0 (the 4 | "License"). Commercial and non-commercial use are permitted in compliance with 5 | the License. 6 | 7 | Copyright (c) 2016 Clement Moron and the 8 | [contributors to docdash](https://github.com/clenemt/docdash/graphs/contributors). 9 | All rights reserved. 10 | 11 | You may obtain a copy of the License at: 12 | http://www.apache.org/licenses/LICENSE-2.0 13 | 14 | In addition, a copy of the License is included with this distribution. 15 | 16 | As stated in Section 7, "Disclaimer of Warranty," of the License: 17 | 18 | > Licensor provides the Work (and each Contributor provides its Contributions) 19 | > on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 20 | > express or implied, including, without limitation, any warranties or 21 | > conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 22 | > PARTICULAR PURPOSE. You are solely responsible for determining the 23 | > appropriateness of using or redistributing the Work and assume any risks 24 | > associated with Your exercise of permissions under this License. 25 | 26 | The source code for docdash is available at: 27 | https://github.com/clenemt/docdash 28 | 29 | # Third-Party Software 30 | 31 | Docdash includes or depends upon the following third-party software, either in 32 | whole or in part. Each third-party software package is provided under its own 33 | license. 34 | 35 | ## JSDoc 3 36 | 37 | JSDoc 3 is free software, licensed under the Apache License, Version 2.0 (the 38 | "License"). Commercial and non-commercial use are permitted in compliance with 39 | the License. 40 | 41 | Copyright (c) 2011-2016 Michael Mathews and the 42 | [contributors to JSDoc](https://github.com/jsdoc3/jsdoc/graphs/contributors). 43 | All rights reserved. 44 | 45 | You may obtain a copy of the License at: 46 | http://www.apache.org/licenses/LICENSE-2.0 47 | 48 | In addition, a copy of the License is included with this distribution. 49 | 50 | As stated in Section 7, "Disclaimer of Warranty," of the License: 51 | 52 | > Licensor provides the Work (and each Contributor provides its Contributions) 53 | > on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 54 | > express or implied, including, without limitation, any warranties or 55 | > conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 56 | > PARTICULAR PURPOSE. You are solely responsible for determining the 57 | > appropriateness of using or redistributing the Work and assume any risks 58 | > associated with Your exercise of permissions under this License. 59 | 60 | The source code for JSDoc 3 is available at: 61 | https://github.com/jsdoc3/jsdoc 62 | -------------------------------------------------------------------------------- /src/common/storage.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright SideeX committers 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | /** 19 | * A copy of data storage in an extension 20 | * @class 21 | */ 22 | class Storage { 23 | 24 | /** 25 | * Retrieve all data from storage and sync with storage 26 | * @constructor 27 | */ 28 | constructor() { 29 | this.params = { 30 | // basic 31 | delay: 0, 32 | networkSpeed: -1, 33 | theme: 0, // it should be constant 34 | 35 | // periodical 36 | isPeriodical: false, 37 | period: 0.1 38 | }; 39 | this.init(); 40 | } 41 | 42 | async init() { 43 | browser.storage.onChanged.addListener( 44 | /** 45 | * A Handler for synchronizing with any changes in storage 46 | * @param {Object} changes All changes in a short interval 47 | */ 48 | (changes) => { 49 | for (let item in changes) { 50 | if (changes[item].newValue) { 51 | // The data has been updated or created 52 | this.params[item] = changes[item].newValue; 53 | } else { 54 | // The data has been removed 55 | delete this.params[item]; 56 | } 57 | } 58 | }); 59 | let results = await browser.storage.sync.get(); 60 | this.params = { ...this.params, ...results }; 61 | } 62 | 63 | /** 64 | * Set value to specific key in storage 65 | * @param {String} key A specific key 66 | * @param {String} value A single value 67 | */ 68 | set(key, value) { 69 | this.params[key] = value; 70 | browser.storage.sync.set({ [key]: value }); 71 | } 72 | 73 | /** 74 | * Retrieve single value from storage 75 | * @param {String} key A key to retrieve data from storage 76 | * @return {*} A single value 77 | */ 78 | get(key) { 79 | return this.params[key]; 80 | } 81 | 82 | async syncPanel(obj) { 83 | let result = await browser.storage.sync.set(obj); 84 | // console.log(result); 85 | } 86 | 87 | async retrieve(keys) { 88 | let results = await browser.storage.sync.get(keys); 89 | for (key in results) { 90 | this[key] = results[key]; 91 | } 92 | } 93 | } 94 | const storage = new Storage() 95 | export default storage; -------------------------------------------------------------------------------- /docs/scripts/search.js: -------------------------------------------------------------------------------- 1 | 2 | var searchAttr = 'data-search-mode'; 3 | function contains(a,m){ 4 | return (a.textContent || a.innerText || "").toUpperCase().indexOf(m) !== -1; 5 | }; 6 | 7 | //on search 8 | document.getElementById("nav-search").addEventListener("keyup", function(event) { 9 | var search = this.value.toUpperCase(); 10 | 11 | if (!search) { 12 | //no search, show all results 13 | document.documentElement.removeAttribute(searchAttr); 14 | 15 | document.querySelectorAll("nav > ul > li:not(.level-hide)").forEach(function(elem) { 16 | elem.style.display = "block"; 17 | }); 18 | 19 | if (typeof hideAllButCurrent === "function"){ 20 | //let's do what ever collapse wants to do 21 | hideAllButCurrent(); 22 | } else { 23 | //menu by default should be opened 24 | document.querySelectorAll("nav > ul > li > ul li").forEach(function(elem) { 25 | elem.style.display = "block"; 26 | }); 27 | } 28 | } else { 29 | //we are searching 30 | document.documentElement.setAttribute(searchAttr, ''); 31 | 32 | //show all parents 33 | document.querySelectorAll("nav > ul > li").forEach(function(elem) { 34 | elem.style.display = "block"; 35 | }); 36 | //hide all results 37 | document.querySelectorAll("nav > ul > li > ul li").forEach(function(elem) { 38 | elem.style.display = "none"; 39 | }); 40 | //show results matching filter 41 | document.querySelectorAll("nav > ul > li > ul a").forEach(function(elem) { 42 | if (!contains(elem.parentNode, search)) { 43 | return; 44 | } 45 | elem.parentNode.style.display = "block"; 46 | }); 47 | //hide parents without children 48 | document.querySelectorAll("nav > ul > li").forEach(function(parent) { 49 | var countSearchA = 0; 50 | parent.querySelectorAll("a").forEach(function(elem) { 51 | if (contains(elem, search)) { 52 | countSearchA++; 53 | } 54 | }); 55 | 56 | var countUl = 0; 57 | var countUlVisible = 0; 58 | parent.querySelectorAll("ul").forEach(function(ulP) { 59 | // count all elements that match the search 60 | if (contains(ulP, search)) { 61 | countUl++; 62 | } 63 | 64 | // count all visible elements 65 | var children = ulP.children 66 | for (i=0; i ul > li:not(.level-hide)").forEach(function(elem) { 16 | elem.style.display = "block"; 17 | }); 18 | 19 | if (typeof hideAllButCurrent === "function"){ 20 | //let's do what ever collapse wants to do 21 | hideAllButCurrent(); 22 | } else { 23 | //menu by default should be opened 24 | document.querySelectorAll("nav > ul > li > ul li").forEach(function(elem) { 25 | elem.style.display = "block"; 26 | }); 27 | } 28 | } else { 29 | //we are searching 30 | document.documentElement.setAttribute(searchAttr, ''); 31 | 32 | //show all parents 33 | document.querySelectorAll("nav > ul > li").forEach(function(elem) { 34 | elem.style.display = "block"; 35 | }); 36 | //hide all results 37 | document.querySelectorAll("nav > ul > li > ul li").forEach(function(elem) { 38 | elem.style.display = "none"; 39 | }); 40 | //show results matching filter 41 | document.querySelectorAll("nav > ul > li > ul a").forEach(function(elem) { 42 | if (!contains(elem.parentNode, search)) { 43 | return; 44 | } 45 | elem.parentNode.style.display = "block"; 46 | }); 47 | //hide parents without children 48 | document.querySelectorAll("nav > ul > li").forEach(function(parent) { 49 | var countSearchA = 0; 50 | parent.querySelectorAll("a").forEach(function(elem) { 51 | if (contains(elem, search)) { 52 | countSearchA++; 53 | } 54 | }); 55 | 56 | var countUl = 0; 57 | var countUlVisible = 0; 58 | parent.querySelectorAll("ul").forEach(function(ulP) { 59 | // count all elements that match the search 60 | if (contains(ulP, search)) { 61 | countUl++; 62 | } 63 | 64 | // count all visible elements 65 | var children = ulP.children 66 | for (i=0; i", 5 | "description": "This is SideeX JavaScript API for recording and playing web browsing behavior. The API can be embeded in a webpage without installing a browser web extension.", 6 | "scripts": { 7 | "build-api": "webpack --mode=production --config webpack.api.config.js", 8 | "build-browser": "webpack --mode=production --config webpack.browser.config.js", 9 | "doc": "jsdoc -c jsdoc.json" 10 | }, 11 | "main": "dist/index.bundle.js", 12 | "keywords": [ 13 | "SideeX", 14 | "web testing", 15 | "web automation", 16 | "record and playback", 17 | "GUI testing" 18 | ], 19 | "repository": { 20 | "type": "git", 21 | "url": "https://github.com/SideeX/sideex-api.git" 22 | }, 23 | "license": "Apache-2.0", 24 | "bugs": { 25 | "url": "https://github.com/SideeX/sideex-api/issues" 26 | }, 27 | "homepage": "https://sideex.io/", 28 | "devDependencies": { 29 | "@babel/cli": "^7.8.4", 30 | "@babel/core": "^7.9.0", 31 | "@babel/plugin-proposal-class-properties": "^7.8.3", 32 | "@babel/plugin-proposal-decorators": "^7.8.3", 33 | "@babel/plugin-proposal-export-namespace-from": "^7.8.3", 34 | "@babel/plugin-proposal-function-sent": "^7.8.3", 35 | "@babel/plugin-proposal-json-strings": "^7.8.3", 36 | "@babel/plugin-proposal-numeric-separator": "^7.8.3", 37 | "@babel/plugin-proposal-object-rest-spread": "^7.9.5", 38 | "@babel/plugin-proposal-throw-expressions": "^7.8.3", 39 | "@babel/plugin-syntax-dynamic-import": "^7.8.3", 40 | "@babel/plugin-syntax-import-meta": "^7.8.3", 41 | "@babel/preset-env": "^7.9.5", 42 | "@babel/preset-react": "^7.9.4", 43 | "@babel/preset-typescript": "^7.9.0", 44 | "autoprefixer": "^9.7.6", 45 | "babel-core": "^6.26.3", 46 | "babel-loader": "^8.1.0", 47 | "copy-webpack-plugin": "^5.1.1", 48 | "css-loader": "^1.0.1", 49 | "docdash": "^1.2.0", 50 | "dotenv": "^8.2.0", 51 | "eslint": "^5.16.0", 52 | "file-loader": "^1.1.11", 53 | "html-loader": "^0.5.5", 54 | "html-webpack-plugin": "^3.2.0", 55 | "img-loader": "^3.0.1", 56 | "jest": "^25.4.0", 57 | "jsdoc": "^3.6.4", 58 | "postcss-loader": "^3.0.0", 59 | "sass-loader": "^7.3.1", 60 | "style-loader": "^0.23.1", 61 | "terser-webpack-plugin": "^2.3.6", 62 | "typescript": "^3.8.3", 63 | "url-loader": "^1.1.2", 64 | "webextension-polyfill-ts": "^0.14.0", 65 | "webpack": "^4.42.1", 66 | "webpack-cli": "^3.3.11", 67 | "webpack-dev-server": "^3.10.3", 68 | "webpack-merge": "^4.2.2", 69 | "webpack-node-externals": "^1.7.2", 70 | "webpack-preprocessor-loader": "^1.1.2", 71 | "core-js": "^3.6.5" 72 | }, 73 | "dependencies": { 74 | "autobind-decorator": "^2.4.0", 75 | "lodash": "^4.17.15", 76 | "platform": "^1.3.5", 77 | "sizzle": "^2.3.5", 78 | "uuid": "^8.0.0" 79 | }, 80 | "browserslist": [ 81 | "last 2 Chrome versions" 82 | ], 83 | "files": [ 84 | "LICENSE", 85 | "README.md", 86 | "dist/*" 87 | ] 88 | } 89 | -------------------------------------------------------------------------------- /custom-template/tmpl/properties.tmpl: -------------------------------------------------------------------------------- 1 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 79 | 80 | 81 | 90 | 91 | 92 | 93 | 98 | 99 | 100 | 103 | 104 | 105 | 106 | 107 |
NameTypeAttributesDefaultDescription
75 | 76 | 77 | 78 | 82 | 83 | <optional>
84 | 85 | 86 | 87 | <nullable>
88 | 89 |
94 | 95 | 96 | 97 | 101 |
Properties
102 |
108 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 6 | # SideeX JavaScript API 7 | [![npm-version](https://img.shields.io/npm/v/@sideex/api)](https://www.npmjs.com/package/@sideex/api) [![npm-download](https://img.shields.io/npm/dw/@sideex/api)](https://www.npmjs.com/package/@sideex/api) [![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0) [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-red)](https://github.com/SideeX/sideex-api) 8 | 9 | SideeX JavaScript API is a JS library running on a webpage for recording and playing web browsing behavior. It is the core library of [SideeX 2](https://github.com/SideeX/sideex). As opposed to acting as a browser web extension, the API can be directly embedded and used within a webpage via JavaScript. 10 | 11 | # Two Ways to Use SideeX API 12 | 13 | ## Via \ 16 | 20 | ``` 21 | 22 | ## Via npm 23 | At first, download the SideeX API library via npm: 24 | ``` 25 | npm i @sideex/api 26 | ``` 27 | Then, import and use the SideeX API as follows: 28 | ```javascript 29 | import { SideeX } from "@sideex/api" 30 | var sideex = new SideeX(); 31 | //Then refer to the following code sample 1, 2, or 3 for using SideeX API 32 | ``` 33 | 34 | # Code Samples 35 | ## Sample 1 - Record and playback 36 | ```javascript 37 | sideex.recorder.start();//start recording 38 | /* 39 | Record a command 40 | For example: click at anywhere on the window, 41 | then you get a ClickAt command 42 | */ 43 | sideex.recorder.stop();//stop recording 44 | console.log(sideex.file.command.get(0));//get the first recorded command 45 | sideex.playback.start();//replay the recorded commands 46 | ``` 47 | 48 | 49 | ## Sample 2 - Save and load the recorded commands 50 | ```javascript 51 | sideex.recorder.start();//start recording 52 | /* 53 | Record a command 54 | For example: click at anywhere on the window, 55 | then you get a ClickAt command 56 | */ 57 | sideex.recorder.stop();//stop recording 58 | console.log(sideex.file.command.get(0));//get the record that you recorded 59 | let jsonString = sideex.file.testSuite.save();//serialize the recorded commands to a JSON string 60 | 61 | //save the jsonString on your own 62 | 63 | sideex.file.testSuite.load(jsonString);//load the jsonString 64 | sideex.playback.start();//replay the recorded commands 65 | ``` 66 | 67 | 68 | ## Sample 3 - Change a recorded command to a user-defined action 69 | ```javascript 70 | sideex.recorder.start();//start recording 71 | /* 72 | Record a command 73 | For example: click at anywhere on the window, 74 | then you get a ClickAt command 75 | */ 76 | sideex.recorder.stop();//stop recording 77 | console.log(sideex.file.command.get(0));//get the first recorded command 78 | let command = sideex.file.command.get(0); 79 | command.name = "myAction";//rename the command name to "myAction" 80 | console.log(sideex.file.command.get(0));//see the change of the command name 81 | //add a user-defined function for executing "myAction" 82 | sideex.playback.addCustomCommand("myAction", true, (target, value) => { 83 | console.log(target, value); 84 | //define the action here 85 | } 86 | ); 87 | sideex.playback.start();//replay the modified recorded commands 88 | ``` 89 | 90 | 91 | # [API Docs](https://sideex.github.io/sideex-api) 92 | 93 | -------------------------------------------------------------------------------- /src/content/prompt-injecter.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright SideeX committers 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | import { browserBot } from "./content-initialization"; 18 | let script = document.createElement('script'); 19 | script.src = browser.runtime.getURL('page/prompt.js'); 20 | script.onload = script.remove; 21 | 22 | window.addEventListener("message", function (event) { 23 | if (event.source == window && event.data && event.data.from == "sideex-page-env") { 24 | switch (event.data.type) { 25 | case "record": 26 | switch (event.data.args.recordedType) { 27 | case "prompt": 28 | if (event.data.args.recordedResult != null) { 29 | recorder.record("answerOnNextPrompt", [[event.data.args.recordedResult]], "", true); 30 | } else { 31 | recorder.record("chooseCancelOnNextPrompt", [[""]], "", true); 32 | } 33 | recorder.record("assertPrompt", [[event.data.args.recordedMessage]], "", false); 34 | break; 35 | case "confirm": 36 | if (event.data.args.recordedResult == true) { 37 | recorder.record("chooseOkOnNextConfirmation", [[""]], "", true); 38 | } else { 39 | recorder.record("chooseCancelOnNextConfirmation", [[""]], "", true); 40 | } 41 | recorder.record("assertConfirmation", [[event.data.args.recordedMessage]], "", false); 42 | break; 43 | case "alert": 44 | //record("answerOnNextAlert",[[event.data.args.recordedResult]],"",true); 45 | recorder.record("assertAlert", [[event.data.args.recordedMessage]], "", false); 46 | break; 47 | } 48 | break; 49 | case "response": 50 | switch (event.data.args.responsedType) { 51 | case "prompt": 52 | browserBot.promptResponse = true; 53 | if (event.data.args.value) 54 | browserBot.promptMessage = event.data.args.value; 55 | break; 56 | case "confirm": 57 | browserBot.confirmationResponse = true; 58 | if (event.data.args.value) 59 | browserBot.confirmationMessage = event.data.args.value; 60 | break; 61 | case "alert": 62 | browserBot.alertResponse = true; 63 | if (event.data.args.value) 64 | browserBot.alertMessage = event.data.args.value; 65 | break; 66 | } 67 | break; 68 | default: 69 | break; 70 | } 71 | } 72 | }); 73 | -------------------------------------------------------------------------------- /custom-template/tmpl/params.tmpl: -------------------------------------------------------------------------------- 1 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 99 | 100 | 101 | 114 | 115 | 116 | 117 | 122 | 123 | 124 | 128 | 129 | 130 | 131 | 132 |
NameTypeAttributesDefaultDescription
95 | 96 | 97 | 98 | 102 | 103 | <optional>
104 | 105 | 106 | 107 | <nullable>
108 | 109 | 110 | 111 | <repeatable>
112 | 113 |
118 | 119 | 120 | 121 | 125 |
Properties
126 | 127 |
133 |
134 | -------------------------------------------------------------------------------- /custom-template/tmpl/layout.tmpl: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | <?js= title ?> - <?js= ((env.conf.docdash.meta && env.conf.docdash.meta.title) || "Documentation") ?> 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 37 | 38 | 39 | 40 | 46 | 47 |
48 | 49 |

50 | 51 | 52 | 53 | 54 | 55 |
56 | 64 | 65 | 66 |
67 | 68 |
69 | 70 |
71 | Documentation generated by JSDoc on using the docdash theme. 72 |
73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 87 | 88 | 91 | 92 | 96 | 97 | 98 | -------------------------------------------------------------------------------- /src/content/targetSelecter.ts: -------------------------------------------------------------------------------- 1 | // Modified in tools.js from selenium-IDE 2 | export default class TargetSelecter { 3 | window: Window 4 | div: HTMLDivElement 5 | element: Element = null 6 | rect: ClientRect | DOMRect = null 7 | callback: (element: Element, window: Window) => void 8 | cleanupCallback: () => void 9 | constructor(callback?: (element: Element, window: Window) => void, cleanupCallback?: () => void) { 10 | this.callback = callback 11 | this.cleanupCallback = cleanupCallback 12 | // This is for XPCOM/XUL addon and can't be used 13 | //var wm = Components.classes["@mozilla.org/appshell/window-mediator;1"].getService(Components.interfaces.nsIWindowMediator); 14 | //this.win = wm.getMostRecentWindow('navigator:browser').getBrowser().contentWindow; 15 | // Instead, we simply assign global content window to this.win 16 | this.window = window 17 | const doc = this.window.document 18 | this.div = doc.createElement("div") 19 | this.div.style.display = "none" 20 | doc.body.insertBefore(this.div, doc.body.firstChild) 21 | doc.addEventListener("mousemove", this, true) 22 | doc.addEventListener("click", this, true) 23 | } 24 | public cleanup() { 25 | try { 26 | if (this.div) { 27 | if (this.div.parentNode) { 28 | this.div.parentNode.removeChild(this.div) 29 | } 30 | this.div = null 31 | } 32 | if (this.window) { 33 | const doc = this.window.document 34 | doc.removeEventListener("mousemove", this, true) 35 | doc.removeEventListener("click", this, true) 36 | } 37 | } catch (e) { 38 | if (!(e instanceof TypeError)) { 39 | throw e 40 | } 41 | } 42 | this.window = null 43 | if (this.cleanupCallback) { 44 | this.cleanupCallback() 45 | } 46 | } 47 | handleEvent(event: MouseEvent) { 48 | switch (event.type) { 49 | case "mousemove": 50 | this.highlight((event.target as Element).ownerDocument, event.clientX, event.clientY) 51 | break 52 | case "click": 53 | if (event.button == 0 && this.element && this.callback) { 54 | this.callback(this.element, this.window) 55 | } //Right click would cancel the select 56 | event.preventDefault() 57 | event.stopPropagation() 58 | this.cleanup() 59 | break 60 | } 61 | } 62 | highlight(doc: Document, x: number, y: number) { 63 | if (doc) { 64 | const elenemt = doc.elementFromPoint(x, y) 65 | if (elenemt && elenemt != this.element) { 66 | this.highlightElement(elenemt) 67 | } 68 | } 69 | } 70 | highlightElement(element: Element) { 71 | if (element && element != this.element) { 72 | this.element = element 73 | } else { 74 | return 75 | } 76 | const rect = element.getBoundingClientRect() 77 | const oldrect = this.rect 78 | if (rect.left >= 0 && rect.top >= 0 && rect.width > 0 && rect.height > 0) { 79 | if (oldrect && rect.top == oldrect.top && rect.left == oldrect.left && rect.width == oldrect.width && rect.height == oldrect.height) { 80 | return 81 | } 82 | this.rect = rect 83 | this.div.style.display = "block" 84 | this.div.style.position = "fixed" 85 | this.div.style.pointerEvents = "none" 86 | this.div.style.boxShadow = "0 0 0 1px black" 87 | this.div.style.outline = "1px dashed white" 88 | this.div.style.outlineOffset = "-1px" 89 | this.div.style.backgroundColor = "rgba(250,250,128,0.4)" 90 | this.div.style.zIndex = "100" 91 | 92 | this.div.style.left = String(rect.left + this.window.scrollX) + "px" 93 | this.div.style.top = String(rect.top + this.window.scrollY) + "px" 94 | this.div.style.width = String(rect.width) + "px" 95 | this.div.style.height = String(rect.height) + "px" 96 | console.log(this.div.style); 97 | } else if (oldrect) { 98 | this.div.style.display = "none" 99 | } 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /custom-template/tmpl/method.tmpl: -------------------------------------------------------------------------------- 1 | 5 | 6 | 7 |

Constructor

8 | 9 | 10 |

21 | 22 | 23 |

24 | 25 | 26 | 27 | 28 | 29 | 30 |
31 | 32 |
33 | 34 | 35 | 36 |
Extends:
37 | 38 | 39 | 40 | 41 |
Type:
42 |
    43 |
  • 44 | 45 |
  • 46 |
47 | 48 | 49 | 50 |
This:
51 |
52 | 53 | 54 | 55 |
Example 1? 's':'' ?>
56 | 57 | 58 | 59 | 60 |
Parameters:
61 | 62 | 63 | 64 | 65 |
Requires:
66 |
    67 |
  • 68 |
69 | 70 | 71 | 72 |
Fires:
73 |
    74 |
  • 75 |
76 | 77 | 78 | 79 |
Listens to Events:
80 |
    81 |
  • 82 |
83 | 84 | 85 | 86 |
Listeners of This Event:
87 |
    88 |
  • 89 |
90 | 91 | 92 | 93 |
Modifies:
94 | 1) { ?>
    96 |
  • 97 |
100 | 101 | 103 | 104 | 105 |
Throws:
106 | 1) { ?>
    108 |
  • 109 |
112 | 113 | 115 | 116 | 117 |
Returns:
118 | 1) { ?>
    120 |
  • 121 |
124 | 125 | 127 | 128 | 129 |
Yields:
130 | 1) { ?>
    132 |
  • 133 |
136 | 137 | 139 | -------------------------------------------------------------------------------- /src/page/onsubmit.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright SideeX committers 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | 19 | (function () { 20 | // for webdriver to save to global 21 | if (typeof window.sideex !== 'undefined') { 22 | window.isEnter = false; 23 | } 24 | 25 | let isEnter = false; 26 | 27 | window.addEventListener("click", function (event) { 28 | if (!isEnter) { 29 | if (event.target.getAttribute('type') == 'submit') { 30 | let tempTarget = event.target.parentElement; 31 | let formChk = tempTarget.tagName.toLowerCase(); 32 | while (formChk != 'form' && formChk != 'body') { 33 | tempTarget = tempTarget.parentElement; 34 | formChk = tempTarget.tagName.toLowerCase(); 35 | } 36 | if (formChk == 'form') { 37 | let originalOnsubmit = tempTarget.onsubmit; 38 | let originalSubmit = tempTarget.submit; 39 | if (tempTarget.onsubmit != null) { 40 | tempTarget.onsubmit = function () { 41 | let event; 42 | event = document.createEvent('Event'); 43 | event.initEvent('onsubmit', true, true); 44 | event.view = window; 45 | event.bubbles = true; 46 | tempTarget.dispatchEvent(event); 47 | originalOnsubmit(); 48 | }; 49 | } 50 | if (tempTarget.submit != null) { 51 | tempTarget.submit = function () { 52 | let event; 53 | event = document.createEvent('Event'); 54 | event.initEvent('cleanBuffer', true, true); 55 | event.view = window; 56 | event.bubbles = true; 57 | tempTarget.dispatchEvent(event); 58 | originalSubmit.apply(tempTarget); 59 | }; 60 | } 61 | } 62 | } 63 | } 64 | }, true); 65 | 66 | window.addEventListener("keydown", function (event) { 67 | if (event.key == "Enter") { 68 | let tempTarget = event.target.parentElement; 69 | let formChk = tempTarget.tagName; 70 | while (["FORM", "BODY", "HTML"].indexOf(formChk) == -1) { 71 | tempTarget = tempTarget.parentElement; 72 | formChk = tempTarget.tagName; 73 | } 74 | if (formChk == 'FORM') { 75 | let originalOnsubmit = tempTarget.onsubmit; 76 | let originalSubmit = tempTarget.submit; 77 | if (tempTarget.onsubmit != null) { 78 | tempTarget.onsubmit = function () { 79 | let event; 80 | event = document.createEvent('Event'); 81 | event.initEvent('onsubmit', true, true); 82 | event.view = window; 83 | event.bubbles = true; 84 | tempTarget.dispatchEvent(event); 85 | originalOnsubmit(); 86 | }; 87 | } 88 | if (tempTarget.submit != null) { 89 | tempTarget.submit = function () { 90 | let event; 91 | event = document.createEvent('Event'); 92 | event.initEvent('cleanBuffer', true, true); 93 | event.view = window; 94 | event.bubbles = true; 95 | tempTarget.dispatchEvent(event); 96 | originalSubmit.apply(tempTarget); 97 | }; 98 | } 99 | } 100 | } 101 | isEnter = true; 102 | setTimeout(() => { 103 | isEnter = false; 104 | }, 500); 105 | }, true); 106 | })(); 107 | 108 | -------------------------------------------------------------------------------- /src/background/background.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright SideeX committers 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | import { Utils } from "../utils/index"; 18 | import { MessageController } from "../content/message-controller"; 19 | 20 | let master = {}; 21 | let clickEnabled = true; 22 | let menusCreated = false; 23 | const menuElements = [ 24 | "verifyText", 25 | "verifyTitle", 26 | "verifyValue", 27 | "verifyVisibility", 28 | "assertText", 29 | "assertTitle", 30 | "assertValue", 31 | "storeText", 32 | "storeTitle", 33 | "storeValue" 34 | ]; 35 | 36 | browser.tabs.onUpdated.addListener(async function (tabId, changeInfo, tabInfo) { 37 | if (changeInfo.status && changeInfo.status == "complete") { 38 | console.log("master: ", master); 39 | for (const contentWindowId of Object.keys(master)) { 40 | if (master[contentWindowId] === tabInfo.windowId) { 41 | try { 42 | const response = await MessageController.tabSendMessage({ 43 | selfWindowId: tabInfo.windowId, 44 | commWindowId: Number(contentWindowId) 45 | }, tabId); 46 | return response.check; 47 | } catch (error) { 48 | console.error(error); 49 | } 50 | } 51 | } 52 | } 53 | }); 54 | 55 | browser.windows.onRemoved.addListener(function (windowId) { 56 | for (let contentWindowId in master) { 57 | if (master[contentWindowId] === windowId) { 58 | unbind(contentWindowId); 59 | } 60 | } 61 | }); 62 | 63 | browser.browserAction.onClicked.addListener(async function focusOrCreatePanel(tab) { 64 | let contentWindowId = tab.windowId; 65 | if (master[contentWindowId] != undefined) { 66 | await browser.windows.update(master[contentWindowId], { focused: true }); 67 | return; 68 | } else if (!clickEnabled) { 69 | return; 70 | } 71 | 72 | // If we click the icon continually in a very short time, 73 | // multiple panels will be created and binded to the same window. 74 | // Add a little delay to prevent unexpected error. 75 | clickEnabled = false; 76 | (async () => { 77 | await Utils.delay(1000); 78 | clickEnabled = true; 79 | })(); 80 | 81 | try { 82 | const panelWindowInfo = await browser.windows.create({ 83 | url: browser.runtime.getURL("panel/index.html"), 84 | type: "popup", 85 | height: 400 + 100, 86 | width: 200 + 320 87 | }); 88 | bind(contentWindowId, panelWindowInfo.id); 89 | } catch (error) { 90 | unbind(contentWindowId); 91 | } 92 | }); 93 | 94 | 95 | 96 | function bind(contentWindowId, panelWindowId) { 97 | master[contentWindowId] = panelWindowId; 98 | if (!menusCreated) { 99 | menusCreated = true; 100 | createMenus(); 101 | } 102 | } 103 | 104 | function unbind(contentWindowId) { 105 | delete master[contentWindowId]; 106 | if (Object.keys(master).length === 0) { 107 | browser.contextMenus.removeAll().then(function () { 108 | menusCreated = false; 109 | return; 110 | }).catch(function () { 111 | // always success 112 | }); 113 | } 114 | } 115 | 116 | function createMenus() { 117 | for (let element of menuElements) { 118 | browser.contextMenus.create({ 119 | id: element, 120 | title: element, 121 | documentUrlPatterns: [""], 122 | contexts: ["all"] 123 | }); 124 | } 125 | } 126 | 127 | var port; 128 | browser.contextMenus.onClicked.addListener(function (info) { 129 | port.postMessage({ cmd: info.menuItemId }); 130 | }); 131 | 132 | browser.runtime.onConnect.addListener(function (p) { 133 | port = p; 134 | }); 135 | 136 | browser.runtime.onInstalled.addListener(async function (details) { 137 | if (details.reason == "install" || details.reason == "update") { 138 | await browser.tabs.create({ url: "http://sideex.org" }); 139 | } 140 | }); 141 | -------------------------------------------------------------------------------- /src/content/command-receiver-for-api.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright SideeX committers 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | import TargetSelecter from "./targetSelecter"; 18 | import { sideex, locatorBuilders, browserBot, recorder} from "./content-initialization"; 19 | import { MessageController } from "../content/message-controller"; 20 | 21 | var targetSelecter = null; 22 | MessageController.addListener(async function doCommands(request) { 23 | // console.log(request); 24 | // request = request.data; 25 | switch (request.action) { 26 | case "Wait": { 27 | await sideex.doAutoWait(request.command, request.value); 28 | return {}; 29 | } 30 | case "Command": { 31 | // console.log("command", request.command); 32 | let command = request.command; 33 | let value = request.value; 34 | if (sideex.hasCommand(command)) { 35 | document.documentElement.setAttribute("SideeXPlayingFlag", true); 36 | let target = await sideex.doVerifyLocator(request); 37 | try { 38 | await sideex.doCommand(command, target, value); 39 | } catch (e) { 40 | console.error(command + " failed\n", e.stack); 41 | document.documentElement.removeAttribute("SideeXPlayingFlag"); 42 | return { status: false, message: e.message }; 43 | } 44 | } else { 45 | return { result: "Unknown command: " + command }; 46 | } 47 | // Command success 48 | document.documentElement.removeAttribute("SideeXPlayingFlag"); 49 | return { status: true }; 50 | } 51 | case "SelectElement": { 52 | // console.log("selectelement"); 53 | if (request.selecting) { 54 | targetSelecter = new TargetSelecter(function (element, win) { 55 | if (element && win) { 56 | //var locatorBuilders = new LocatorBuilders(win); 57 | var target = locatorBuilders.buildAll(element); 58 | locatorBuilders.detach(); 59 | if (target != null && target instanceof Array) { 60 | if (target) { 61 | //self.editor.treeView.updateCurrentCommand('targetCandidates', target); 62 | MessageController.runtimeSendMessage({ 63 | selectTarget: true, 64 | target: target 65 | }); 66 | } else { 67 | //alert("LOCATOR_DETECTION_FAILED"); 68 | } 69 | } 70 | } 71 | targetSelecter = null; 72 | }, async function () { 73 | await MessageController.runtimeSendMessage({ 74 | cancelSelectTarget: true 75 | }); 76 | }); 77 | } else { 78 | if (targetSelecter) { 79 | targetSelecter.cleanup(); 80 | targetSelecter = null; 81 | return {}; 82 | } 83 | } 84 | return {}; 85 | } 86 | case "ShowElement": { 87 | try { 88 | let element = browserBot.findElement(request.targetValue.value); 89 | await sideex.doShowElement(element, request.customHtml); 90 | await sideex.doConcealElement(500); 91 | return { result: true }; 92 | } catch (error) { 93 | return { result: false }; 94 | } 95 | } 96 | case "AttachRecorder": { 97 | recorder.attach(); 98 | return {}; 99 | } 100 | case "DetachRecorder": { 101 | recorder.detach(); 102 | return {}; 103 | } 104 | // default: 105 | // throw new Error("Action not found"); 106 | } 107 | }); 108 | 109 | -------------------------------------------------------------------------------- /src/common/patternMatcher.ts: -------------------------------------------------------------------------------- 1 | import { SeleniumError } from "./seleniumError"; 2 | export class RegexMatcher { 3 | private regexp: RegExp 4 | constructor(regexp: RegExp) { 5 | this.regexp = regexp; 6 | } 7 | matches(actual: string) { 8 | return this.regexp.test(actual); 9 | } 10 | } 11 | export interface RegexMatcherConstructor { 12 | new(string: string): RegexMatcher 13 | } 14 | export class PatternMatcher { 15 | private matcher: RegexMatcher 16 | constructor(pattern: string) { 17 | this.selectStrategy(pattern); 18 | } 19 | selectStrategy(pattern: string): void { 20 | let strategyName = 'glob'; 21 | // by default 22 | if (/^([a-z-]+):(.*)/.test(pattern)) { 23 | var possibleNewStrategyName = RegExp.$1; 24 | var possibleNewPattern = RegExp.$2; 25 | if (PatternMatcher.strategies[possibleNewStrategyName]) { 26 | strategyName = possibleNewStrategyName; 27 | pattern = possibleNewPattern; 28 | } 29 | } 30 | const matchStrategy = PatternMatcher.strategies[strategyName]; 31 | if (!matchStrategy) { 32 | throw new SeleniumError(`cannot find PatternMatcher.strategies.${strategyName}`); 33 | } 34 | this.matcher = new matchStrategy(pattern); 35 | } 36 | matches(actual: string) { 37 | // Note: appending an empty string avoids a Konqueror bug 38 | return this.matcher.matches(actual + ''); 39 | } 40 | /** @summary A "static" convenience method for easy matching */ 41 | static matches(pattern: string, actual: string) { 42 | return new PatternMatcher(pattern).matches(actual); 43 | } 44 | 45 | static convertGlobMetaCharsToRegexpMetaChars(glob: string) { 46 | return glob.replace(/([.^$+(){}[\]\\|])/g, "\\$1") 47 | .replace(/\?/g, "(.|[\r\n])") 48 | .replace(/\*/g, "(.|[\r\n])*"); 49 | } 50 | static regexpFromGlobContains(globContains: string) { 51 | return PatternMatcher.convertGlobMetaCharsToRegexpMetaChars(globContains); 52 | } 53 | static regexpFromGlob = function (glob: string) { 54 | return `^${PatternMatcher.convertGlobMetaCharsToRegexpMetaChars(glob)}$`; 55 | } 56 | static strategies: { [key: string]: new(string: string)=> RegexMatcher } = { 57 | /** @summary Exact matching, e.g. "exact:***" */ 58 | exact: class extends RegexMatcher { 59 | constructor(expected: string) { 60 | //overwrite default match strategy 61 | super(null); 62 | this.matches = function (actual: string) { 63 | return expected == actual; 64 | }; 65 | } 66 | }, 67 | /** @summary Match by regular expression, e.g. "regexp:^[0-9]+$" */ 68 | regexp: class extends RegexMatcher { 69 | constructor(regexpString: string) { 70 | super(new RegExp(regexpString)); 71 | } 72 | }, 73 | regex: class extends RegexMatcher { 74 | constructor(regexpString: string) { 75 | super(new RegExp(regexpString)); 76 | } 77 | }, 78 | regexpi: class extends RegexMatcher { 79 | constructor(regexpString: string) { 80 | super(new RegExp(regexpString, "i")); 81 | } 82 | }, 83 | regexi: class extends RegexMatcher { 84 | constructor(regexpString: string) { 85 | super(new RegExp(regexpString, "i")); 86 | } 87 | }, 88 | 89 | /** 90 | * @summary "globContains" (aka "wildmat") patterns, e.g. "glob:one,two,*", 91 | * but don't require a perfect match; instead succeed if actual 92 | * contains something that matches globString. 93 | * @description Making this distinction is motivated by a bug in IE6 which 94 | * leads to the browser hanging if we implement *TextPresent tests 95 | * by just matching against a regular expression beginning and 96 | * ending with ".*". The globcontains strategy allows us to satisfy 97 | * the functional needs of the *TextPresent ops more efficiently 98 | * and so avoid running into this IE6 freeze. 99 | */ 100 | globContains: class extends RegexMatcher { 101 | constructor(globString: string) { 102 | super(new RegExp(PatternMatcher.regexpFromGlobContains(globString))); 103 | } 104 | }, 105 | /** @summary "glob" (aka "wildmat") patterns, e.g. "glob:one,two,*" */ 106 | glob: class extends RegexMatcher { 107 | constructor(globString: string) { 108 | super(new RegExp(PatternMatcher.regexpFromGlob(globString))); 109 | } 110 | } 111 | }; 112 | } 113 | -------------------------------------------------------------------------------- /src/page/autoWait.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright SideeX committers 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 'use strict'; 18 | 19 | class AutoWait { 20 | 21 | constructor() { 22 | this.startTime = 0; 23 | this.waitErrType = ""; 24 | } 25 | 26 | doAutoWaitPreprocessing() { 27 | this.clearXHRInstance(); 28 | 29 | let _send = window.XMLHttpRequest.prototype.send; 30 | window.XMLHttpRequest.prototype.send = function () { 31 | window.ajax_obj.push(this); 32 | return _send.apply(this, arguments); 33 | }; 34 | } 35 | 36 | clearXHRInstance() { 37 | window.ajax_obj = []; 38 | } 39 | 40 | checkAjaxWait() { 41 | if (window.ajax_obj.length == 0) { 42 | return true; 43 | } else { 44 | for (const obj of window.ajax_obj) { 45 | // if readyState is 1~3, then keep waiting 46 | switch (obj.readyState) { 47 | case 1: 48 | case 2: 49 | return false; 50 | case 3: 51 | continue; 52 | default: 53 | break; 54 | } 55 | this.removeArrayItem(window.ajax_obj, obj); 56 | console.log(window.ajax_obj); 57 | } 58 | return true; 59 | } 60 | } 61 | 62 | async checkPageWaitLoop() { 63 | this.waitErrType = "pageWait"; 64 | while (true) { 65 | let response = window.document.readyState == "complete"; 66 | if (this.startTime != 0 && (Date.now() - this.startTime) > AutoWait.PAGE_WAIT_TIMEOUT) { 67 | this.startTime = 0; 68 | if (typeof window.sideexDone !== 'undefined') { 69 | window.sideexDone(false); 70 | } 71 | return false; 72 | } else if (response) { 73 | this.startTime = 0; 74 | return true; 75 | } else { 76 | if (this.startTime == 0) { 77 | this.startTime = Date.now(); 78 | } 79 | await this.delay(30); 80 | continue; 81 | } 82 | } 83 | } 84 | 85 | async checkAjaxWaitLoop() { 86 | this.waitErrType = "ajaxWait"; 87 | while (true) { 88 | let response = this.checkAjaxWait(); 89 | if (this.startTime != 0 && (Date.now() - this.startTime) > AutoWait.COMMAND_WAIT_TIMEOUT) { 90 | this.startTime = 0; 91 | return false; 92 | } else if (response) { 93 | this.startTime = 0; 94 | return true; 95 | } else { 96 | if (this.startTime == 0) { 97 | this.startTime = Date.now(); 98 | } 99 | await this.delay(1000); 100 | continue; 101 | } 102 | } 103 | } 104 | 105 | async checkDOMWait() { 106 | this.waitErrType = "DOMWait"; 107 | // await this.delay(preWaitTime); 108 | return true; 109 | } 110 | 111 | async delay(time) { 112 | await new Promise((resolve) => { 113 | setTimeout(resolve, time); 114 | }); 115 | } 116 | 117 | removeArrayItem(array, item) { 118 | var index = array.indexOf(item); 119 | if (index > -1) { 120 | array.splice(index, 1); 121 | } 122 | return array; 123 | } 124 | 125 | static get PAGE_WAIT_TIMEOUT() { 126 | return 30000; 127 | } 128 | 129 | static get COMMAND_WAIT_TIMEOUT() { 130 | return 10000; 131 | } 132 | } 133 | 134 | window.autoWait = new AutoWait; 135 | 136 | autoWait.doAutoWaitPreprocessing(); 137 | 138 | window.addEventListener("unload", autoWait.clearXHRInstance, false); 139 | 140 | window.addEventListener("message", async function (event) { 141 | if (event.source == window && event.data) { 142 | if (event.data.direction == "from-content-playback-auto-wait") { 143 | let type = event.data.type; 144 | let result = false; 145 | result = await autoWait.checkPageWaitLoop(); 146 | result = result && await autoWait.checkAjaxWaitLoop(); 147 | result = result && await autoWait.checkDOMWait(); 148 | 149 | window.postMessage({ 150 | direction: "from-page-playback-auto-wait", 151 | type: type, 152 | result: result 153 | }, "*"); 154 | 155 | } 156 | } 157 | }); 158 | -------------------------------------------------------------------------------- /custom-template/tmpl/details.tmpl: -------------------------------------------------------------------------------- 1 | " + data.defaultvalue + ""; 13 | defaultObjectClass = ' class="object-value"'; 14 | } 15 | ?> 16 | 17 |
18 | 19 | 20 |
Source:
21 |
  • 22 | , 23 |
24 | 25 | 26 | 27 |
Version:
28 |
29 | 30 | 31 | 32 |
Since:
33 |
34 | 35 | 36 | 37 |
Inherited From:
38 |
  • 39 | 40 |
41 | 42 | 43 | 44 |
Overrides:
45 |
  • 46 | 47 |
48 | 49 | 50 | 51 |
Implementations:
52 |
    53 | 54 |
  • 55 | 56 |
57 | 58 | 59 | 60 |
Implements:
61 |
    62 | 63 |
  • 64 | 65 |
66 | 67 | 68 | 69 |
Mixes In:
70 | 71 |
    72 | 73 |
  • 74 | 75 |
76 | 77 | 78 | 79 |
Deprecated:
  • Yes
83 | 84 | 85 | 86 |
Author:
87 |
88 |
    89 |
  • 90 |
91 |
92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 |
License:
101 |
102 | 103 | 104 | 105 |
Default Value:
106 |
    107 | > 108 |
109 | 110 | 111 | 112 |
Tutorials:
113 |
114 |
    115 |
  • 116 |
117 |
118 | 119 | 120 | 121 |
See:
122 |
123 |
    124 |
  • 125 |
126 |
127 | 128 | 129 | 130 |
To Do:
131 |
132 |
    133 |
  • 134 |
135 |
136 | 137 |
138 | 139 | 143 | 144 |
Properties:
145 | 146 | 147 | 148 | 149 | -------------------------------------------------------------------------------- /src/panel/js/background/preRecorder.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright SideeX committers 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | export class PreRecorder { 19 | constructor(root, bgRecorder) { 20 | this.root = root; 21 | this.bgRecorder = bgRecorder; 22 | 23 | this.isNeedBuffer = false; 24 | this.isNeedReturn = false; 25 | this.buffer = []; 26 | this.bufferInsertBeforeLastCommand = []; 27 | this.bufferActualFrameLocation = []; 28 | } 29 | 30 | /** 31 | * TBD 32 | */ 33 | flushBuffer(isNeedBuffer) { 34 | this.isNeedBuffer = isNeedBuffer; 35 | let bufferLength = this.buffer.length; 36 | for (let i = 0; i < bufferLength; i++) { 37 | this.buffer.shift(); 38 | } 39 | } 40 | 41 | isReturn() { 42 | return this.isNeedReturn; 43 | } 44 | 45 | async promptVarName(command, windowId) { 46 | try { 47 | // In Google Chrome, window.prompt() must be triggered in 48 | // an active tabs of front window, so we let panel window been focused 49 | await browser.windows.update(windowId, {focused: true}); 50 | // Even if window has been focused, window.prompt() still failed. 51 | // Delay a little time to ensure that status has been updated 52 | var varName = ""; 53 | await setTimeout(() => { 54 | varName = prompt("Enter the name of the variable"); 55 | }, 100); 56 | } catch (e) { 57 | console.log(e); 58 | } 59 | return varName; 60 | } 61 | 62 | appendCommand(command) { 63 | if (command.insertBeforeLastCommand) { 64 | this.root.fileController.addCommandBeforeLastCommand(command.command, command.target, command.value); 65 | } else { 66 | this.bgRecorder.notification(command.command, command.target.options[0].value, command.value.options[0].value); 67 | this.root.fileController.insertCommand("after", command.command, command.target, command.value); 68 | } 69 | } 70 | 71 | deleteCommandNum(command) { 72 | let records = this.root.fileController.getRecords(this.root.fileController.getSelectedCases()[0]); 73 | let length = records.length; 74 | let lastCommand = records[length - 1]; 75 | 76 | if (command.command == "doubleClickAt") { 77 | if (this.root.fileController.compareTwoCommand(this.root.fileController.getSelectedCases()[0], length - 1, length - 2, "ntv")) { 78 | if (lastCommand.name === "clickAt" || records[length - 2].name === "clickAt") { 79 | return 2; 80 | } 81 | } 82 | } 83 | 84 | return 0; 85 | } 86 | 87 | async preProcess(command, windowId) { 88 | // console.log(command); 89 | this.isNeedReturn = false; 90 | 91 | let deleteNum = this.deleteCommandNum(command); 92 | for (let i = 0; i < deleteNum; i++) { 93 | this.root.fileController.deleteLastRecord( 94 | this.root.fileController.getSelectedCases()[0] 95 | ); 96 | } 97 | 98 | 99 | if (command.command.includes("Value") && command.value.options[0].value === undefined) { 100 | this.root.log.pushLog("error", "Error: This element does not have property 'value'. Please change to use storeText command."); 101 | this.isNeedReturn = true; 102 | } else if (command.command.includes("Text") && command.value.options[0].value === '') { 103 | this.root.log.pushLog("error", "Error: This element does not have property 'Text'. Please change to use storeValue command."); 104 | this.isNeedReturn = true; 105 | } else if (command.command.includes("store")) { 106 | command.value.options[0].value = await this.promptVarName(command, windowId); 107 | this.appendCommand(command); 108 | this.isNeedReturn = true; 109 | } else if (command.command == "submit") { 110 | this.isNeedBuffer = false; 111 | var elementFound = false; 112 | for (let element of this.buffer) { 113 | this.appendCommand(element); 114 | elementFound = true; 115 | } 116 | 117 | this.flushBuffer(this.isNeedBuffer); 118 | if (elementFound == true) { 119 | this.isNeedReturn = true; 120 | } else { 121 | command.value.options[0].value = command.value.options[0].value.toString(); 122 | } 123 | } else if (command.command == "cleanBuffer") { 124 | this.flushBuffer(false); 125 | this.isNeedReturn = true; 126 | } else if (command.command == "onsubmit" || this.isNeedBuffer) { 127 | this.isNeedBuffer = true; 128 | this.isNeedReturn = true; 129 | this.buffer.push({ 130 | command: command.command, 131 | target: command.target, 132 | value: command.value, 133 | insertBeforeLastCommand: command.insertBeforeLastCommand, 134 | actualFrameLocation: command.actualFrameLocation 135 | }); 136 | } 137 | } 138 | } 139 | 140 | -------------------------------------------------------------------------------- /src/page/keys.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright SideeX committers 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | (function () { 18 | let sendString = false; 19 | let tempElement = ""; 20 | let sendKeysAttribute = ["text", "password", "email", "url", "search", "tel", "number"]; 21 | function triggerKeyEvent(element, eventType, keySequence, canBubble, controlKeyDown, altKeyDown, shiftKeyDown) { 22 | let charCode = (eventType == 'keypress') ? keySequence.charCode : 0; 23 | let keyCode = (eventType == 'keypress') ? keySequence.charCode : keySequence.keyCode; 24 | let which = (eventType == 'keypress') ? keySequence.charCode : keySequence.which; 25 | let event = new Event(eventType, { 26 | bubbles: true, 27 | cancelable: true 28 | }); 29 | event.view = window; 30 | event.altKey = altKeyDown; 31 | event.ctrlKey = controlKeyDown; 32 | event.shiftKey = shiftKeyDown; 33 | event.keyCode = keyCode; 34 | event.which = which; 35 | event.charCode = charCode; 36 | event.code = keySequence.code; 37 | event.key = keySequence.key; 38 | element.dispatchEvent(event); 39 | } 40 | 41 | function triggerInputEvent(element, eventType, data) { 42 | let event; 43 | event = document.createEvent('Event'); 44 | event.initEvent(eventType, true, true); 45 | event.view = window; 46 | event.data = data.data; 47 | element.dispatchEvent(event); 48 | } 49 | 50 | function findElement(locator) { 51 | switch (locator.type) { 52 | case 'id': 53 | return document.querySelector('#' + locator.string); 54 | case 'link': { 55 | return Array.from(document.getElementsByTagName("A")).find(link => link.textContent == locator.string); 56 | } 57 | case 'name': 58 | return document.getElementsByName(locator.string)[0]; 59 | case 'css': 60 | return document.querySelector(locator.string); 61 | case 'implicit': 62 | return document.evaluate(locator.string, document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue; 63 | default: 64 | return null; 65 | } 66 | } 67 | 68 | window.addEventListener("message", function (event) { 69 | if (event.source == window && event.data && event.data.direction == "from-sendkeys") { 70 | let element = tempElement; 71 | if (!sendString) { 72 | element = findElement(event.data.element); 73 | } 74 | if (event.data.keys == "") { 75 | //element.value = ""; 76 | sendString = true; 77 | tempElement = element; 78 | return; 79 | } 80 | 81 | if ('string' == typeof event.data.keys) { 82 | if (event.data.keys == "sendkeyEnd") { 83 | let evt = new Event("change", { 84 | bubbles: true, 85 | cancelable: false 86 | }); 87 | element.dispatchEvent(evt); 88 | sendString = false; 89 | tempElement = ""; 90 | } else { 91 | triggerInputEvent(element, 'textInput', event.data.keys); 92 | triggerInputEvent(element, 'input', event.data.keys); 93 | //element.value += event.data.keys; 94 | } 95 | } else if (event.data.keys.charCode != 0 && event.data.keys.keyCode != 13) { 96 | if (sendKeysAttribute.indexOf(element.type) >= 0) { 97 | if (!event.data.repeat) { 98 | //element.value += event.data.keys.data; 99 | } 100 | triggerKeyEvent(element, 'keydown', event.data.keys); 101 | triggerKeyEvent(element, 'keypress', event.data.keys); 102 | triggerInputEvent(element, 'textInput', event.data.keys); 103 | triggerInputEvent(element, 'input', event.data.keys); 104 | triggerKeyEvent(element, 'keyup', event.data.keys); 105 | } else { 106 | //element.value = event.data.keys.data; 107 | } 108 | } else { 109 | if (event.data.keys.keyCode == 13 && event.data.keys.press) { 110 | triggerKeyEvent(element, 'keydown', event.data.keys); 111 | triggerKeyEvent(element, 'keypress', event.data.keys); 112 | triggerKeyEvent(element, 'keyup', event.data.keys); 113 | } else { 114 | triggerKeyEvent(element, 'keydown', event.data.keys); 115 | triggerKeyEvent(element, 'keyup', event.data.keys); 116 | } 117 | } 118 | } 119 | }); 120 | 121 | // for webdriver to save to global 122 | if (typeof window.sideex !== 'undefined') { 123 | window.sendKeysAttribute = sendKeysAttribute; 124 | window.triggerKeyEvent = triggerKeyEvent; 125 | window.triggerInputEvent = triggerInputEvent; 126 | window.findElement = findElement; 127 | } 128 | })(); 129 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | /* Basic Options */ 4 | "target": "esnext", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019' or 'ESNEXT'. */ 5 | "module": "es6", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */ 6 | // "lib": [], /* Specify library files to be included in the compilation. */ 7 | // "allowJs": true, /* Allow javascript files to be compiled. */ 8 | // "checkJs": true, /* Report errors in .js files. */ 9 | // "jsx": "react", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */ 10 | // "declaration": true, /* Generates corresponding '.d.ts' file. */ 11 | // "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */ 12 | "sourceMap": false, /* Generates corresponding '.map' file. */ 13 | // "outFile": "./", /* Concatenate and emit output to single file. */ 14 | // "outDir": "./", /* Redirect output structure to the directory. */ 15 | // "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */ 16 | // "composite": true, /* Enable project compilation */ 17 | // "incremental": true, /* Enable incremental compilation */ 18 | // "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */ 19 | // "removeComments": true, /* Do not emit comments to output. */ 20 | "noEmit": true, /* Do not emit outputs. */ 21 | // "importHelpers": true, /* Import emit helpers from 'tslib'. */ 22 | "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */ 23 | "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */ 24 | 25 | /* Strict Type-Checking Options */ 26 | // "strict": true, /* Enable all strict type-checking options. */ 27 | "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */ 28 | // "strictNullChecks": true, /* Enable strict null checks. */ 29 | "strictFunctionTypes": true, /* Enable strict checking of function types. */ 30 | "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */ 31 | // "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */ 32 | "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */ 33 | // "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */ 34 | 35 | /* Additional Checks */ 36 | "noUnusedLocals": true, /* Report errors on unused locals. */ 37 | "noUnusedParameters": true, /* Report errors on unused parameters. */ 38 | "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ 39 | "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ 40 | 41 | /* Module Resolution Options */ 42 | "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */ 43 | // "baseUrl": "./", /* Base directory to resolve non-absolute module names. */ 44 | // "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */ 45 | // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */ 46 | // "typeRoots": [], /* List of folders to include type definitions from. */ 47 | // "types": [], /* Type declaration files to be included in compilation. */ 48 | // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ 49 | "esModuleInterop": true, /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */ 50 | // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */ 51 | 52 | /* Source Map Options */ 53 | // "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */ 54 | // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ 55 | // "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */ 56 | // "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */ 57 | 58 | /* Experimental Options */ 59 | "experimentalDecorators": true /* Enables experimental support for ES7 decorators. */ 60 | // "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */ 61 | }, 62 | "include": ["./src"], 63 | "exclude": ["**/build"] 64 | } 65 | -------------------------------------------------------------------------------- /src/panel/js/background/variable-controller.js: -------------------------------------------------------------------------------- 1 | export class VariableController { 2 | constructor(root) { 3 | this.root = root; 4 | 5 | this.keyBoard = ["KEY_ENTER", "KEY_DOWN", "KEY_UP", "KEY_LEFT", "KEY_RIGHT"]; 6 | this.globalVars = { 7 | count: 0, 8 | startNum: 0, 9 | varNames: {}, 10 | vars: {} 11 | }; 12 | this.localVars = { 13 | count: 0, 14 | startNum: 0, 15 | varNames: {}, 16 | vars: {} 17 | }; 18 | } 19 | 20 | initLocalVars() { 21 | this.localVars = Object.assign({}, this.globalVars); 22 | } 23 | 24 | makeCSVText() { 25 | let text = ""; 26 | for (let key in this.globalVars.vars) { 27 | let varObj = this.globalVars.vars[key]; 28 | text += `${varObj.name}, ${varObj.value},\n`; 29 | } 30 | 31 | return text; 32 | } 33 | 34 | makeJSONText() { 35 | let temp = {}; 36 | for (let varObjKey in this.globalVars.vars) { 37 | let varObj = this.globalVars.vars[varObjKey]; 38 | temp[varObj.name] = varObj.value; 39 | } 40 | return JSON.stringify(temp, null, " "); 41 | } 42 | 43 | isVarExisted(name) { 44 | if (this.globalVars.varNames[name]) { 45 | return true; 46 | } 47 | return false; 48 | } 49 | 50 | getVarNum() { 51 | return this.globalVars.count; 52 | } 53 | 54 | addVariable(name = "", value = "") { 55 | // console.log(this.root); 56 | this.globalVars.count++; 57 | let index = this.globalVars.startNum++; 58 | let varIdText = `var-${index}`; 59 | if (name !== "") this.globalVars.varNames[name] = true; 60 | this.globalVars[name] = value; 61 | this.globalVars.vars[varIdText] = { 62 | index: index, 63 | name: name, 64 | value: value 65 | }; 66 | return varIdText; 67 | } 68 | 69 | updateVarName(varIdText, name) { 70 | if (this.isVarExisted(name)) { 71 | this.root.log.pushLog("error", "The variable is existed"); 72 | } 73 | let lastName = this.globalVars.vars[varIdText].name; 74 | delete this.globalVars.varNames[lastName]; 75 | this.globalVars.varNames[name] = true; 76 | this.globalVars.vars[varIdText].name = name; 77 | } 78 | 79 | updateVarValue(varIdText, value) { 80 | this.globalVars.vars[varIdText].value = value; 81 | } 82 | 83 | deleteVariable(varIdText) { 84 | this.globalVars.count--; 85 | let name = this.globalVars.vars[varIdText].name; 86 | if (name !== "") { 87 | delete this.globalVars.varNames[name]; 88 | } 89 | delete this.globalVars.vars[varIdText]; 90 | } 91 | 92 | clearVariables() { 93 | this.globalVars = { 94 | count: 0, 95 | startNum: 0, 96 | varNames: {}, 97 | vars: {} 98 | }; 99 | } 100 | 101 | clearLocalVars() { 102 | this.localVars = {}; 103 | } 104 | 105 | /** 106 | * Read file of variable 107 | * @param {FILE Object} file - input for reading file. 108 | * @param {String} type - input file type, e.g. "csv", "json" 109 | */ 110 | readImportFile(file, type) { 111 | var reader = new FileReader(); 112 | reader.onerror = function(error) { 113 | console.log(error); 114 | }; 115 | 116 | reader.onload = function(event) { 117 | let variables; 118 | if (type === "csv") { 119 | variables = this.csvParser(event.target.result); 120 | } else if (type === "json") { 121 | variables = this.jsonParser(event.target.result); 122 | } else { 123 | this.root.log.pushLog("error", "Error on file type"); 124 | return; 125 | } 126 | 127 | for (let variable of variables) { 128 | if (this.globalVars.varNames[variable[0]]) { 129 | this.root.log.pushLog("warn", "Duplicated variables"); 130 | } else { 131 | this.addVariable(variable[0], variable[1]); 132 | } 133 | } 134 | }; 135 | 136 | reader.readAsText(file); 137 | } 138 | 139 | exportVariables(type) { 140 | let text = ""; 141 | switch (type) { 142 | case "json": 143 | text = this.makeJSONText(); 144 | break; 145 | case "csv": 146 | text = this.makeCSVText(); 147 | break; 148 | } 149 | let downloading = browser.downloads.download({ 150 | filename: `Global Variables.${type}`, 151 | url: this.root.fileController.saveFile.makeTextFile(text), 152 | saveAs: true, 153 | conflictAction: 'overwrite' 154 | }); 155 | } 156 | 157 | csvParser(str) { 158 | let variables = []; 159 | str = str.replace(/\r/g, ""); 160 | str = str.replace(/,\n/g, ","); 161 | str = str.replace(/\n/g, ","); 162 | let temp = str.split(","); 163 | for (let i = 0; i < temp.length; i += 2) { 164 | if (temp[i] !== "" && temp[i + 1] !== "") { 165 | variables.push([temp[i], temp[i + 1]]); 166 | } 167 | } 168 | return variables; 169 | } 170 | 171 | jsonParser(str) { 172 | let variables = []; 173 | let temp = JSON.parse(str); 174 | for (let key in temp) { 175 | variables.push([key, typeof temp[key] === "object" ? 176 | JSON.stringify(temp[key]) : temp[key]]); 177 | } 178 | return variables; 179 | } 180 | 181 | isKeyBoardVars(str) { 182 | for (let i = 0; i < this.keyBoard.length; i++) { 183 | if (str === `\${${this.keyBoard[i]}}`) { 184 | return true; 185 | } 186 | } 187 | return false; 188 | } 189 | } 190 | -------------------------------------------------------------------------------- /src/panel/js/background/preprocessor.js: -------------------------------------------------------------------------------- 1 | export class Preprocessor { 2 | constructor(root) { 3 | this.root = root; 4 | 5 | this.conditionStack = []; 6 | this.result = { 7 | isPassed: false, 8 | message: "" 9 | }; 10 | } 11 | 12 | preprocess(playMode, testSuite) { 13 | let isPreprocessPassed = true; 14 | for (let suiteIdText of testSuite.order) { 15 | let suiteTitle = this.root.fileController.getSuiteTitle(suiteIdText); 16 | this.root.log.pushLog("info", `Checking test suite ${suiteTitle}`); 17 | 18 | for (let caseIdText of testSuite.suites[suiteIdText].cases) { 19 | let caseTitle = this.root.fileController.getCaseTitle(caseIdText); 20 | this.root.log.pushLog("info", `Checking test case ${caseTitle}`); 21 | 22 | let result = this.checkSyntax(caseIdText); 23 | if (!result.isPassed) { 24 | isPreprocessPassed = false; 25 | this.root.log.pushLog("error", `${ 26 | result.message} in (suite) ${suiteTitle} - (case) ${caseTitle}`); 27 | } 28 | } 29 | } 30 | this.root.log.pushLog("info", "Preprocessing Done"); 31 | 32 | let suiteResult = this.preparePlaySuites(testSuite); 33 | 34 | return { isPassed: isPreprocessPassed, ...suiteResult }; 35 | } 36 | 37 | preparePlaySuites(testSuite) { 38 | let playSuites = []; 39 | let caseNum = 0; 40 | for (let suiteIdText of testSuite.order) { 41 | let suiteTitle = this.root.fileController.getSuiteTitle(suiteIdText); 42 | 43 | let suite = { idText: suiteIdText, title: suiteTitle, cases: [] }; 44 | playSuites.push(suite); 45 | 46 | for (let caseIdText of testSuite.suites[suiteIdText].cases) { 47 | let caseTitle = this.root.fileController.getCaseTitle(caseIdText); 48 | suite.cases.push({ idText: caseIdText, title: caseTitle, 49 | records: this.preparePlayRecords( 50 | this.root.fileController.getRecords(caseIdText) 51 | ) 52 | }); 53 | caseNum++; 54 | } 55 | } 56 | return { caseNum: caseNum, playSuites: playSuites }; 57 | } 58 | 59 | preparePlayRecords(records) { 60 | let parent = undefined; 61 | let current = []; 62 | for (let record of records) { 63 | let name = record.name; 64 | if (name === "WHILE" || name === "IF") { 65 | let target = record.target.options[record.target.usedIndex].value, 66 | value = record.value.options[record.value.usedIndex].value; 67 | let temp = Object.assign(record, { 68 | expression: target, 69 | limitTimes: name === "IF" ? 1 : 70 | parseInt(value) > 0 ? parseInt(value) : Infinity, 71 | parent: parent, 72 | children: [[], []] 73 | }); 74 | current.push(temp); 75 | parent = current; 76 | current = temp.children[0]; 77 | } else if (name === "ELSE") { 78 | current = parent[parent.length - 1].children[1]; 79 | current.push(record); 80 | } else if (name === "END") { 81 | current = parent; 82 | parent = current[current.length - 1].parent; 83 | record.parent = parent; 84 | current.push(record); 85 | } else if (name === "INCLUDE") { 86 | let temp = Object.assign(record, { 87 | include: record.target.options[record.target.usedIndex].value, 88 | children: [this.preparePlayRecords(record.children[0])], 89 | parent: parent 90 | }); 91 | current.push(temp); 92 | } else { 93 | record.parent = parent; 94 | current.push(record); 95 | } 96 | } 97 | return current; 98 | } 99 | 100 | checkSyntax(caseIdText) { 101 | let stack = []; 102 | for (let record of this.root.fileController.getRecords(caseIdText)) { 103 | let command = record.name; 104 | if (command === "INCLUDE") { 105 | let caseIdText = this.preprocessIncludeRecords(record); 106 | let result = this.checkSyntax(caseIdText); 107 | if (!result.isPassed) { 108 | return { ...result, message: `${result.message} on INCLUDE command` }; 109 | } 110 | } else if (command === "WHILE" || command === "IF") { 111 | this.pushStack(stack, command); 112 | } else if (command === "ELSE") { 113 | this.pushStack(stack, command); 114 | } else if (command === "END") { 115 | if (stack.length === 0) { 116 | return { isPassed: false, message: "Missing IF/WHILE" }; 117 | } 118 | this.popStack(stack); 119 | } 120 | } 121 | 122 | if (stack.length !== 0) { 123 | return { isPassed: false, message: "Missing END" }; 124 | } else { 125 | return { isPassed: true, message: "Syntax Checked" }; 126 | } 127 | } 128 | 129 | preprocessIncludeRecords(record) { 130 | let caseIdText = this.root.fileController.getIncludeCaseIdText( 131 | record.target.options[record.target.usedIndex].value 132 | ); 133 | record.caseIdText = caseIdText; 134 | record.children = [this.root.fileController.getRecords(caseIdText)]; 135 | return caseIdText; 136 | } 137 | 138 | pushStack(stack, command) { 139 | stack.push({ name: command }); 140 | } 141 | 142 | popStack(stack) { 143 | let lastIndex = stack.length - 1; 144 | let popCondition = stack[lastIndex]; 145 | stack.splice(lastIndex, 1); 146 | 147 | if (popCondition.name == "ELSE") { 148 | if (stack[lastIndex - 1] != null && stack[lastIndex - 1].name == "IF") { 149 | this.popStack(stack); 150 | } else { 151 | return { isPassed: false, message: "Missing IF" }; 152 | } 153 | } 154 | } 155 | } 156 | -------------------------------------------------------------------------------- /custom-template/README.md: -------------------------------------------------------------------------------- 1 | # Docdash 2 | [![Build Status](https://api.travis-ci.org/clenemt/docdash.png?branch=master)](https://travis-ci.org/clenemt/docdash) [![npm version](https://badge.fury.io/js/docdash.svg)](https://badge.fury.io/js/docdash) [![license](https://img.shields.io/npm/l/docdash.svg)](LICENSE.md) 3 | 4 | A clean, responsive documentation template theme for JSDoc 3. 5 | 6 | ![docdash-screenshot](https://cloud.githubusercontent.com/assets/447956/13398144/4dde7f36-defd-11e5-8909-1a9013302cb9.png) 7 | 8 | ![docdash-screenshot-2](https://cloud.githubusercontent.com/assets/447956/13401057/e30effd8-df0a-11e5-9f51-66257ac38e94.jpg) 9 | 10 | ## Example 11 | See http://clenemt.github.io/docdash/ for a sample demo. :rocket: 12 | 13 | ## Install 14 | 15 | ```bash 16 | $ npm install docdash 17 | ``` 18 | 19 | ## Usage 20 | Clone repository to your designated `jsdoc` template directory, then: 21 | 22 | ```bash 23 | $ jsdoc entry-file.js -t path/to/docdash 24 | ``` 25 | 26 | ## Usage (npm) 27 | In your projects `package.json` file add a new script: 28 | 29 | ```json 30 | "script": { 31 | "generate-docs": "node_modules/.bin/jsdoc -c jsdoc.json" 32 | } 33 | ``` 34 | 35 | In your `jsdoc.json` file, add a template option. 36 | 37 | ```json 38 | "opts": { 39 | "template": "node_modules/docdash" 40 | } 41 | ``` 42 | 43 | ## Sample `jsdoc.json` 44 | See the config file for the [fixtures](fixtures/fixtures.conf.json) or the sample below. 45 | 46 | ```json 47 | { 48 | "tags": { 49 | "allowUnknownTags": false 50 | }, 51 | "source": { 52 | "include": "../js", 53 | "includePattern": ".js$", 54 | "excludePattern": "(node_modules/|docs)" 55 | }, 56 | "plugins": [ 57 | "plugins/markdown" 58 | ], 59 | "opts": { 60 | "template": "assets/template/docdash/", 61 | "encoding": "utf8", 62 | "destination": "docs/", 63 | "recurse": true, 64 | "verbose": true 65 | }, 66 | "templates": { 67 | "cleverLinks": false, 68 | "monospaceLinks": false 69 | } 70 | } 71 | ``` 72 | 73 | ## Options 74 | Docdash supports the following options: 75 | 76 | ```json5 77 | { 78 | "docdash": { 79 | "static": [false|true], // Display the static members inside the navbar 80 | "sort": [false|true], // Sort the methods in the navbar 81 | "sectionOrder": [ // Order the main section in the navbar (default order shown here) 82 | "Classes", 83 | "Modules", 84 | "Externals", 85 | "Events", 86 | "Namespaces", 87 | "Mixins", 88 | "Tutorials", 89 | "Interfaces" 90 | ], 91 | "disqus": "", // Shortname for your disqus (subdomain during site creation) 92 | "openGraph": { // Open Graph options (mostly for Facebook and other sites to easily extract meta information) 93 | "title": "", // Title of the website 94 | "type": "website", // Type of the website 95 | "image": "", // Main image/logo 96 | "site_name": "", // Site name 97 | "url": "" // Main canonical URL for the main page of the site 98 | }, 99 | "meta": { // Meta information options (mostly for search engines that have not indexed your site yet) 100 | "title": "", // Also will be used as postfix to actualy page title, prefixed with object/document name 101 | "description": "", // Description of overal contents of your website 102 | "keyword": "" // Keywords for search engines 103 | }, 104 | "search": [false|true], // Display seach box above navigation which allows to search/filter navigation items 105 | "collapse": [false|true], // Collapse navigation by default except current object's navigation of the current page 106 | "wrap": [false|true], // Wrap long navigation names instead of trimming them 107 | "typedefs": [false|true], // Include typedefs in menu 108 | "navLevel": [integer], // depth level to show in navbar, starting at 0 (false or -1 to disable) 109 | "private": [false|true], // set to false to not show @private in navbar 110 | "removeQuotes": [none|all|trim],// Remove single and double quotes, trim removes only surrounding ones 111 | "scripts": [], // Array of external (or relative local copied using templates.default.staticFiles.include) js or css files to inject into HTML, 112 | "menu": { // Adding additional menu items after Home 113 | "Project Website": { // Menu item name 114 | "href":"https://myproject.com", //the rest of HTML properties to add to manu item 115 | "target":"_blank", 116 | "class":"menu-item", 117 | "id":"website_link" 118 | }, 119 | "Forum": { 120 | "href":"https://myproject.com.forum", 121 | "target":"_blank", 122 | "class":"menu-item", 123 | "id":"forum_link" 124 | } 125 | } 126 | } 127 | } 128 | ``` 129 | 130 | Place them anywhere inside your `jsdoc.json` file. 131 | 132 | ## Contributors 133 | 134 | [![0](https://sourcerer.io/fame/ar2rsawseen/clenemt/docdash/images/0)](https://sourcerer.io/fame/ar2rsawseen/clenemt/docdash/links/0) 135 | [![1](https://sourcerer.io/fame/ar2rsawseen/clenemt/docdash/images/1)](https://sourcerer.io/fame/ar2rsawseen/clenemt/docdash/links/1) 136 | [![2](https://sourcerer.io/fame/ar2rsawseen/clenemt/docdash/images/2)](https://sourcerer.io/fame/ar2rsawseen/clenemt/docdash/links/2) 137 | [![3](https://sourcerer.io/fame/ar2rsawseen/clenemt/docdash/images/3)](https://sourcerer.io/fame/ar2rsawseen/clenemt/docdash/links/3) 138 | [![4](https://sourcerer.io/fame/ar2rsawseen/clenemt/docdash/images/4)](https://sourcerer.io/fame/ar2rsawseen/clenemt/docdash/links/4) 139 | [![5](https://sourcerer.io/fame/ar2rsawseen/clenemt/docdash/images/5)](https://sourcerer.io/fame/ar2rsawseen/clenemt/docdash/links/5) 140 | [![6](https://sourcerer.io/fame/ar2rsawseen/clenemt/docdash/images/6)](https://sourcerer.io/fame/ar2rsawseen/clenemt/docdash/links/6) 141 | [![7](https://sourcerer.io/fame/ar2rsawseen/clenemt/docdash/images/7)](https://sourcerer.io/fame/ar2rsawseen/clenemt/docdash/links/7) 142 | 143 | ## Thanks 144 | Thanks to [lodash](https://lodash.com) and [minami](https://github.com/nijikokun/minami). 145 | 146 | ## License 147 | Licensed under the Apache License, version 2.0. (see [Apache-2.0](LICENSE.md)). 148 | -------------------------------------------------------------------------------- /src/page/prompt.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright SideeX committers 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | (function () { 19 | let originalPrompt = window.prompt; 20 | 21 | let nextPromptResult = false; 22 | let recordedPrompt = null; 23 | 24 | let originalConfirmation = window.confirm; 25 | let nextConfirmationResult = false; 26 | let recordedConfirmation = null; 27 | 28 | let originalAlert = window.alert; 29 | let recordedAlert = null; 30 | 31 | function postMessageToContentEnv(data = {}) { 32 | data.from = "sideex-page-env"; 33 | window.postMessage(data, "*"); 34 | } 35 | 36 | window.prompt = function (text, defaultText) { 37 | if (document.documentElement.hasAttribute("setPrompt")) { 38 | recordedPrompt = text; 39 | document.documentElement.removeAttribute("setPrompt"); 40 | return nextPromptResult; 41 | } else { 42 | let result = originalPrompt(text, defaultText); 43 | postMessageToContentEnv({ 44 | type: "record", 45 | args: { 46 | recordedType: "prompt", 47 | recordedMessage: text, 48 | recordedResult: result 49 | } 50 | }); 51 | return result; 52 | } 53 | }; 54 | window.confirm = function (text) { 55 | if (document.documentElement.hasAttribute("setConfirm")) { 56 | recordedConfirmation = text; 57 | document.body.removeAttribute("setConfirm"); 58 | return nextConfirmationResult; 59 | } else { 60 | let result = originalConfirmation(text); 61 | postMessageToContentEnv({ 62 | type: "record", 63 | args: { 64 | recordedType: "confirm", 65 | recordedMessage: text, 66 | recordedResult: result 67 | } 68 | }); 69 | return result; 70 | } 71 | }; 72 | window.alert = function (text) { 73 | if (document.documentElement.hasAttribute("SideeXPlayingFlag")) { 74 | recordedAlert = text; 75 | postMessageToContentEnv({ 76 | type: "response", 77 | args: { 78 | responsedType: "alert", 79 | value: recordedAlert 80 | } 81 | }); 82 | return; 83 | } else { 84 | let result = originalAlert(text); 85 | postMessageToContentEnv({ 86 | type: "record", 87 | args: { 88 | recordedType: "alert", 89 | recordedMessage: text, 90 | recordedResult: result 91 | } 92 | }); 93 | return result; 94 | } 95 | }; 96 | 97 | window.addEventListener("message", function (event) { 98 | if (event.source == window && event.data && event.data.from == "sideex-content-env") { 99 | switch (event.data.type) { 100 | case "command": 101 | switch (event.data.args.commandType) { 102 | case "setNextPromptResult": 103 | nextPromptResult = event.data.args.target; 104 | document.documentElement.setAttribute("setPrompt", true); 105 | postMessageToContentEnv({ 106 | type: "response", 107 | args: { 108 | responsedType: "prompt" 109 | } 110 | }); 111 | break; 112 | case "getPromptMessage": 113 | result = recordedPrompt; 114 | recordedPrompt = null; 115 | postMessageToContentEnv({ 116 | type: "response", 117 | args: { 118 | responsedType: "prompt", 119 | value: result 120 | } 121 | }); 122 | break; 123 | case "setNextConfirmationResult": 124 | nextConfirmationResult = event.data.args.target; 125 | document.documentElement.setAttribute("setConfirm", true); 126 | postMessageToContentEnv({ 127 | type: "response", 128 | args: { 129 | responsedType: "confirm" 130 | } 131 | }); 132 | break; 133 | case "getConfirmationMessage": 134 | result = recordedConfirmation; 135 | recordedConfirmation = null; 136 | postMessageToContentEnv({ 137 | type: "response", 138 | args: { 139 | responsedType: "confirm", 140 | value: result 141 | } 142 | }); 143 | break; 144 | case "setNextAlertResult": 145 | nextAlertResult = event.data.args.target; 146 | document.documentElement.setAttribute("setAlert", true); 147 | postMessageToContentEnv({ 148 | type: "response", 149 | args: { 150 | responsedType: "alert" 151 | } 152 | }); 153 | break; 154 | } 155 | break; 156 | default: 157 | break; 158 | } 159 | } 160 | }); 161 | })(); 162 | -------------------------------------------------------------------------------- /src/utils/windowListeners.ts: -------------------------------------------------------------------------------- 1 | 2 | import { v4 as uuidv4 } from 'uuid'; 3 | import { Utils } from '../utils/index'; 4 | import { TimeoutError } from './timeoutError'; 5 | type MessageListener = (data: MessageData, source?: CrossOriginWindow) => Promise; 6 | declare global { 7 | interface Window { 8 | /** 9 | * @summery Awaitable version of window.postMessage 10 | * @param {string} eventName only listener register with same `eventName` from `addAsyncMessageListener` will received. 11 | * @returns `Promise` `T` is the listener's return type from `addAsyncMessageListener` 12 | */ 13 | postMessageAsync(eventName: string, data: any, options?: Partial): Promise 14 | addAsyncMessageListener(eventName: string, callback: MessageListener): void 15 | removeAsyncMessageListener(eventName: string, callback: MessageListener): void 16 | } 17 | } 18 | const messageListeners: Record[]> = {}; 19 | async function init() { 20 | if (document.readyState == "loading") { 21 | await new Promise((resolve) => { 22 | document.addEventListener("DOMContentLoaded", resolve, { once: true }); 23 | }); 24 | } 25 | window.addEventListener("message", async (event: WindowMessageEvent) => { 26 | if (event.data && event.data._details && !event.data._details.callbackId && event.data._details.eventName in messageListeners) { 27 | const promises: Promise[] = []; 28 | for (const listerner of messageListeners[event.data._details.eventName]) { 29 | promises.push(listerner(event.data, event.source as WindowProxy)); 30 | } 31 | let results: any[] = []; 32 | const returnObj: MessageData = { 33 | _details: { 34 | id: uuidv4(), 35 | callbackId: event.data._details.id, 36 | eventName: event.data._details.eventName 37 | } 38 | }; 39 | try { 40 | results = (await Promise.all(promises)).filter(value => value); 41 | returnObj._details.returnValue = results[0]; 42 | } catch (error) { 43 | if (error instanceof Error) { 44 | returnObj._details.error = error.message; 45 | } else if (error instanceof String) { 46 | returnObj._details.error = error.toString(); 47 | } else { 48 | returnObj._details.error = JSON.stringify(error); 49 | } 50 | } 51 | if (Utils.is(event.source, event?.source?.postMessage instanceof Function)) { 52 | event.source.postMessage(returnObj, "*"); 53 | } else { 54 | window.postMessage.call(event.source, returnObj, "*"); 55 | } 56 | if (results.length > 1) { 57 | throw new Error(`More than one onMessage listener send response, eventName=${event.data._details.eventName}`); 58 | } 59 | } 60 | }); 61 | } 62 | init(); 63 | interface WindowMessageEvent extends MessageEvent { 64 | data: MessageData 65 | } 66 | export type MessageData = T & { 67 | /** @private For listeners mechanism ,do not put data here. */ 68 | _details?: MessageDetails 69 | } 70 | interface MessageDetails { 71 | id: string 72 | eventName: string 73 | callbackId?: string 74 | returnValue?: any 75 | error?: string 76 | } 77 | interface MessagePostOptions { 78 | targetOrigin: string, 79 | timeout: number 80 | } 81 | Reflect.defineProperty(window, "postMessageAsync", { 82 | configurable: true, 83 | writable: true, 84 | async value(this: Window, eventName: string, data: MessageData, options?: Partial): Promise { 85 | return await postMessageCrossOrigin(this, eventName, data, options); 86 | } 87 | }); 88 | 89 | Reflect.defineProperty(window, "addAsyncMessageListener", { 90 | configurable: true, 91 | writable: true, 92 | value(eventName: string, callback: MessageListener) { 93 | if (messageListeners[eventName] == undefined) { 94 | messageListeners[eventName] = []; 95 | } 96 | if (!messageListeners[eventName].includes(callback)) { 97 | messageListeners[eventName].push(callback); 98 | } 99 | } 100 | }); 101 | 102 | Reflect.defineProperty(window, "removeAsyncMessageListener", { 103 | configurable: true, 104 | writable: true, 105 | value(eventName: string, callback: MessageListener) { 106 | if (messageListeners[eventName] && messageListeners[eventName].includes(callback)) { 107 | messageListeners[eventName].remove(callback); 108 | if (messageListeners[eventName].length == 0) { 109 | messageListeners[eventName] = undefined; 110 | } 111 | } 112 | } 113 | }); 114 | 115 | 116 | type CrossOriginWindow = Pick 117 | 118 | /** 119 | * @description async `postMessage` with target window assignment function 120 | */ 121 | export async function postMessageCrossOrigin(targetWindow: CrossOriginWindow, eventName: string, data: MessageData, options?: Partial): Promise { 122 | const _options = Object.assign({ targetOrigin: "*", timeout: 0 } as MessagePostOptions, options); 123 | const id = uuidv4(); 124 | data._details = { 125 | id: id, 126 | eventName: eventName 127 | }; 128 | const sendMessage = async () => { 129 | return await new Promise((resolve, reject) => { 130 | const callback = (event: WindowMessageEvent) => { 131 | if (event.data?._details?.callbackId == id) { 132 | window.removeEventListener("message", callback); 133 | if (event.data._details.error) { 134 | reject(event.data._details.error); 135 | } else { 136 | resolve(event.data._details.returnValue); 137 | } 138 | } 139 | }; 140 | window.addEventListener("message", callback); 141 | if (targetWindow?.postMessage instanceof Function) { 142 | targetWindow.postMessage(data, _options.targetOrigin); 143 | } else { 144 | window.postMessage.call(targetWindow, data, _options.targetOrigin); 145 | } 146 | }); 147 | }; 148 | if (document.readyState == "loading") { 149 | await new Promise((resolve) => { 150 | document.addEventListener("DOMContentLoaded", resolve, { once: true }); 151 | }); 152 | } 153 | return _options.timeout > 0 ? await Utils.promiseTimeout(sendMessage, _options.timeout, new TimeoutError(`postMessageAsync timeout, eventName=${eventName}`), true) : await sendMessage(); 154 | } 155 | // #!endif 156 | -------------------------------------------------------------------------------- /custom-template/tmpl/container.tmpl: -------------------------------------------------------------------------------- 1 | 7 | 8 | 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 | 43 |
44 | 45 |
46 | 47 | 48 |
49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 |
61 | 62 | 63 | 64 |

Example 1? 's':'' ?>

65 | 66 | 67 | 68 |
69 | 70 | 71 | 72 |

Extends

73 | 74 | 75 | 76 | 77 | 78 |

Requires

79 | 80 |
    81 |
  • 82 |
83 | 84 | 85 | 89 |

Classes

90 | 91 |
92 |
93 |
94 |
95 | 96 | 97 | 101 |

Interfaces

102 | 103 |
104 |
105 |
106 |
107 | 108 | 109 | 113 |

Mixins

114 | 115 |
116 |
117 |
118 |
119 | 120 | 121 | 125 |

Namespaces

126 | 127 |
128 |
129 |
130 |
131 | 132 | 133 | 144 |

Members

145 | 146 | 147 | 148 | 149 | 150 | 151 | 155 |

Methods

156 | 157 | 158 | 159 | 160 | 161 | 162 | 166 |

Type Definitions

167 | 168 | 171 | 172 | 176 | 177 | 180 | 181 | 182 | 186 |

Events

187 | 188 | 189 | 190 | 191 | 192 |
193 | 194 |
195 | 196 | 197 | 198 | -------------------------------------------------------------------------------- /docs/tutorial-tutorial.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Tutorial: tutorial - Documentation 7 | 8 | 9 | 10 | 11 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 25 | 26 | 27 | 28 | 32 | 33 |
34 | 35 |

Tutorial: tutorial

36 | 37 | 38 |
39 | 40 |
41 | 42 | 43 |

tutorial

44 |
45 | 46 |
47 | 52 |

SideeX-API

53 |

SideeX JavaScript API is a JS library running on a webpage for recording and playing web browsing behavior. As opposed to acting as a browser web extension, the API can be directly embedded and used within a webpage via JavaScript.

54 |

Installation

55 |
npm i @sideex/api
56 | 
57 |

Import

58 |
var {SideeX} = require('@sideex/api')
59 | var sideex = new SideeX();
60 | 
61 |
import {SideeX} from "@sideex/api"
62 | var sideex = new SideeX();
63 | 
64 |
65 | 66 |
67 | 68 | 69 | 70 |
71 | 72 |
73 | 74 |
75 | Documentation generated by JSDoc 3.6.4 on Tue May 26 2020 11:45:58 GMT+0800 (GMT+08:00) using the docdash theme. 76 |
77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | -------------------------------------------------------------------------------- /docs/styles/jsdoc-default.css: -------------------------------------------------------------------------------- 1 | @font-face { 2 | font-family: 'Open Sans'; 3 | font-weight: normal; 4 | font-style: normal; 5 | src: url('../fonts/OpenSans-Regular-webfont.eot'); 6 | src: 7 | local('Open Sans'), 8 | local('OpenSans'), 9 | url('../fonts/OpenSans-Regular-webfont.eot?#iefix') format('embedded-opentype'), 10 | url('../fonts/OpenSans-Regular-webfont.woff') format('woff'), 11 | url('../fonts/OpenSans-Regular-webfont.svg#open_sansregular') format('svg'); 12 | } 13 | 14 | @font-face { 15 | font-family: 'Open Sans Light'; 16 | font-weight: normal; 17 | font-style: normal; 18 | src: url('../fonts/OpenSans-Light-webfont.eot'); 19 | src: 20 | local('Open Sans Light'), 21 | local('OpenSans Light'), 22 | url('../fonts/OpenSans-Light-webfont.eot?#iefix') format('embedded-opentype'), 23 | url('../fonts/OpenSans-Light-webfont.woff') format('woff'), 24 | url('../fonts/OpenSans-Light-webfont.svg#open_sanslight') format('svg'); 25 | } 26 | 27 | html 28 | { 29 | overflow: auto; 30 | background-color: #fff; 31 | font-size: 14px; 32 | } 33 | 34 | body 35 | { 36 | font-family: 'Open Sans', sans-serif; 37 | line-height: 1.5; 38 | color: #4d4e53; 39 | background-color: white; 40 | } 41 | 42 | a, a:visited, a:active { 43 | color: #0095dd; 44 | text-decoration: none; 45 | } 46 | 47 | a:hover { 48 | text-decoration: underline; 49 | } 50 | 51 | header 52 | { 53 | display: block; 54 | padding: 0px 4px; 55 | } 56 | 57 | tt, code, kbd, samp { 58 | font-family: Consolas, Monaco, 'Andale Mono', monospace; 59 | } 60 | 61 | .class-description { 62 | font-size: 130%; 63 | line-height: 140%; 64 | margin-bottom: 1em; 65 | margin-top: 1em; 66 | } 67 | 68 | .class-description:empty { 69 | margin: 0; 70 | } 71 | 72 | #main { 73 | float: left; 74 | width: 70%; 75 | } 76 | 77 | article dl { 78 | margin-bottom: 40px; 79 | } 80 | 81 | article img { 82 | max-width: 100%; 83 | } 84 | 85 | section 86 | { 87 | display: block; 88 | background-color: #fff; 89 | padding: 12px 24px; 90 | border-bottom: 1px solid #ccc; 91 | margin-right: 30px; 92 | } 93 | 94 | .variation { 95 | display: none; 96 | } 97 | 98 | .signature-attributes { 99 | font-size: 60%; 100 | color: #aaa; 101 | font-style: italic; 102 | font-weight: lighter; 103 | } 104 | 105 | nav 106 | { 107 | display: block; 108 | float: right; 109 | margin-top: 28px; 110 | width: 30%; 111 | box-sizing: border-box; 112 | border-left: 1px solid #ccc; 113 | padding-left: 16px; 114 | } 115 | 116 | nav ul { 117 | font-family: 'Lucida Grande', 'Lucida Sans Unicode', arial, sans-serif; 118 | font-size: 100%; 119 | line-height: 17px; 120 | padding: 0; 121 | margin: 0; 122 | list-style-type: none; 123 | } 124 | 125 | nav ul a, nav ul a:visited, nav ul a:active { 126 | font-family: Consolas, Monaco, 'Andale Mono', monospace; 127 | line-height: 18px; 128 | color: #4D4E53; 129 | } 130 | 131 | nav h3 { 132 | margin-top: 12px; 133 | } 134 | 135 | nav li { 136 | margin-top: 6px; 137 | } 138 | 139 | footer { 140 | display: block; 141 | padding: 6px; 142 | margin-top: 12px; 143 | font-style: italic; 144 | font-size: 90%; 145 | } 146 | 147 | h1, h2, h3, h4 { 148 | font-weight: 200; 149 | margin: 0; 150 | } 151 | 152 | h1 153 | { 154 | font-family: 'Open Sans Light', sans-serif; 155 | font-size: 48px; 156 | letter-spacing: -2px; 157 | margin: 12px 24px 20px; 158 | } 159 | 160 | h2, h3.subsection-title 161 | { 162 | font-size: 30px; 163 | font-weight: 700; 164 | letter-spacing: -1px; 165 | margin-bottom: 12px; 166 | } 167 | 168 | h3 169 | { 170 | font-size: 24px; 171 | letter-spacing: -0.5px; 172 | margin-bottom: 12px; 173 | } 174 | 175 | h4 176 | { 177 | font-size: 18px; 178 | letter-spacing: -0.33px; 179 | margin-bottom: 12px; 180 | color: #4d4e53; 181 | } 182 | 183 | h5, .container-overview .subsection-title 184 | { 185 | font-size: 120%; 186 | font-weight: bold; 187 | letter-spacing: -0.01em; 188 | margin: 8px 0 3px 0; 189 | } 190 | 191 | h6 192 | { 193 | font-size: 100%; 194 | letter-spacing: -0.01em; 195 | margin: 6px 0 3px 0; 196 | font-style: italic; 197 | } 198 | 199 | table 200 | { 201 | border-spacing: 0; 202 | border: 0; 203 | border-collapse: collapse; 204 | } 205 | 206 | td, th 207 | { 208 | border: 1px solid #ddd; 209 | margin: 0px; 210 | text-align: left; 211 | vertical-align: top; 212 | padding: 4px 6px; 213 | display: table-cell; 214 | } 215 | 216 | thead tr 217 | { 218 | background-color: #ddd; 219 | font-weight: bold; 220 | } 221 | 222 | th { border-right: 1px solid #aaa; } 223 | tr > th:last-child { border-right: 1px solid #ddd; } 224 | 225 | .ancestors, .attribs { color: #999; } 226 | .ancestors a, .attribs a 227 | { 228 | color: #999 !important; 229 | text-decoration: none; 230 | } 231 | 232 | .clear 233 | { 234 | clear: both; 235 | } 236 | 237 | .important 238 | { 239 | font-weight: bold; 240 | color: #950B02; 241 | } 242 | 243 | .yes-def { 244 | text-indent: -1000px; 245 | } 246 | 247 | .type-signature { 248 | color: #aaa; 249 | } 250 | 251 | .name, .signature { 252 | font-family: Consolas, Monaco, 'Andale Mono', monospace; 253 | } 254 | 255 | .details { margin-top: 14px; border-left: 2px solid #DDD; } 256 | .details dt { width: 120px; float: left; padding-left: 10px; padding-top: 6px; } 257 | .details dd { margin-left: 70px; } 258 | .details ul { margin: 0; } 259 | .details ul { list-style-type: none; } 260 | .details li { margin-left: 30px; padding-top: 6px; } 261 | .details pre.prettyprint { margin: 0 } 262 | .details .object-value { padding-top: 0; } 263 | 264 | .description { 265 | margin-bottom: 1em; 266 | margin-top: 1em; 267 | } 268 | 269 | .code-caption 270 | { 271 | font-style: italic; 272 | font-size: 107%; 273 | margin: 0; 274 | } 275 | 276 | .source 277 | { 278 | border: 1px solid #ddd; 279 | width: 80%; 280 | overflow: auto; 281 | } 282 | 283 | .prettyprint.source { 284 | width: inherit; 285 | } 286 | 287 | .source code 288 | { 289 | font-size: 100%; 290 | line-height: 18px; 291 | display: block; 292 | padding: 4px 12px; 293 | margin: 0; 294 | background-color: #fff; 295 | color: #4D4E53; 296 | } 297 | 298 | .prettyprint code span.line 299 | { 300 | display: inline-block; 301 | } 302 | 303 | .prettyprint.linenums 304 | { 305 | padding-left: 70px; 306 | -webkit-user-select: none; 307 | -moz-user-select: none; 308 | -ms-user-select: none; 309 | user-select: none; 310 | } 311 | 312 | .prettyprint.linenums ol 313 | { 314 | padding-left: 0; 315 | } 316 | 317 | .prettyprint.linenums li 318 | { 319 | border-left: 3px #ddd solid; 320 | } 321 | 322 | .prettyprint.linenums li.selected, 323 | .prettyprint.linenums li.selected * 324 | { 325 | background-color: lightyellow; 326 | } 327 | 328 | .prettyprint.linenums li * 329 | { 330 | -webkit-user-select: text; 331 | -moz-user-select: text; 332 | -ms-user-select: text; 333 | user-select: text; 334 | } 335 | 336 | .params .name, .props .name, .name code { 337 | color: #4D4E53; 338 | font-family: Consolas, Monaco, 'Andale Mono', monospace; 339 | font-size: 100%; 340 | } 341 | 342 | .params td.description > p:first-child, 343 | .props td.description > p:first-child 344 | { 345 | margin-top: 0; 346 | padding-top: 0; 347 | } 348 | 349 | .params td.description > p:last-child, 350 | .props td.description > p:last-child 351 | { 352 | margin-bottom: 0; 353 | padding-bottom: 0; 354 | } 355 | 356 | .disabled { 357 | color: #454545; 358 | } 359 | -------------------------------------------------------------------------------- /docs/SideeX_file.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | file - Documentation 7 | 8 | 9 | 10 | 11 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 25 | 26 | 27 | 28 | 32 | 33 |
34 | 35 |

file

36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 |
44 | 45 |
46 | 47 |

48 | #SideeX# 49 | 50 | file 51 |

52 | 53 | 54 |
55 | 56 |
57 | 58 |
59 | 60 | 61 | 62 |
63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 |
96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 |
105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 |

Namespaces

119 | 120 |
121 |
command
122 |
123 | 124 |
testCase
125 |
126 | 127 |
testSuite
128 |
129 |
130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 |
140 | 141 |
142 | 143 | 144 | 145 | 146 | 147 | 148 |
149 | 150 |
151 | 152 |
153 | Documentation generated by JSDoc 3.6.4 on Tue May 26 2020 11:45:58 GMT+0800 (GMT+08:00) using the docdash theme. 154 |
155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | --------------------------------------------------------------------------------