mirror of
https://github.com/publiclab/image-sequencer.git
synced 2025-12-07 17:00:02 +01:00
Compare commits
100 Commits
audit
...
daemon1024
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f5a6ceae62 | ||
|
|
a8ae036077 | ||
|
|
417c1b1826 | ||
|
|
5298d1e0ec | ||
|
|
7751024d71 | ||
|
|
3204bafd44 | ||
|
|
3b8181a26a | ||
|
|
66442b9d25 | ||
|
|
5cef13ba9a | ||
|
|
73b49371ab | ||
|
|
f6949c9bbd | ||
|
|
b04a5cbc6c | ||
|
|
48b08c69da | ||
|
|
f31859bd9c | ||
|
|
e34f69c706 | ||
|
|
269703a27b | ||
|
|
71bf8c872b | ||
|
|
0580218c63 | ||
|
|
cc297ee3f7 | ||
|
|
e5039d8daa | ||
|
|
f59b64fb96 | ||
|
|
aa3238e1ba | ||
|
|
3a3abd28bd | ||
|
|
3b857796af | ||
|
|
e08b706497 | ||
|
|
72175fa789 | ||
|
|
97b4f38db3 | ||
|
|
9a281746bf | ||
|
|
25275e4987 | ||
|
|
4609d0a9c6 | ||
|
|
0bdf71a47d | ||
|
|
ed94a0fb39 | ||
|
|
49a21cf4a9 | ||
|
|
eb81b7de4b | ||
|
|
a73cba8d3c | ||
|
|
240fd5356d | ||
|
|
f7ca35af23 | ||
|
|
d340a78aab | ||
|
|
ea39f4ad88 | ||
|
|
911651260a | ||
|
|
0fc3539f38 | ||
|
|
d9779f8bf4 | ||
|
|
bd2187b2a0 | ||
|
|
e5ea6e5978 | ||
|
|
ffdba0b9a6 | ||
|
|
3ead0ee24c | ||
|
|
729759e06a | ||
|
|
04d2eca3c0 | ||
|
|
64e6da3c41 | ||
|
|
ec2aa108c1 | ||
|
|
a3b4044a0b | ||
|
|
a47039a411 | ||
|
|
616932823d | ||
|
|
8730dd8136 | ||
|
|
a1ca60a29d | ||
|
|
0d7ab2bbda | ||
|
|
580a154f33 | ||
|
|
fc799bfa6a | ||
|
|
5a6d5560ff | ||
|
|
cbaa0f0f6f | ||
|
|
ba34f100d0 | ||
|
|
62c3cdb8bd | ||
|
|
2159d03e76 | ||
|
|
f0cb35a0aa | ||
|
|
149e8d2150 | ||
|
|
127d417e40 | ||
|
|
8c57b2393f | ||
|
|
a9bb7c6834 | ||
|
|
a8f8029a83 | ||
|
|
3e91725bda | ||
|
|
b5135c716c | ||
|
|
e2cd7b1950 | ||
|
|
4e9324b81e | ||
|
|
1af80b325a | ||
|
|
77a9f01e33 | ||
|
|
893bc108e0 | ||
|
|
0622a0c21a | ||
|
|
67e11edcd8 | ||
|
|
195a54d355 | ||
|
|
0f1a8f922b | ||
|
|
0fe8c1e7a1 | ||
|
|
e10678e8d8 | ||
|
|
183b75373f | ||
|
|
775378d8fd | ||
|
|
4e3d9f97c0 | ||
|
|
fddf2b6ed8 | ||
|
|
a5e3584ea8 | ||
|
|
a82bdea390 | ||
|
|
099e7e2e1d | ||
|
|
5408467800 | ||
|
|
30c3fba933 | ||
|
|
cfd3750b68 | ||
|
|
fadaebcdb6 | ||
|
|
10f2739435 | ||
|
|
3caa033c8f | ||
|
|
14f13c300b | ||
|
|
a2d654321d | ||
|
|
ad2f63ce4f | ||
|
|
82601ecbea | ||
|
|
c750958add |
1
.github/CODEOWNERS
vendored
1
.github/CODEOWNERS
vendored
@@ -44,6 +44,7 @@
|
||||
/*.lock @ubliclab/is-maintainers
|
||||
/Gruntfile.js @publiclab/is-maintainers
|
||||
/.github/ @publiclab/is-maintainers
|
||||
/scripts/ @publiclab/is-maintainers
|
||||
# <-- /COMMON TO ALL MAINTAINERS -->
|
||||
|
||||
# <-- SPECIFIC MAINTAINERS -->
|
||||
|
||||
@@ -1,3 +1,10 @@
|
||||
---
|
||||
name: Bug report 🐞
|
||||
about: Help us identify and fix a bug!
|
||||
title: ''
|
||||
labels: bug
|
||||
---
|
||||
|
||||
### Please describe the problem (or idea)
|
||||
|
||||
> What happened just before the problem occurred? Or what problem could this idea solve?
|
||||
@@ -10,13 +17,13 @@
|
||||
|
||||
### Please show us where to look
|
||||
|
||||
https://beta.sequencer.publiclab.org
|
||||
Paste in a full URL, starting with:
|
||||
|
||||
> https://beta.sequencer.publiclab.org/
|
||||
|
||||
### What's your PublicLab.org username?
|
||||
|
||||
> This can help us diagnose the issue:
|
||||
If you can share a screenshot or a GIF that is EXTRA helpful! 💖
|
||||
|
||||
If you can see a version number in the upper right, please note that!
|
||||
|
||||
|
||||
### Browser, version, and operating system
|
||||
39
.github/ISSUE_TEMPLATE/release_workflow.md
vendored
Normal file
39
.github/ISSUE_TEMPLATE/release_workflow.md
vendored
Normal file
@@ -0,0 +1,39 @@
|
||||
---
|
||||
name: New release checklist ✅
|
||||
about: Coordinate steps to publish a new release
|
||||
title: 'Checklist and coordination for v0.0.0 major/minor/patch release'
|
||||
labels: release
|
||||
assignees: '@publiclab/is-maintainers'
|
||||
---
|
||||
|
||||
This template guides us through the steps of creating a new release, based on conversation and testing in [#1692](https://github.com/publiclab/image-sequencer/issues/1692).
|
||||
|
||||
Discuss with @publiclab/is-maintainers if anything is ambiguous!
|
||||
|
||||
<!-- NOTE: Change v0.0.0 to the appropriate release version -->
|
||||
|
||||
* [x] open an issue using the "release" template with this checklist with title `Checklist and coordination for v0.0.0 major/minor/patch release` (see [semantic versioning](https://docs.npmjs.com/about-semantic-versioning/))
|
||||
* [ ] create a release [project](https://github.com/publiclab/image-sequencer/projects) from [this template](https://github.com/publiclab/image-sequencer/projects/5). You can copy a project from its menu.
|
||||
* [ ] compile release notes below from corresponding [release project](https://github.com/publiclab/image-sequencer/projects).
|
||||
* [ ] update version number in `examples/sw.js` (ex #1734) and `package.json` (ex #1695)
|
||||
* [ ] finalize and merge to `main` branch (freeze merges to `main` branch until next step)
|
||||
* [ ] merge, build and publish `/dist/` files to `stable` (merges to `main` branch can resume for next release)
|
||||
* [ ] create a release on GitHub and use features description + release notes from below
|
||||
* [ ] tag version number branch (i.e. `v0.0.0`)
|
||||
* [ ] publish tagged branch to `npm`
|
||||
* [ ] publish to live Github pages [demo](https://sequencer.publiclab.org) (with [bash script](https://github.com/publiclab/image-sequencer/pull/1703) from `/scripts/update-demo`) (from within GitPod works well)
|
||||
* [ ] move anything necessary to next release project, i.e. <!-- Update this link -->https://github.com/publiclab/image-sequencer/projects/[insert project number]
|
||||
* [ ] close this issue!
|
||||
|
||||
Noting we're now in this process in https://github.com/publiclab/image-sequencer/pull/1695 for `v3.6.0`.
|
||||
|
||||
****
|
||||
|
||||
### Release notes
|
||||
Compile and edit release notes below, to be copied into the release description.
|
||||
|
||||
#### Added
|
||||
|
||||
#### Fixed
|
||||
|
||||
#### Changed
|
||||
209
.github/workflows/tests.yml
vendored
Normal file
209
.github/workflows/tests.yml
vendored
Normal file
@@ -0,0 +1,209 @@
|
||||
name: tests
|
||||
on: [pull_request]
|
||||
jobs:
|
||||
base-tests:
|
||||
runs-on: ubuntu-18.04
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Setup node
|
||||
uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: '12'
|
||||
check-latest: true
|
||||
- name: Cache node modules
|
||||
uses: actions/cache@v2
|
||||
env:
|
||||
cache-name: cache-node-modules
|
||||
with:
|
||||
path: ~/.npm
|
||||
key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/package-lock.json') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-build-${{ env.cache-name }}-
|
||||
${{ runner.os }}-build-
|
||||
${{ runner.os }}-
|
||||
- name: Install Dependencies
|
||||
run: npm install
|
||||
- name: "Base istanbul/tape node tests"
|
||||
run: npm test
|
||||
|
||||
benchmark-tests:
|
||||
runs-on: ubuntu-18.04
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Setup node
|
||||
uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: '12'
|
||||
check-latest: true
|
||||
- name: Cache node modules
|
||||
uses: actions/cache@v2
|
||||
env:
|
||||
cache-name: cache-node-modules
|
||||
with:
|
||||
path: ~/.npm
|
||||
key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/package-lock.json') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-build-${{ env.cache-name }}-
|
||||
${{ runner.os }}-build-
|
||||
${{ runner.os }}-
|
||||
- name: Install Dependencies
|
||||
run: npm install
|
||||
- name: "Benchmark tests"
|
||||
run: npm run benchmark
|
||||
|
||||
gif-tests:
|
||||
runs-on: ubuntu-18.04
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Setup node
|
||||
uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: '12'
|
||||
check-latest: true
|
||||
- name: Cache node modules
|
||||
uses: actions/cache@v2
|
||||
env:
|
||||
cache-name: cache-node-modules
|
||||
with:
|
||||
path: ~/.npm
|
||||
key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/package-lock.json') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-build-${{ env.cache-name }}-
|
||||
${{ runner.os }}-build-
|
||||
${{ runner.os }}-
|
||||
- name: Install Dependencies
|
||||
run: npm install
|
||||
- name: "Gif tests"
|
||||
run: npm run gif-test
|
||||
|
||||
browserify-core-tests:
|
||||
runs-on: ubuntu-18.04
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Setup node
|
||||
uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: '12'
|
||||
check-latest: true
|
||||
- name: Cache node modules
|
||||
uses: actions/cache@v2
|
||||
env:
|
||||
cache-name: cache-node-modules
|
||||
with:
|
||||
path: ~/.npm
|
||||
key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/package-lock.json') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-build-${{ env.cache-name }}-
|
||||
${{ runner.os }}-build-
|
||||
${{ runner.os }}-
|
||||
- name: Install Dependencies
|
||||
run: npm install
|
||||
- run: sudo apt-get install xvfb
|
||||
- name: "Browserify core tests and run"
|
||||
run: grunt tests && xvfb-run --auto-servernum npm run core-tests
|
||||
|
||||
jsmine-ui-tests:
|
||||
runs-on: ubuntu-18.04
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Setup node
|
||||
uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: '12'
|
||||
check-latest: true
|
||||
- name: Cache node modules
|
||||
uses: actions/cache@v2
|
||||
env:
|
||||
cache-name: cache-node-modules
|
||||
with:
|
||||
path: ~/.npm
|
||||
key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/package-lock.json') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-build-${{ env.cache-name }}-
|
||||
${{ runner.os }}-build-
|
||||
${{ runner.os }}-
|
||||
- name: Install Dependencies
|
||||
run: npm install
|
||||
- name: "Jasmine UI tests (mocked browser env)"
|
||||
run: npm run test-ui
|
||||
|
||||
jest-ui-tests:
|
||||
runs-on: ubuntu-18.04
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Setup node
|
||||
uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: '12'
|
||||
check-latest: true
|
||||
- name: Cache node modules
|
||||
uses: actions/cache@v2
|
||||
env:
|
||||
cache-name: cache-node-modules
|
||||
with:
|
||||
path: ~/.npm
|
||||
key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/package-lock.json') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-build-${{ env.cache-name }}-
|
||||
${{ runner.os }}-build-
|
||||
${{ runner.os }}-
|
||||
- name: Install Dependencies
|
||||
run: npm install
|
||||
- name: "jest-puppeteer UI tests (full browser env)"
|
||||
run: npm run test-ui-2
|
||||
|
||||
cli-tests:
|
||||
runs-on: ubuntu-18.04
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Setup node
|
||||
uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: '12'
|
||||
check-latest: true
|
||||
- name: Cache node modules
|
||||
uses: actions/cache@v2
|
||||
env:
|
||||
cache-name: cache-node-modules
|
||||
with:
|
||||
path: ~/.npm
|
||||
key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/package-lock.json') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-build-${{ env.cache-name }}-
|
||||
${{ runner.os }}-build-
|
||||
${{ runner.os }}-
|
||||
- name: Install Dependencies
|
||||
run: npm install
|
||||
- name: "CLI tests"
|
||||
run: npm run test-cli
|
||||
|
||||
grunt-build-test:
|
||||
runs-on: ubuntu-18.04
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Setup node
|
||||
uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: '12'
|
||||
check-latest: true
|
||||
- name: Cache node modules
|
||||
uses: actions/cache@v2
|
||||
env:
|
||||
cache-name: cache-node-modules
|
||||
with:
|
||||
path: ~/.npm
|
||||
key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/package-lock.json') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-build-${{ env.cache-name }}-
|
||||
${{ runner.os }}-build-
|
||||
${{ runner.os }}-
|
||||
- name: Install Dependencies
|
||||
run: npm install
|
||||
- name: "Grunt build test of dev environment"
|
||||
run: grunt build
|
||||
|
||||
## Cache NPM folder
|
||||
# cache:
|
||||
# directories:
|
||||
# - ~/.npm
|
||||
# - ~/.cache
|
||||
@@ -3,5 +3,61 @@ FROM gitpod/workspace-full
|
||||
USER root
|
||||
RUN sudo apt-get update && apt-get install -y apt-transport-https \
|
||||
&& sudo apt-get install -y \
|
||||
xserver-xorg-dev libxext-dev libxi-dev build-essential libxi-dev libglu1-mesa-dev libglew-dev pkg-config libglu1-mesa-dev freeglut3-dev mesa-common-dev \
|
||||
xserver-xorg-dev \
|
||||
libxext-dev \
|
||||
build-essential \
|
||||
libxi-dev \
|
||||
libglew-dev \
|
||||
pkg-config \
|
||||
libglu1-mesa-dev \
|
||||
freeglut3-dev \
|
||||
mesa-common-dev \
|
||||
x11-apps \
|
||||
libice6 \
|
||||
libsm6 \
|
||||
libxaw7 \
|
||||
libxft2 \
|
||||
libxmu6 \
|
||||
libxpm4 \
|
||||
libxt6 \
|
||||
x11-apps \
|
||||
xbitmaps \
|
||||
ca-certificates \
|
||||
fonts-liberation \
|
||||
libappindicator3-1 \
|
||||
libasound2 \
|
||||
libatk-bridge2.0-0 \
|
||||
libatk1.0-0 \
|
||||
libc6 \
|
||||
libcairo2 \
|
||||
libcups2 \
|
||||
libdbus-1-3 \
|
||||
libexpat1 \
|
||||
libfontconfig1 \
|
||||
libgbm1 \
|
||||
libgcc1 \
|
||||
libglib2.0-0 \
|
||||
libgtk-3-0 \
|
||||
libnspr4 \
|
||||
libnss3 \
|
||||
libpango-1.0-0 \
|
||||
libpangocairo-1.0-0 \
|
||||
libstdc++6 \
|
||||
libx11-6 \
|
||||
libx11-xcb1 \
|
||||
libxcb1 \
|
||||
libxcomposite1 \
|
||||
libxcursor1 \
|
||||
libxdamage1 \
|
||||
libxext6 \
|
||||
libxfixes3 \
|
||||
libxi6 \
|
||||
libxrandr2 \
|
||||
libxrender1 \
|
||||
libxss1 \
|
||||
libxtst6 \
|
||||
lsb-release \
|
||||
wget \
|
||||
xdg-utils \
|
||||
xvfb \
|
||||
&& apt-get clean && rm -rf /var/lib/apt/lists/* /tmp/*
|
||||
|
||||
@@ -23,7 +23,9 @@ jobs:
|
||||
- name: "Jasmine UI tests (mocked browser env)"
|
||||
script: npm run test-ui
|
||||
- name: "jest-puppeteer UI tests (full browser env)"
|
||||
script: npm run test-ui-2
|
||||
script: npm run test-ui-2
|
||||
- name: "CLI tests"
|
||||
script: npm run test-cli
|
||||
- name: "Grunt build test of dev environment"
|
||||
script: grunt build
|
||||
after_success:
|
||||
|
||||
@@ -17,6 +17,7 @@ Most contribution (we imagine) would be in the form of API-compatible modules, w
|
||||
* [Ideas](#Contribution-ideas)
|
||||
* [Grunt Tasks](#grunt-tasks)
|
||||
* [UI Helper Methods](#ui-helper-methods)
|
||||
* [Scripts](#scripts)
|
||||
|
||||
****
|
||||
|
||||
@@ -374,7 +375,7 @@ module.exports =
|
||||
We are now using `eslint` and `husky` to help lint and format our code each time we commit. Eslint defines coding standards and helps in cleaning up the code. To run eslint for checking errors globally or within a specific file run:
|
||||
|
||||
```
|
||||
npx eslint .
|
||||
npx eslint .
|
||||
|
||||
npx eslint <file path>
|
||||
```
|
||||
@@ -412,15 +413,15 @@ The following command is used for running the tasks: `grunt [task-name]`. Here `
|
||||
|
||||
The method returns a scoped `jQuery` object which only searches for elements inside a given scope (a DOM element).
|
||||
|
||||
To use the method,
|
||||
To use the method,
|
||||
* import the `scopeSelector` and `scopeSelectorAll` methods from `lib/scopeQuery.js`
|
||||
* call the methods with scope as a parameter
|
||||
|
||||
|
||||
```js
|
||||
var scopeQuery = require('./scopeQuery');
|
||||
|
||||
var $step = scopeQuery.scopeSelector(scope),
|
||||
$stepAll = scopeQuery.scopeSelectorAll(scope);
|
||||
$stepAll = scopeQuery.scopeSelectorAll(scope);
|
||||
```
|
||||
This will return an object with a constructor which returns a `jQuery` object (from inside the scope) but with new `elem` and `elemAll` methods.
|
||||
|
||||
@@ -433,7 +434,7 @@ This will return an object with a constructor which returns a `jQuery` object (f
|
||||
#### Example
|
||||
|
||||
```js
|
||||
//The scope is a div element with id=“container“ and there are three divs in it
|
||||
//The scope is a div element with id=“container“ and there are three divs in it
|
||||
//with ids „1“, „2“, and „3“, and all of them have a „child“ class attribute
|
||||
|
||||
var $step = require('./scopeQuery').scopeSelector(document.getElementById('container'));
|
||||
@@ -458,3 +459,23 @@ The following code can be used
|
||||
$step('query').show().hide();
|
||||
$stepAll('q2').show().hide();
|
||||
```
|
||||
|
||||
## Scripts
|
||||
The following shell scripts are present in the `scripts/` directory.
|
||||
|
||||
- `update-gh-pages`: This script can be used to update the `gh-pages` branch of this repo or a fork.
|
||||
This script is not meant to be used directly as it runs in the current working directory.
|
||||
If you run it on your primary local clone, it can **delete** the local changes. This script is made to be used in a github action
|
||||
or in a temporary directory via another script, such as `update-demo`.
|
||||
|
||||
Arguments:
|
||||
1. Repo(to use as upstream) url in the form username/repo (default: publiclab/image-sequencer) NOTE: Github only
|
||||
2. Branch to pull from eg: main or stable (default: stable)
|
||||
3. CNAME URL (default: none)
|
||||
4. Set the fourth argument to anything to bypass the warning. You will have to set this argument if you want to run this script in another script without needing
|
||||
user interaction, such as in a github action.
|
||||
|
||||
- `update-demo`: A safe, interactive script that can be used to update the `gh-pages` branch of any image-sequencer fork.
|
||||
This script is safe to use directly because it separately clones the repo in a temporary directory.
|
||||
|
||||
Arguments: None since it is a an *interactive* script, ie it asks the user for input.
|
||||
|
||||
@@ -1,12 +1,8 @@
|
||||
/* https://github.com/theleagueof/league-spartan */
|
||||
@font-face {
|
||||
font-family: 'League Spartan';
|
||||
src: url('https://raw.githubusercontent.com/theleagueof/league-spartan/master/_webfonts/leaguespartan-bold.eot');
|
||||
src: url('https://raw.githubusercontent.com/theleagueof/league-spartan/master/_webfonts/leaguespartan-bold.eot?#iefix') format('embedded-opentype'),
|
||||
url('https://raw.githubusercontent.com/theleagueof/league-spartan/master/_webfonts/leaguespartan-bold.woff2') format('woff2'),
|
||||
url('https://raw.githubusercontent.com/theleagueof/league-spartan/master/_webfonts/leaguespartan-bold.woff') format('woff'),
|
||||
url('https://raw.githubusercontent.com/theleagueof/league-spartan/master/_webfonts/leaguespartan-bold.ttf') format('truetype'),
|
||||
url('https://raw.githubusercontent.com/theleagueof/league-spartan/master/_webfonts/leaguespartan-bold.svg#league_spartanbold') format('svg');
|
||||
src: url('https://cdn.jsdelivr.net/npm/fontsource-league-spartan@3/files/league-spartan-latin-600-normal.woff2') format('woff2'),
|
||||
url('https://cdn.jsdelivr.net/npm/fontsource-league-spartan@3/files/league-spartan-latin-600-normal.woff') format('woff');
|
||||
font-weight: bold;
|
||||
font-style: normal;
|
||||
}
|
||||
@@ -82,7 +78,8 @@ body > .container-fluid {
|
||||
}
|
||||
|
||||
.hover {
|
||||
background: #eee;
|
||||
border: 4px dashed #888888;
|
||||
background: #F0F0F0;
|
||||
}
|
||||
|
||||
.step {
|
||||
@@ -337,21 +334,47 @@ a.name-header{
|
||||
display: block;
|
||||
}
|
||||
|
||||
#update-prompt-modal {
|
||||
#update-prompt-modal,.notify-box {
|
||||
visibility: hidden;
|
||||
min-width: 250px;
|
||||
margin-left: -125px;
|
||||
background-color: #333;
|
||||
color: #fff;
|
||||
text-align: center;
|
||||
border-radius: 2px;
|
||||
border-radius: 8px;
|
||||
padding: 16px;
|
||||
position: fixed;
|
||||
z-index: 1000;
|
||||
left: 10%;
|
||||
top: 30px;
|
||||
}
|
||||
#update-prompt-modal.show {
|
||||
|
||||
#update-prompt-modal {
|
||||
width: 30vw;
|
||||
margin: 0.2rem;
|
||||
}
|
||||
|
||||
.notify-box {
|
||||
width:34vw;
|
||||
padding:18px;
|
||||
border-radius:8px;
|
||||
margin-left:0.8rem;
|
||||
text-align:left;
|
||||
color:#333;
|
||||
background:#c3c3c3;
|
||||
}
|
||||
|
||||
/* Bootstrap class for display none remove it after updating to version v4 */
|
||||
.d-none {
|
||||
display:none;
|
||||
}
|
||||
|
||||
/* Bootstrap class for display block remove it after updating to version v4 */
|
||||
.d-block {
|
||||
display:block;
|
||||
}
|
||||
|
||||
#update-prompt-modal.show,.notify-box {
|
||||
visibility: visible;
|
||||
-webkit-animation: fadein 0.5s;
|
||||
animation: fadein 0.5s;
|
||||
|
||||
@@ -58,6 +58,15 @@
|
||||
|
||||
<div id="update-prompt-modal">A new version of image sequencer is available. Click <a href="#" id="reload">here</a> to update.</div>
|
||||
|
||||
<div class="notify-box d-none">
|
||||
<strong>Failed To Load Image</strong>
|
||||
<button type="button" class="ml-2 mb-1 close" id="close-popup"><span>×</span></button>
|
||||
<div class="notify-msg">
|
||||
Can not Load Image Due to CORS Error Learn more about this
|
||||
<a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS/Errors" target="_blank">here</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="container-fluid">
|
||||
|
||||
<header class="text-center">
|
||||
@@ -66,7 +75,7 @@
|
||||
A pure JavaScript sequential image processing system, inspired by storyboards. Instead of modifying the original
|
||||
image, it
|
||||
creates a new image at each step in a sequence.
|
||||
<a href="https://github.com/publiclab/image-sequencer/blob/main/README.md">Learn more</a>
|
||||
<a href="https://publiclab.org/image-sequencer">Learn more</a>
|
||||
</p>
|
||||
<p>
|
||||
Open Source
|
||||
@@ -232,7 +241,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<p id="version-number-text">Unable to load version number</p>
|
||||
<p id="version-number-text">Loading Version Number</p>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
var setupCache = function() {
|
||||
let newWorker; // When sw.js is changed, this is the new service worker generated.
|
||||
|
||||
|
||||
// Toggle a CSS class to display a popup prompting the user to fetch a new version.
|
||||
function showUpdateModal() {
|
||||
$('#update-prompt-modal').addClass('show');
|
||||
@@ -19,34 +19,42 @@ var setupCache = function() {
|
||||
// Register the service worker.
|
||||
navigator.serviceWorker.register('sw.js', { scope: '/examples/' })
|
||||
.then(function(registration) {
|
||||
registration.addEventListener('updatefound', () => {
|
||||
// When sw.js has been changed, get a reference to the new service worker.
|
||||
newWorker = registration.installing;
|
||||
newWorker.addEventListener('statechange', () => {
|
||||
// Check if service worker state has changed.
|
||||
switch(newWorker.state) {
|
||||
case 'installed':
|
||||
if(navigator.serviceWorker.controller) {
|
||||
// New service worker available; prompt the user to update.
|
||||
showUpdateModal();
|
||||
}
|
||||
// No updates available; do nothing.
|
||||
break;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
const installingWorker = registration.installing;
|
||||
installingWorker.onstatechange = () => {
|
||||
console.log(installingWorker);
|
||||
if (installingWorker.state === 'installed') {
|
||||
location.reload();
|
||||
}
|
||||
};
|
||||
console.log('Registration successful, scope is:', registration.scope);
|
||||
})
|
||||
.catch(function(error) {
|
||||
console.log('Service worker registration failed, error:', error);
|
||||
return new Promise(function(resolve,reject){
|
||||
|
||||
registration.addEventListener('updatefound', () => {
|
||||
// When sw.js has been changed, get a reference to the new service worker.
|
||||
newWorker = registration.installing;
|
||||
|
||||
if(!newWorker){
|
||||
return reject(new Error('error in installing service worker'));
|
||||
}
|
||||
|
||||
newWorker.addEventListener('statechange', () => {
|
||||
// Check if service worker state has changed.
|
||||
switch(newWorker.state) {
|
||||
case 'installed':
|
||||
if(navigator.serviceWorker.controller) {
|
||||
// New service worker available; prompt the user to update.
|
||||
showUpdateModal();
|
||||
$('#reload').on('click',(e) => {
|
||||
e.preventDefault();
|
||||
console.log('New Service Worker Installed Successfully');
|
||||
location.reload();
|
||||
return resolve();
|
||||
})
|
||||
}
|
||||
// No updates available; do nothing.
|
||||
break;
|
||||
|
||||
case 'redundant':
|
||||
return reject(new Error('installing new service worker now became redundant'));
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
}).catch(err => {
|
||||
console.log('Failed In Registering Service Worker: ',err);
|
||||
});
|
||||
|
||||
/**
|
||||
@@ -69,21 +77,22 @@ var setupCache = function() {
|
||||
});
|
||||
}
|
||||
|
||||
$('#clear-cache').click(function() {
|
||||
const clearCache = () => {
|
||||
if ('serviceWorker' in navigator) {
|
||||
caches.keys().then(function(cacheNames) {
|
||||
cacheNames.forEach(function(cacheName) {
|
||||
caches.delete(cacheName);
|
||||
});
|
||||
return caches.keys()
|
||||
.then(function(cache) {
|
||||
return Promise.all(cache.map(function(cacheItem) {
|
||||
return caches.delete(cacheItem);
|
||||
}));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
$('#clear-cache').click(function() {
|
||||
clearCache();
|
||||
location.reload();
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
};
|
||||
|
||||
module.exports = setupCache;
|
||||
|
||||
@@ -47,7 +47,12 @@ function DefaultHtmlSequencerUi(_sequencer, options) {
|
||||
}
|
||||
|
||||
function addStepUi() {
|
||||
if ($(addStepSel + ' select').val() == 'none') return;
|
||||
|
||||
if ($(addStepSel + ' select').val() == ''){
|
||||
alert('Please Select a Step to Proceed');
|
||||
return;
|
||||
}
|
||||
|
||||
var newStepName;
|
||||
if(typeof arguments[0] !== 'string')
|
||||
newStepName = $(addStepSel + ' select option').html().toLowerCase().split(' ').join('-');
|
||||
|
||||
@@ -24,6 +24,9 @@ function DefaultHtmlStepUi(_sequencer, options) {
|
||||
function onSetup(step, stepOptions) {
|
||||
if (step.options && step.options.description)
|
||||
step.description = step.options.description;
|
||||
|
||||
let stepDocsLink = '';
|
||||
if (step.moduleInfo) stepDocsLink = step.moduleInfo['docs-link'] || '';
|
||||
|
||||
step.ui = // Basic UI markup for the step
|
||||
'\
|
||||
@@ -48,7 +51,7 @@ function DefaultHtmlStepUi(_sequencer, options) {
|
||||
<div class="row step">\
|
||||
<div class="col-md-4 details container-fluid">\
|
||||
<div class="cal collapse in"><p>' +
|
||||
'<a href="https://github.com/publiclab/image-sequencer/blob/main/docs/MODULES.md#' + step.name + '-module">' + (step.description || '') + '</a>' +
|
||||
'<a href="' + stepDocsLink + '">' + (step.description || '') + '</a>' +
|
||||
'</p></div>\
|
||||
</div>\
|
||||
<div class="col-md-8 cal collapse in step-column">\
|
||||
@@ -118,7 +121,14 @@ function DefaultHtmlStepUi(_sequencer, options) {
|
||||
paramVal + '">' + '<span class="input-group-addon"><i></i></span>' +
|
||||
'</div>';
|
||||
}
|
||||
else { // Non color-picker input types
|
||||
else if(inputDesc.type === 'button'){
|
||||
html = '<div><button name="' + paramName + '" type="' + inputDesc.type + '" >\
|
||||
<i class="fa fa-crosshairs"></i></button>\
|
||||
<span>click to select coordinates</span>\
|
||||
</div>';
|
||||
}
|
||||
else { // Non color-picker input types and other than a button
|
||||
|
||||
html =
|
||||
'<input class="form-control target" type="' +
|
||||
inputDesc.type +
|
||||
@@ -136,7 +146,7 @@ function DefaultHtmlStepUi(_sequencer, options) {
|
||||
'"max="' +
|
||||
inputDesc.max +
|
||||
'"step="' +
|
||||
(inputDesc.step ? inputDesc.step : 1) + '">' + '<span>' + paramVal + '</span>';
|
||||
inputDesc.step + '">' + '<span>' + paramVal + '</span>';
|
||||
|
||||
}
|
||||
else html += '">';
|
||||
@@ -329,12 +339,24 @@ function DefaultHtmlStepUi(_sequencer, options) {
|
||||
|
||||
$stepAll('.download-btn').on('click', () => {
|
||||
|
||||
function dataURLtoBlob(dataurl) {
|
||||
let arr = dataurl.split(','), mime = arr[0].match(/:(.*?);/)[1],
|
||||
bstr = atob(arr[1]), n = bstr.length, u8arr = new Uint8Array(n);
|
||||
while(n--){
|
||||
u8arr[n] = bstr.charCodeAt(n);
|
||||
}
|
||||
return new Blob([u8arr], {type:mime});
|
||||
}
|
||||
|
||||
var element = document.createElement('a');
|
||||
element.setAttribute('href', step.output);
|
||||
|
||||
element.setAttribute('download', step.name + '.' + fileExtension(step.imgElement.src));
|
||||
element.style.display = 'none';
|
||||
document.body.appendChild(element);
|
||||
|
||||
var blob = dataURLtoBlob(step.output);
|
||||
var objurl = URL.createObjectURL(blob);
|
||||
element.setAttribute('href', objurl);
|
||||
|
||||
element.click();
|
||||
});
|
||||
|
||||
@@ -399,6 +421,19 @@ function DefaultHtmlStepUi(_sequencer, options) {
|
||||
|
||||
var img = $(step.imgElement);
|
||||
|
||||
let customXCoord = '20'; //default x coordinate
|
||||
let customYCoord = '20'; //default y coordinate
|
||||
|
||||
const customButton = $('button[name="Custom-Coordinates"]');
|
||||
img.click(function(e) {
|
||||
customXCoord = e.offsetX;
|
||||
customYCoord = e.offsetY;
|
||||
customButton.click(function() {
|
||||
$('input[name="x"]').val(customXCoord);
|
||||
$('input[name="y"]').val(customYCoord);
|
||||
})
|
||||
});
|
||||
|
||||
img.mousemove(function(e) {
|
||||
var canvas = document.createElement('canvas');
|
||||
canvas.width = img.width();
|
||||
|
||||
@@ -7,6 +7,7 @@ function mapHtmlTypes(inputInfo){
|
||||
switch(inputInfo.type.toLowerCase()){
|
||||
case 'integer':
|
||||
htmlType = inputInfo.min != undefined ? 'range' : 'number';
|
||||
if (htmlType === 'range') inputInfo.step = inputInfo.step || 1; // default range step size for integer
|
||||
break;
|
||||
case 'string':
|
||||
htmlType = 'text';
|
||||
@@ -19,6 +20,10 @@ function mapHtmlTypes(inputInfo){
|
||||
break;
|
||||
case 'float':
|
||||
htmlType = inputInfo.min != undefined ? 'range' : 'text';
|
||||
if (htmlType === 'range') inputInfo.step = inputInfo.step || 0.1; // default range step size for float
|
||||
break;
|
||||
case 'coordinate-input':
|
||||
htmlType = 'button';
|
||||
break;
|
||||
default:
|
||||
htmlType = 'text';
|
||||
|
||||
34
examples/offline.html
Normal file
34
examples/offline.html
Normal file
@@ -0,0 +1,34 @@
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Error 502 | Bad Gateway</title>
|
||||
<style>
|
||||
body{
|
||||
background-color:#d3d3d3;
|
||||
}
|
||||
p {
|
||||
font-size:20px;
|
||||
}
|
||||
main {
|
||||
display: flex;
|
||||
flex-direction:column;
|
||||
text-align: center;
|
||||
justify-content: center;
|
||||
}
|
||||
a {
|
||||
text-decoration: none;
|
||||
color:blue;
|
||||
}
|
||||
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<main>
|
||||
<p>Error 502: something went wrong.</p>
|
||||
<p>It seems that you are not connected to internet.<br>Please try after some time.</p>
|
||||
<a href="/">Go To Home Page</a>
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,6 +1,14 @@
|
||||
const staticCacheName = 'image-sequencer-static-v3.5.1';
|
||||
self.addEventListener('install', event => {
|
||||
console.log('Attempting to install service worker');
|
||||
const staticCacheName = 'image-sequencer-static-v3.6.0';
|
||||
self.addEventListener('install', function(e) {
|
||||
e.waitUntil(
|
||||
caches.open(staticCacheName).then(function(cache) {
|
||||
console.log('Attempting to install service worker');
|
||||
return cache.addAll([
|
||||
'/',
|
||||
'/examples/offline.html'
|
||||
]);
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
self.addEventListener('activate', function(e) {
|
||||
@@ -21,16 +29,28 @@ self.addEventListener('activate', function(e) {
|
||||
|
||||
self.addEventListener('fetch', function(event) {
|
||||
event.respondWith(
|
||||
caches.open(staticCacheName).then(function(cache) {
|
||||
return cache.match(event.request).then(function (response) {
|
||||
return response || fetch(event.request).then(function(response) {
|
||||
if(event.request.method == 'GET')
|
||||
cache.put(event.request, response.clone());
|
||||
return response;
|
||||
// Try to fetch the latest data first.
|
||||
fetch(event.request)
|
||||
.then(function(response) {
|
||||
return caches.open(staticCacheName)
|
||||
.then(function(cache) {
|
||||
if(event.request.method == 'GET'){
|
||||
cache.put(event.request.url, response.clone());
|
||||
}
|
||||
return response;
|
||||
})
|
||||
})
|
||||
.catch(function(err) {
|
||||
// Now the request has been failed so show cached data.
|
||||
return caches.match(event.request).then(function(res){
|
||||
if (res === undefined) {
|
||||
// Display offline page
|
||||
return caches.match('offline.html');
|
||||
}
|
||||
return res;
|
||||
});
|
||||
});
|
||||
})
|
||||
);
|
||||
)
|
||||
});
|
||||
|
||||
// When the update modal sends a 'skipWaiting' message, call the skipWaiting method.
|
||||
|
||||
88
index.js
88
index.js
@@ -1,89 +1,5 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
require('./src/ImageSequencer');
|
||||
sequencer = ImageSequencer({ ui: true });
|
||||
var fs = require('fs');
|
||||
var program = require('commander');
|
||||
var utils = require('./src/CliUtils');
|
||||
var cli = require('./src/cli');
|
||||
|
||||
var saveSequence = require('./src/cli/saveSequence.js');
|
||||
var installModule = require('./src/cli/installModule.js');
|
||||
var sequencerSteps = require('./src/cli/sequencerSteps.js');
|
||||
|
||||
function exit(message) {
|
||||
console.error(message);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
program
|
||||
.version('0.1.0')
|
||||
.option('-i, --image [PATH/URL]', 'Input image URL')
|
||||
.option('-s, --step [step-name]', 'Name of the step to be added.')
|
||||
.option('-o, --output [PATH]', 'Directory where output will be stored.')
|
||||
.option('-b, --basic', 'Basic mode outputs only final image')
|
||||
.option('-c, --config [Object]', 'Options for the step')
|
||||
.option('--save-sequence [string]', 'Name space separated with Stringified sequence')
|
||||
.option('--install-module [string]', 'Module name space seaprated npm package name')
|
||||
.parse(process.argv);
|
||||
|
||||
if (program.saveSequence) saveSequence(program, sequencer);
|
||||
|
||||
else if (program.installModule) installModule(program, sequencer);
|
||||
|
||||
else {
|
||||
// Parse step into an array to allow for multiple steps.
|
||||
if (!program.step) exit('No steps passed');
|
||||
program.step = program.step.split(' ');
|
||||
|
||||
// User must input an image.
|
||||
if (!program.image) exit('Can\'t read file.');
|
||||
|
||||
// User must input an image.
|
||||
fs.access(program.image, function(err) {
|
||||
if (err) exit('Can\'t read file.');
|
||||
});
|
||||
|
||||
// User must input a step. If steps exist, check that every step is a valid step.
|
||||
if (!program.step || !(utils.validateSteps(program.step, sequencer)))
|
||||
exit('Please ensure all steps are valid.');
|
||||
|
||||
// If there's no user defined output directory, select a default directory.
|
||||
program.output = program.output || './output/';
|
||||
|
||||
// Set sequencer to log module outputs, if any.
|
||||
sequencer.setUI({
|
||||
onComplete: function(step) {
|
||||
// Get information of outputs.
|
||||
step.info = sequencer.modulesInfo(step.name);
|
||||
|
||||
for (var output in step.info.outputs) {
|
||||
console.log('[' + program.step + ']: ' + output + ' = ' + step[output]);
|
||||
}
|
||||
},
|
||||
notify: function(msg) {
|
||||
console.log('\x1b[36m%s\x1b[0m', '🌟 ' + msg);
|
||||
}
|
||||
});
|
||||
|
||||
// Finally, if everything is alright, load the image, add the steps and run the sequencer.
|
||||
sequencer.loadImages(program.image, function() {
|
||||
console.warn(
|
||||
'\x1b[33m%s\x1b[0m',
|
||||
'Please wait \n output directory generated will be empty until the execution is complete'
|
||||
);
|
||||
|
||||
//Generate the Output Directory
|
||||
var outputFilename = program.output.split('/').slice(-1)[0];
|
||||
if (outputFilename.includes('.')) {
|
||||
// user did give an output filename we have to remove it from dir
|
||||
program.output = program.output.split('/').slice(0, -1).join('/');
|
||||
}
|
||||
else {
|
||||
outputFilename = null;
|
||||
}
|
||||
|
||||
sequencerSteps(program, sequencer, outputFilename);
|
||||
|
||||
});
|
||||
|
||||
}
|
||||
cli(process.argv);
|
||||
@@ -1,5 +1,6 @@
|
||||
module.exports = {
|
||||
launch: {
|
||||
args: ['--no-sandbox', '--disable-setuid-sandbox'], // https://github.com/puppeteer/puppeteer/blob/main/docs/troubleshooting.md#setting-up-chrome-linux-sandbox
|
||||
headless: process.env.HEADLESS !== 'false',
|
||||
},
|
||||
server: {
|
||||
|
||||
7614
package-lock.json
generated
7614
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
21
package.json
21
package.json
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "image-sequencer",
|
||||
"version": "3.5.1",
|
||||
"version": "3.6.0",
|
||||
"description": "A modular JavaScript image manipulation library modeled on a storyboard.",
|
||||
"main": "src/ImageSequencer.js",
|
||||
"scripts": {
|
||||
@@ -12,6 +12,7 @@
|
||||
"test-all": "npm run test && npm run benchmark && npm run gif-test && grunt tests && npm run core-tests",
|
||||
"test-ui": "node node_modules/jasmine/bin/jasmine test/ui/spec/*.js",
|
||||
"test-ui-2": "node ./node_modules/.bin/jest",
|
||||
"test-cli": "node test/cli/*.js | tap-spec",
|
||||
"setup": "npm i && npm i -g grunt grunt-cli && npm rebuild --build-from-source && grunt build",
|
||||
"start": "grunt serve"
|
||||
},
|
||||
@@ -39,12 +40,13 @@
|
||||
"base64-img": "^1.0.4",
|
||||
"bootstrap": "^3.4.1",
|
||||
"bootstrap-colorpicker": "^2.5.3",
|
||||
"buffer": "~5.6.0",
|
||||
"commander": "^4.0.1",
|
||||
"buffer": "~6.0.2",
|
||||
"commander": "^7.0.0",
|
||||
"compressorjs": "^1.0.5",
|
||||
"data-uri-to-buffer": "^3.0.0",
|
||||
"downloadjs": "^1.4.7",
|
||||
"eslint": "^6.1.0",
|
||||
"expr-eval": "^2.0.2",
|
||||
"fisheyegl": "^0.1.2",
|
||||
"font-awesome": "~4.7.0",
|
||||
"geotiff": "^1.0.0-beta.6",
|
||||
@@ -63,14 +65,14 @@
|
||||
"jpegtran-bin": "^5.0.2",
|
||||
"jquery": "^3.3.1",
|
||||
"jsdom": "^16.3.0",
|
||||
"jspdf": "^1.5.3",
|
||||
"jspdf": "^2.1.1",
|
||||
"jsqr": "^1.1.1",
|
||||
"lodash": "^4.17.11",
|
||||
"ndarray": "^1.0.18",
|
||||
"opencv.js": "^1.2.1",
|
||||
"ora": "^4.0.3",
|
||||
"ora": "^5.1.0",
|
||||
"pace": "0.0.4",
|
||||
"pngquant-bin": "^5.0.2",
|
||||
"pngquant-bin": "^6.0.0",
|
||||
"puppeteer": "^1.14.0",
|
||||
"qrcode": "^1.3.3",
|
||||
"readline-sync": "^1.4.7",
|
||||
@@ -85,7 +87,7 @@
|
||||
"@babel/plugin-proposal-object-rest-spread": "^7.4.3",
|
||||
"@babel/plugin-syntax-object-rest-spread": "^7.2.0",
|
||||
"babelify": "^10.0.0",
|
||||
"browserify": "16.5.0",
|
||||
"browserify": "17.0.0",
|
||||
"eslint": "^6.1.0",
|
||||
"grunt": "^1.0.3",
|
||||
"grunt-browser-sync": "^2.2.0",
|
||||
@@ -99,14 +101,15 @@
|
||||
"image-filter-threshold": "~2.0.1",
|
||||
"jasmine-core": "^3.3.0",
|
||||
"jasmine-jquery": "^2.1.1",
|
||||
"jasmine-spec-reporter": "^4.2.1",
|
||||
"jasmine-spec-reporter": "^6.0.0",
|
||||
"jest": "^26.1.0",
|
||||
"jest-puppeteer": "^4.3.0",
|
||||
"lint-staged": "^10.0.3",
|
||||
"looks-same": "^7.0.0",
|
||||
"matchdep": "^2.0.0",
|
||||
"resemblejs": "^3.2.5",
|
||||
"tap-spec": "^5.0.0",
|
||||
"tape": "^4.9.2",
|
||||
"tape": "^5.2.0",
|
||||
"tape-run": "^8.0.0",
|
||||
"uglify-es": "^3.3.7"
|
||||
},
|
||||
|
||||
26
scripts/update-demo
Executable file
26
scripts/update-demo
Executable file
@@ -0,0 +1,26 @@
|
||||
#!/bin/bash
|
||||
|
||||
scriptsDir=$(realpath $(dirname $0))
|
||||
|
||||
echo -ne "Enter the repo to push to in the form username/repo (required): "
|
||||
read -e pushRepo
|
||||
|
||||
echo -ne "Enter repo URL to pull from (upstream) in the form username/repo (default: publiclab/image-sequencer): "
|
||||
read -e repoInput
|
||||
|
||||
echo -ne "Enter branch name (default: stable): "
|
||||
read -e branchInput
|
||||
|
||||
echo -ne "Enter CNAME URL(default: none): "
|
||||
read -e cnameUrlInput
|
||||
|
||||
tempDir=$(mktemp -d)
|
||||
pushd $tempDir > /dev/null
|
||||
|
||||
git clone https://github.com/$pushRepo
|
||||
pushd image-sequencer > /dev/null
|
||||
|
||||
$scriptsDir/update-gh-pages "$repoInput" "$branchInput" "$cnameUrlInput" "no warn"
|
||||
|
||||
popd > /dev/null
|
||||
popd > /dev/null
|
||||
117
scripts/update-gh-pages
Executable file
117
scripts/update-gh-pages
Executable file
@@ -0,0 +1,117 @@
|
||||
#!/bin/bash
|
||||
set -e # So that nothing wrong is published
|
||||
|
||||
warn() {
|
||||
echo -e "\033[1;31m
|
||||
------IMPORTANT------
|
||||
THIS SCRIPT IS NOT MEANT TO BE USED DIRECTLY, PLEASE NEWLY CLONE THE REPO IN A SEPARATE DIRECTORY AND USE THE SCRIPT THERE.
|
||||
USING THIS SCRIPT IN YOUR MAIN CLONE MAY DELETE YOUR LOCAL CHANGES.
|
||||
This script is made to be reusable: If you want to manually update the demo, \
|
||||
use the interactive script \`update-demo\`. This script can also be used in a github action.
|
||||
|
||||
You can set the 4th argument to anything to bypass this warning. \
|
||||
Setting the 4th argument means that the first 3 arguments are also set which means that you know what you are doing (I assume).
|
||||
------IMPORTANT------
|
||||
\033[0m"
|
||||
|
||||
echo -ne "Do you still want to continue? [Y/n]: "
|
||||
read -e yN
|
||||
|
||||
case $yN in
|
||||
[yY][eE][sS] | [yY])
|
||||
;;
|
||||
*)
|
||||
exit 0
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
# --- Constants ---
|
||||
deps="jquery bootstrap imgareaselect gifshot downloadjs selectize font-awesome bootstrap-colorpicker jspdf opencv.js/opencv.js" # A list of node_module dependencies to force commit
|
||||
# --- Constants ---
|
||||
|
||||
# --- Arguments ---
|
||||
# $1: Repo(to use as upstream) url in the form username/repo (default: publiclab/image-sequencer) NOTE: Github only
|
||||
# $2: Branch to pull from eg: main or stable (default: stable)
|
||||
# $3: CNAME URL (default: none)
|
||||
# $4: Set the fourth argument to anything to bypass the warning.
|
||||
|
||||
if [[ "$1" != "" ]];
|
||||
then
|
||||
repo=$1
|
||||
else
|
||||
repo="publiclab/image-sequencer"
|
||||
fi
|
||||
|
||||
if [[ "$2" != "" ]];
|
||||
then
|
||||
branch=$2
|
||||
else
|
||||
branch="stable"
|
||||
fi
|
||||
|
||||
if [[ "$3" != "" ]];
|
||||
then
|
||||
CNAMEURL=$3
|
||||
else
|
||||
CNAMEURL=""
|
||||
fi
|
||||
# --- Arguments ---
|
||||
|
||||
# --- Main Script ---
|
||||
if [[ "$4" == "" ]]; # Set a 4th argument to anything to bypass this warning.
|
||||
then
|
||||
warn
|
||||
fi
|
||||
|
||||
git checkout gh-pages
|
||||
git remote add upstream https://github.com/$repo
|
||||
git fetch upstream
|
||||
|
||||
git reset --hard upstream/$branch
|
||||
|
||||
echo -e "Running setup script."
|
||||
npm run setup
|
||||
|
||||
echo -e "Building dist files."
|
||||
grunt production
|
||||
|
||||
if [ ! -f CNAME ];
|
||||
then
|
||||
echo -e "Creating CNAME"
|
||||
touch CNAME
|
||||
fi
|
||||
|
||||
echo $CNAMEURL > CNAME
|
||||
|
||||
echo -e "Removing unnecessary files."
|
||||
rm -R docs/
|
||||
rm -R test/
|
||||
rm CONTRIBUTING.md
|
||||
rm index.js
|
||||
|
||||
echo -e "Copying important files from src/"
|
||||
cp src/ui/prepareDynamic.js prepareDynamic.js
|
||||
|
||||
echo "Removing src/"
|
||||
rm -R src/
|
||||
mkdir -p src/ui/
|
||||
mv prepareDynamic.js src/ui/prepareDynamic.js
|
||||
|
||||
echo -e "git add dist and node_modules dependencies."
|
||||
git add .
|
||||
|
||||
for dep in $deps; # Force add node_modules dependencies
|
||||
do
|
||||
git add -f node_modules/$dep
|
||||
done
|
||||
|
||||
git add -f dist/image-sequencer.js
|
||||
git add -f dist/image-sequencer-ui.js
|
||||
|
||||
echo -e "committing and pusing."
|
||||
git commit --no-verify -m "update"
|
||||
git push -f
|
||||
|
||||
exit 0
|
||||
# --- Main Script ---
|
||||
@@ -31,8 +31,10 @@ module.exports = {
|
||||
'gradient': require('./modules/Gradient'),
|
||||
'grid-overlay': require('./modules/GridOverlay'),
|
||||
'import-image': require('./modules/ImportImage'),
|
||||
'mask': require('./modules/Mask'),
|
||||
'minify-image': require('./modules/MinifyImage'),
|
||||
// 'invert': require('image-sequencer-invert'),
|
||||
// 'invert': require('image-sequencer-invert'), this code imports the invert module from a different repository altogether (using a require statement)
|
||||
// Which is a powerful feature of ImageSequencer, the modules are independent of the rest of the library's source.
|
||||
'invert': require('./modules/Invert'),
|
||||
'ndvi': require('./modules/Ndvi'),
|
||||
'ndvi-colormap': require('./modules/NdviColormap'),
|
||||
@@ -43,6 +45,7 @@ module.exports = {
|
||||
'resize': require('./modules/Resize'),
|
||||
'rotate': require('./modules/Rotate'),
|
||||
'saturation': require('./modules/Saturation'),
|
||||
'shadow': require('./modules/Shadow'),
|
||||
'text-overlay': require('./modules/TextOverlay'),
|
||||
'threshold': require('./modules/Threshold'),
|
||||
'tint': require('./modules/Tint'),
|
||||
|
||||
102
src/cli/index.js
Normal file
102
src/cli/index.js
Normal file
@@ -0,0 +1,102 @@
|
||||
require('../ImageSequencer');
|
||||
sequencer = ImageSequencer({ ui: true });
|
||||
var fs = require('fs');
|
||||
var { Command } = require('commander');
|
||||
var utils = require('../CliUtils');
|
||||
|
||||
var saveSequence = require('./saveSequence.js');
|
||||
var installModule = require('./installModule.js');
|
||||
var sequencerSteps = require('./sequencerSteps.js');
|
||||
|
||||
function exit(message) {
|
||||
console.error(message);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
function executeSteps(program) {
|
||||
// Set sequencer to log module outputs, if any.
|
||||
sequencer.setUI({
|
||||
onComplete: function (step) {
|
||||
// Get information of outputs.
|
||||
step.info = sequencer.modulesInfo(step.name);
|
||||
|
||||
for (var output in step.info.outputs) {
|
||||
console.log('[' + program.step + ']: ' + output + ' = ' + step[output]);
|
||||
}
|
||||
},
|
||||
notify: function (msg) {
|
||||
console.log('\x1b[36m%s\x1b[0m', '🌟 ' + msg);
|
||||
},
|
||||
});
|
||||
|
||||
// Finally, if everything is alright, load the image, add the steps and run the sequencer.
|
||||
sequencer.loadImages(program.image, function () {
|
||||
console.warn(
|
||||
'\x1b[33m%s\x1b[0m',
|
||||
'Please wait \n output directory generated will be empty until the execution is complete'
|
||||
);
|
||||
|
||||
//Generate the Output Directory
|
||||
var outputFilename = program.output.split('/').slice(-1)[0];
|
||||
if (outputFilename.includes('.')) {
|
||||
// user did give an output filename we have to remove it from dir
|
||||
program.output = program.output.split('/').slice(0, -1).join('/');
|
||||
} else {
|
||||
outputFilename = null;
|
||||
}
|
||||
|
||||
sequencerSteps(program, sequencer, outputFilename);
|
||||
});
|
||||
}
|
||||
|
||||
function parseSteps(program) {
|
||||
// Parse step into an array to allow for multiple steps.
|
||||
if (!program.step) exit('No steps passed');
|
||||
program.step = program.step.split(' ');
|
||||
|
||||
// User must input an image.
|
||||
if (!program.image) exit('Can\'t read file.');
|
||||
|
||||
// User must input an image.
|
||||
fs.access(program.image, function (err) {
|
||||
if (err) exit('Can\'t read file.');
|
||||
});
|
||||
|
||||
// User must input a step. If steps exist, check that every step is a valid step.
|
||||
if (!program.step || !utils.validateSteps(program.step, sequencer))
|
||||
exit('Please ensure all steps are valid.');
|
||||
|
||||
// If there's no user defined output directory, select a default directory.
|
||||
program.output = program.output || './output/';
|
||||
|
||||
executeSteps(program);
|
||||
}
|
||||
|
||||
function cli(args) {
|
||||
|
||||
let program = new Command();
|
||||
|
||||
program
|
||||
.version('0.1.0')
|
||||
.option('-i, --image [PATH/URL]', 'Input image URL')
|
||||
.option('-s, --step [step-name]', 'Name of the step to be added.')
|
||||
.option('-o, --output [PATH]', 'Directory where output will be stored.')
|
||||
.option('-b, --basic', 'Basic mode outputs only final image')
|
||||
.option('-c, --config [Object]', 'Options for the step')
|
||||
.option(
|
||||
'--save-sequence [string]',
|
||||
'Name space separated with Stringified sequence'
|
||||
)
|
||||
.option(
|
||||
'--install-module [string]',
|
||||
'Module name space seaprated npm package name'
|
||||
)
|
||||
.parse(args);
|
||||
|
||||
const options = program.opts();
|
||||
if (options.saveSequence) saveSequence(options, sequencer);
|
||||
else if (options.installModule) installModule(options, sequencer);
|
||||
else parseSteps(options);
|
||||
}
|
||||
|
||||
module.exports = cli;
|
||||
@@ -8,7 +8,8 @@
|
||||
"desc": "angle of rotation of the halftone patterns in radians",
|
||||
"default": "0.25",
|
||||
"min": "0",
|
||||
"max": "1.57"
|
||||
"max": "1.57",
|
||||
"step": "0.05"
|
||||
},
|
||||
"size": {
|
||||
"type": "integer",
|
||||
|
||||
@@ -14,20 +14,18 @@ module.exports = function Dynamic(options, UI) {
|
||||
options.blue = options.blue || defaults.blue;
|
||||
options.green = options.green || defaults.green;
|
||||
|
||||
const Parser = require('expr-eval').Parser;
|
||||
function generator(expression) {
|
||||
var func = 'f = function (r, g, b, a) { var R = r, G = g, B = b, A = a; return ' + expression + ';}';
|
||||
var f;
|
||||
eval(func);
|
||||
return f;
|
||||
let expr = Parser.parse('R = r; G = g; B = b; A = a; ' + expression);
|
||||
return expr.toJSFunction("r,g,b,a,R,G,B,A");
|
||||
}
|
||||
|
||||
|
||||
var channels = ['red', 'green', 'blue', 'alpha'];
|
||||
|
||||
channels.forEach(function(channel) {
|
||||
if (channel === 'alpha'){
|
||||
options['alpha_function'] = function() { return 255; };
|
||||
}
|
||||
else{
|
||||
} else {
|
||||
options[channel + '_function'] = generator(options[channel]);
|
||||
}
|
||||
});
|
||||
|
||||
82
src/modules/Mask/Module.js
Normal file
82
src/modules/Mask/Module.js
Normal file
@@ -0,0 +1,82 @@
|
||||
module.exports = function Mask(options, UI, util) {
|
||||
var defaults = require('./../../util/getDefaults.js')(require('./info.json'));
|
||||
options.offset = options.offset || defaults.offset;
|
||||
options.resize = options.resize || defaults.resize;
|
||||
|
||||
var output;
|
||||
|
||||
// This function is called on every draw.
|
||||
function draw(input, callback, progressObj) {
|
||||
progressObj.stop(true);
|
||||
progressObj.overrideFlag = true;
|
||||
|
||||
var step = this;
|
||||
|
||||
var getPixels = require('get-pixels');
|
||||
|
||||
// convert offset as string to int
|
||||
if (typeof options.offset === 'string')
|
||||
options.offset = parseInt(options.offset);
|
||||
|
||||
// save first image's pixels
|
||||
var priorStep = this.getStep(options.offset);
|
||||
|
||||
if (priorStep.output === undefined) {
|
||||
this.output = input;
|
||||
UI.notify('Offset Unavailable', 'offset-notification');
|
||||
callback();
|
||||
}
|
||||
|
||||
const alpha_masking = function(c, a) {
|
||||
return (a * c + (255 - a) * c) / 255;
|
||||
};
|
||||
const internalSequencer = ImageSequencer({ inBrowser: false, ui: false });
|
||||
internalSequencer.loadImage(priorStep.output.src, function() {
|
||||
internalSequencer.importJSON([{ 'name': 'resize', 'options': { resize: options.resize } }]);
|
||||
internalSequencer.run(function onCallback(internalOutput) {
|
||||
|
||||
getPixels(internalOutput, function(err, pixels) {
|
||||
options.firstImagePixels = pixels;
|
||||
|
||||
function changePixel(r2, g2, b2, a2, x, y) {
|
||||
let p = options.firstImagePixels;
|
||||
let r1 = p.get(x, y, 0),
|
||||
g1 = p.get(x, y, 1),
|
||||
b1 = p.get(x, y, 2),
|
||||
a1 = p.get(x, y, 3);
|
||||
|
||||
return [alpha_masking(r1, a2), alpha_masking(g1, a2), alpha_masking(b1, a2)];
|
||||
}
|
||||
|
||||
function output(image, datauri, mimetype, wasmSuccess) {
|
||||
step.output = {
|
||||
src: datauri,
|
||||
format: mimetype,
|
||||
wasmSuccess,
|
||||
useWasm: options.useWasm
|
||||
};
|
||||
}
|
||||
|
||||
// run PixelManipulatin on second image's pixels
|
||||
return require('../_nomodule/PixelManipulation.js')(input, {
|
||||
output: output,
|
||||
ui: options.step.ui,
|
||||
changePixel: changePixel,
|
||||
format: input.format,
|
||||
image: options.image,
|
||||
inBrowser: options.inBrowser,
|
||||
callback: callback,
|
||||
useWasm: options.useWasm
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
options: options,
|
||||
draw: draw,
|
||||
output: output,
|
||||
UI: UI
|
||||
};
|
||||
};
|
||||
4
src/modules/Mask/index.js
Normal file
4
src/modules/Mask/index.js
Normal file
@@ -0,0 +1,4 @@
|
||||
module.exports = [
|
||||
require('./Module'),
|
||||
require('./info.json')
|
||||
];
|
||||
18
src/modules/Mask/info.json
Normal file
18
src/modules/Mask/info.json
Normal file
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"name": "mask",
|
||||
"description": "Masks two images according to their Alpha values",
|
||||
"inputs": {
|
||||
"offset": {
|
||||
"type": "integer",
|
||||
"desc": "Choose which image to mask the current image with. Two steps back is -2, three steps back is -3 etc.",
|
||||
"default": -2
|
||||
},
|
||||
"resize": {
|
||||
"type": "string",
|
||||
"desc": "Percentage value by which first image is to be resized",
|
||||
"default": "125%"
|
||||
}
|
||||
},
|
||||
"docs-link":"https://github.com/publiclab/image-sequencer/blob/main/docs/MODULES.md"
|
||||
}
|
||||
|
||||
@@ -1,15 +1,17 @@
|
||||
module.exports = function Dynamic(options, UI, util) {
|
||||
|
||||
var defaults = require('./../../util/getDefaults.js')(require('./info.json'));
|
||||
const defaults = require('./../../util/getDefaults.js')(require('./info.json'));
|
||||
options.x = options.x || defaults.x;
|
||||
options.y = options.y || defaults.y;
|
||||
|
||||
if(options.step.inBrowser && !options.noUI && sequencer.getSteps().length < 2)
|
||||
options.offset = -1;
|
||||
|
||||
if (options.step.inBrowser && !options.noUI) var ui = require('./Ui.js')(options.step, UI);
|
||||
let ui;
|
||||
|
||||
var output;
|
||||
if (options.step.inBrowser && !options.noUI) ui = require('./Ui.js')(options.step, UI);
|
||||
|
||||
let output;
|
||||
|
||||
// This function is called on every draw.
|
||||
function draw(input, callback, progressObj) {
|
||||
@@ -19,15 +21,15 @@ module.exports = function Dynamic(options, UI, util) {
|
||||
progressObj.stop(true);
|
||||
progressObj.overrideFlag = true;
|
||||
|
||||
var step = this;
|
||||
const step = this;
|
||||
|
||||
var parseCornerCoordinateInputs = require('../../util/ParseInputCoordinates');
|
||||
const parseCornerCoordinateInputs = require('../../util/ParseInputCoordinates');
|
||||
|
||||
// save the pixels of the base image
|
||||
var baseStepImage = this.getStep(options.offset).image;
|
||||
var baseStepOutput = this.getOutput(options.offset);
|
||||
const baseStepImage = this.getStep(options.offset).image;
|
||||
const baseStepOutput = this.getOutput(options.offset);
|
||||
|
||||
var getPixels = require('get-pixels');
|
||||
const getPixels = require('get-pixels');
|
||||
|
||||
getPixels(input.src, function(err, pixels) {
|
||||
// parse the inputs
|
||||
@@ -47,20 +49,29 @@ module.exports = function Dynamic(options, UI, util) {
|
||||
|
||||
function changePixel(r1, g1, b1, a1, x, y) {
|
||||
|
||||
const firstImagePixels = [r1, g1, b1, a1];
|
||||
|
||||
// overlay
|
||||
var p = options.secondImagePixels;
|
||||
const p = options.secondImagePixels;
|
||||
if (x >= options.x
|
||||
&& x - options.x < p.shape[0]
|
||||
&& y >= options.y
|
||||
&& y - options.y < p.shape[1])
|
||||
return [
|
||||
&& y - options.y < p.shape[1]){
|
||||
|
||||
const secondImagePixels = [
|
||||
p.get(x - options.x, y - options.y, 0),
|
||||
p.get(x - options.x, y - options.y, 1),
|
||||
p.get(x - options.x, y - options.y, 2),
|
||||
p.get(x - options.x, y - options.y, 3)
|
||||
];
|
||||
|
||||
if(secondImagePixels[3] === 0)
|
||||
return firstImagePixels;
|
||||
else
|
||||
return secondImagePixels;
|
||||
}
|
||||
else
|
||||
return [r1, g1, b1, a1];
|
||||
return firstImagePixels;
|
||||
}
|
||||
|
||||
function output(image, datauri, mimetype, wasmSuccess) {
|
||||
|
||||
@@ -2,7 +2,7 @@ const imagejs = require('imagejs'),
|
||||
pixelSetter = require('../../util/pixelSetter'),
|
||||
ndarray = require('ndarray');
|
||||
module.exports = function Resize(pixels, options) {
|
||||
const resize_value = parseInt(options.resize.slice(0, -1));
|
||||
const resize_value = parseFloat(options.resize);
|
||||
|
||||
if (resize_value == 100) return pixels;
|
||||
|
||||
|
||||
146
src/modules/Shadow/Module.js
Normal file
146
src/modules/Shadow/Module.js
Normal file
@@ -0,0 +1,146 @@
|
||||
/*
|
||||
* Create Shadow
|
||||
*/
|
||||
module.exports = function canvasResize(options, UI) {
|
||||
|
||||
var defaults = require('./../../util/getDefaults.js')(require('./info.json'));
|
||||
const pixelSetter = require('../../util/pixelSetter.js');
|
||||
|
||||
var output;
|
||||
|
||||
function draw(input, callback, progressObj) {
|
||||
|
||||
options.X_value = parseInt(options.X_value || defaults.X_value);
|
||||
options.Y_value = parseInt(options.Y_value || defaults.Y_value);
|
||||
|
||||
progressObj.stop(true);
|
||||
progressObj.overrideFlag = true;
|
||||
|
||||
var step = this;
|
||||
|
||||
function extraManipulation(pixels) {
|
||||
let [w, h] = pixels.shape;
|
||||
let newPixels = require('ndarray')(new Uint8Array(4 * (w + Math.abs(options.X_value)) * (h + Math.abs(options.Y_value))).fill(0), [(w + Math.abs(options.X_value)), (h + Math.abs(options.Y_value)), 4]);
|
||||
let iMax = w,
|
||||
jMax = h;
|
||||
if (options.X_value < 0 && options.Y_value < 0) {
|
||||
for (var k = 0; k < Math.abs(options.X_value); k++) {
|
||||
for (var l = 0; l < (h + Math.abs(options.Y_value)); l++) {
|
||||
let val = 255 - ((k / Math.abs(options.X_value)) * 255);
|
||||
pixelSetter(k, l, [val, val, val, 255], newPixels);
|
||||
}
|
||||
}
|
||||
for (var k = 0; k < (w + Math.abs(options.X_value)); k++) {
|
||||
for (var l = 0; l < Math.abs(options.Y_value); l++) {
|
||||
if (k < Math.abs(options.X_value) && k < l) {
|
||||
continue;
|
||||
}
|
||||
let val = 255 - ((l / Math.abs(options.Y_value)) * 255);
|
||||
pixelSetter(k, l, [val, val, val, 255], newPixels);
|
||||
}
|
||||
}
|
||||
for (let i = 0; i < iMax && i < w; i++) {
|
||||
for (let j = 0; j < jMax && j < h; j++) {
|
||||
let x = i + Math.abs(options.X_value), y = j + Math.abs(options.Y_value);
|
||||
pixelSetter(x, y, [pixels.get(i, j, 0), pixels.get(i, j, 1), pixels.get(i, j, 2), pixels.get(i, j, 3)], newPixels);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
else if (options.X_value >= 0 && options.Y_value >= 0) {
|
||||
for (var k = w; k < (w + options.X_value); k++) {
|
||||
for (var l = 0; l < (h + options.Y_value); l++) {
|
||||
let val = (((k - w) / options.X_value) * 255);
|
||||
pixelSetter(k, l, [val, val, val, 255], newPixels);
|
||||
}
|
||||
}
|
||||
for (var k = 0; k < (w + options.X_value); k++) {
|
||||
for (var l = h; l < (h + options.Y_value); l++) {
|
||||
if (k >= w && l >= h && ((k - w) >= (l - h))) {
|
||||
continue;
|
||||
}
|
||||
let val = ((l - h) / options.Y_value * 255);
|
||||
pixelSetter(k, l, [val, val, val, 255], newPixels);
|
||||
}
|
||||
}
|
||||
for (let i = 0; i < iMax && i < w; i++) {
|
||||
for (let j = 0; j < jMax && j < h; j++) {
|
||||
let x = i, y = j;
|
||||
pixelSetter(x, y, [pixels.get(i, j, 0), pixels.get(i, j, 1), pixels.get(i, j, 2), pixels.get(i, j, 3)], newPixels);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
else if (options.X_value < 0 && options.Y_value >= 0) {
|
||||
for (var k = 0; k < (w + Math.abs(options.X_value)); k++) {
|
||||
for (var l = h; l < (h + options.Y_value); l++) {
|
||||
let val = ((l - h) / options.Y_value * 255);
|
||||
pixelSetter(k, l, [val, val, val, 255], newPixels);
|
||||
}
|
||||
}
|
||||
for (var k = 0; k < Math.abs(options.X_value); k++) {
|
||||
for (var l = 0; l < (h + options.Y_value); l++) {
|
||||
if (l + (k * (options.Y_value / Math.abs(options.X_value))) - (options.Y_value + h) > 0 && l >= h) {
|
||||
continue;
|
||||
}
|
||||
let val = 255 - ((k / Math.abs(options.X_value)) * 255);
|
||||
pixelSetter(k, l, [val, val, val, 255], newPixels);
|
||||
}
|
||||
}
|
||||
for (let i = 0; i < iMax && i < w; i++) {
|
||||
for (let j = 0; j < jMax && j < h; j++) {
|
||||
let x = i + Math.abs(options.X_value), y = j;
|
||||
pixelSetter(x, y, [pixels.get(i, j, 0), pixels.get(i, j, 1), pixels.get(i, j, 2), pixels.get(i, j, 3)], newPixels);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
else if (options.X_value >= 0 && options.Y_value < 0) {
|
||||
for (var k = w; k < (w + options.X_value); k++) {
|
||||
for (var l = 0; l < (h + Math.abs(options.Y_value)); l++) {
|
||||
let val = (((k - w) / options.X_value) * 255);
|
||||
pixelSetter(k, l, [val, val, val, 255], newPixels);
|
||||
}
|
||||
}
|
||||
for (var k = 0; k < (w + options.X_value); k++) {
|
||||
for (var l = 0; l < Math.abs(options.Y_value); l++) {
|
||||
if (l >= ((options.X_value / Math.abs(options.Y_value)) * (w + options.X_value - k)) && k >= w) {
|
||||
continue;
|
||||
}
|
||||
let val = 255 - (l / Math.abs(options.Y_value) * 255);
|
||||
pixelSetter(k, l, [val, val, val, 255], newPixels);
|
||||
}
|
||||
}
|
||||
for (let i = 0; i < iMax && i < w; i++) {
|
||||
for (let j = 0; j < jMax && j < h; j++) {
|
||||
let x = i, y = j + Math.abs(options.Y_value);
|
||||
pixelSetter(x, y, [pixels.get(i, j, 0), pixels.get(i, j, 1), pixels.get(i, j, 2), pixels.get(i, j, 3)], newPixels);
|
||||
}
|
||||
}
|
||||
}
|
||||
return newPixels;
|
||||
}
|
||||
|
||||
function output(image, datauri, mimetype, wasmSuccess) {
|
||||
step.output = { src: datauri, format: mimetype, wasmSuccess, useWasm: options.useWasm };
|
||||
}
|
||||
|
||||
return require('../_nomodule/PixelManipulation.js')(input, {
|
||||
output: output,
|
||||
ui: options.step.ui,
|
||||
extraManipulation: extraManipulation,
|
||||
format: input.format,
|
||||
image: options.image,
|
||||
inBrowser: options.inBrowser,
|
||||
callback: callback,
|
||||
useWasm:options.useWasm
|
||||
});
|
||||
|
||||
}
|
||||
return {
|
||||
options: options,
|
||||
draw: draw,
|
||||
output: output,
|
||||
UI: UI
|
||||
};
|
||||
};
|
||||
4
src/modules/Shadow/index.js
Normal file
4
src/modules/Shadow/index.js
Normal file
@@ -0,0 +1,4 @@
|
||||
module.exports = [
|
||||
require('./Module'),
|
||||
require('./info.json')
|
||||
];
|
||||
17
src/modules/Shadow/info.json
Normal file
17
src/modules/Shadow/info.json
Normal file
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"name": "shadow",
|
||||
"description": "Creates a two sided shadow.",
|
||||
"inputs": {
|
||||
"X_value": {
|
||||
"type": "integer",
|
||||
"desc": "X-value",
|
||||
"default": 20
|
||||
},
|
||||
"Y_value": {
|
||||
"type": "integer",
|
||||
"desc": "Y-value",
|
||||
"default": 20
|
||||
}
|
||||
},
|
||||
"docs-link":"https://github.com/publiclab/image-sequencer/blob/main/docs/MODULES.md#shadow-module"
|
||||
}
|
||||
@@ -8,6 +8,10 @@
|
||||
"desc": "Enter the text to overlay.",
|
||||
"default": "Lorem ipsum"
|
||||
},
|
||||
"Custom-Coordinates": {
|
||||
"type": "coordinate-input",
|
||||
"desc": "Click to fill Coordinates"
|
||||
},
|
||||
"x": {
|
||||
"type": "integer",
|
||||
"desc": "Starting text horizontal position.",
|
||||
|
||||
@@ -7,6 +7,22 @@ function LoadImage(ref, name, src, main_callback) {
|
||||
};
|
||||
return image;
|
||||
}
|
||||
|
||||
// function to check whether a image can be fetched from external source or not
|
||||
function checkForError(image_url) {
|
||||
return fetch(image_url).then(function(res) {
|
||||
if(res)
|
||||
return false;
|
||||
else
|
||||
return true;
|
||||
}).catch(function(err) {
|
||||
if(err)
|
||||
console.log('Error occured because of image URL ',err);
|
||||
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
function CImage(src, step, callback) {
|
||||
var datauri;
|
||||
if (src.match(/^data:/i)) {
|
||||
@@ -25,18 +41,42 @@ function LoadImage(ref, name, src, main_callback) {
|
||||
});
|
||||
}
|
||||
else if (ref.options.inBrowser) {
|
||||
var ext = src.split('.').pop();
|
||||
var image = document.createElement('img');
|
||||
var canvas = document.createElement('canvas');
|
||||
var context = canvas.getContext('2d');
|
||||
image.onload = function() {
|
||||
canvas.width = image.naturalWidth;
|
||||
canvas.height = image.naturalHeight;
|
||||
context.drawImage(image, 0, 0);
|
||||
datauri = canvas.toDataURL(ext);
|
||||
callback(datauri, step);
|
||||
};
|
||||
image.src = src;
|
||||
|
||||
let notifyBox = document.querySelector('div.notify-box');
|
||||
let closePopUP = document.getElementById('close-popup');
|
||||
if(src.indexOf('images/') !== 0 && src.indexOf('./images/') !== 0 && checkForError(src)){
|
||||
|
||||
if(notifyBox){
|
||||
notifyBox.classList.remove('d-none');
|
||||
notifyBox.classList.add('d-block');
|
||||
}
|
||||
|
||||
if(closePopUP){
|
||||
closePopUP.addEventListener('click',function(){
|
||||
if(notifyBox){
|
||||
notifyBox.classList.remove('d-block');
|
||||
notifyBox.classList.add('d-none');
|
||||
}
|
||||
if(document.querySelector('button.remove'))
|
||||
document.querySelector('button.remove').click(); // Remove the step due to redundant processing.
|
||||
location.reload();
|
||||
});
|
||||
}
|
||||
}
|
||||
else{
|
||||
var ext = src.split('.').pop();
|
||||
var image = document.createElement('img');
|
||||
var canvas = document.createElement('canvas');
|
||||
var context = canvas.getContext('2d');
|
||||
image.onload = function() {
|
||||
canvas.width = image.naturalWidth;
|
||||
canvas.height = image.naturalHeight;
|
||||
context.drawImage(image, 0, 0);
|
||||
datauri = canvas.toDataURL(ext);
|
||||
callback(datauri, step);
|
||||
};
|
||||
image.src = src;
|
||||
}
|
||||
}
|
||||
else {
|
||||
datauri = require('urify')(src);
|
||||
|
||||
@@ -51,21 +51,49 @@ function setInputStepInit() {
|
||||
video.onloadedmetadata = function(e) {
|
||||
video.play();
|
||||
};
|
||||
|
||||
document.getElementById('capture').addEventListener('click', function(){
|
||||
context.drawImage(video, 0, 0, 400, 300);
|
||||
options.onTakePhoto(canvas.toDataURL());
|
||||
setTimeout(stopStream(stream),1); // wait for 1 second before closing webcam so that image loads properly
|
||||
});
|
||||
|
||||
document.getElementById('close').addEventListener('click', function () {
|
||||
stopStream(stream);
|
||||
});
|
||||
}
|
||||
function handleError(error) {
|
||||
console.log('navigator.getUserMedia error: ', error);
|
||||
|
||||
// when user dismissed the camera access (includes closing of prompt which requests for camera access)
|
||||
if(error.message == 'Permission denied' || error.message == 'NotAllowedError' || error.message == 'PermissionDismissedError'){
|
||||
document.getElementById('capture').addEventListener('click', function(e) {
|
||||
alert('Enable camera access in order to take picture');
|
||||
});
|
||||
}
|
||||
|
||||
// when user don't have webcam to use.
|
||||
if(error.message == 'NotFoundError' || error.message == 'DevicesNotFoundError'){
|
||||
alert('You do not have appropriate devices to use this Functionality');
|
||||
}
|
||||
|
||||
// when webcam is already used by some other application
|
||||
if(error.message == 'NotReadableError' || error.message == 'TrackStartError' || error.message == 'Concurrent mic process limit'){
|
||||
alert('Your webcam is already in use by some other application');
|
||||
}
|
||||
|
||||
// when some of the requested constraints can't be satisfied like high frame rate or high resolution
|
||||
if(error.message == 'OverconstrainedError' || error.message == 'ConstraintNotSatisfiedError'){
|
||||
console.log('Requested Constraints can not be satisfied ', error);
|
||||
}
|
||||
}
|
||||
navigator.mediaDevices.getUserMedia(constraints).then(handleSuccess).catch(handleError);
|
||||
|
||||
|
||||
document.getElementById('capture').addEventListener('click', function(stream){
|
||||
context.drawImage(video, 0, 0, 400, 300);
|
||||
options.onTakePhoto(canvas.toDataURL());
|
||||
|
||||
document.getElementById('close').addEventListener('click', function() {
|
||||
video.style.display = 'none';
|
||||
});
|
||||
|
||||
|
||||
function stopStream(stream) {
|
||||
stream.getVideoTracks().forEach(function (track) {
|
||||
track.stop();
|
||||
@@ -86,10 +114,10 @@ function setInputStepInit() {
|
||||
dropzone[0].addEventListener('drop', handleFile, false);
|
||||
|
||||
dropzone.on('dragover', function onDragover(e) {
|
||||
e.stopPropagation();
|
||||
dropzone.addClass('hover');
|
||||
e.preventDefault();
|
||||
e.dataTransfer.dropEffect = 'copy'; // Explicitly show this is a copy.
|
||||
}, false);
|
||||
e.stopPropagation();
|
||||
});
|
||||
|
||||
dropzone.on('dragenter', function onDragEnter(e) {
|
||||
dropzone.addClass('hover');
|
||||
@@ -99,6 +127,11 @@ function setInputStepInit() {
|
||||
dropzone.removeClass('hover');
|
||||
});
|
||||
|
||||
dropzone.on('drop', function onDrop(e) {
|
||||
dropzone.removeClass('hover');
|
||||
e.preventDefault();
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
/** Parses the defaults and gets the input which is available. */
|
||||
module.exports = function(info){
|
||||
var defaults = {};
|
||||
for (var key in info.inputs) {
|
||||
|
||||
@@ -1,5 +1,12 @@
|
||||
/**
|
||||
* @param {number} x x-coordinate.
|
||||
* @param {number} y y-coordinate.
|
||||
* @param {object} value array [r, g, b, a]
|
||||
* @param {object} pixels NDarray of pixels.
|
||||
* @description Sets the pixels from 0 through length of value.
|
||||
*/
|
||||
module.exports = function(x, y, value, pixels){
|
||||
for(let i = 0; i < value.length; i++){
|
||||
pixels.set(x, y, i, value[i]);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
22
test/cli/saveSequence.js
Executable file
22
test/cli/saveSequence.js
Executable file
@@ -0,0 +1,22 @@
|
||||
const test = require('tape');
|
||||
const cli = require('../../src/cli');
|
||||
|
||||
test('testing save sequence function', function (t) {
|
||||
try {
|
||||
cli([
|
||||
'node', 'test',
|
||||
'--save-sequence',
|
||||
'"invert-colormap invert(),colormap()"',
|
||||
]);
|
||||
t.true(1, 'creation success');
|
||||
} catch (error) {
|
||||
t.true(!error, 'creation fail');
|
||||
}
|
||||
try {
|
||||
cli(['node', 'test', '--save-sequence']);
|
||||
t.true(0, 'creation success');
|
||||
} catch (error) {
|
||||
t.true(1, 'creation fail');
|
||||
}
|
||||
t.end();
|
||||
});
|
||||
37
test/cli/steps.js
Executable file
37
test/cli/steps.js
Executable file
@@ -0,0 +1,37 @@
|
||||
const test = require('tape');
|
||||
const cli = require('../../src/cli');
|
||||
const stdout = require('./util/readConsole').stdout;
|
||||
const stderr = require('./util/readConsole').stderr;
|
||||
|
||||
function sleep(ms) {
|
||||
return new Promise(resolve => setTimeout(resolve, ms));
|
||||
}
|
||||
|
||||
test('testing steps parsing', function (t) {
|
||||
|
||||
t.plan(1);
|
||||
|
||||
let out = stdout.read();
|
||||
|
||||
cli([
|
||||
'node', 'test',
|
||||
'-i', 'examples/images/test.png',
|
||||
'-s', 'invert',
|
||||
]);
|
||||
|
||||
sleep(1000).then(() => {
|
||||
out.restore();
|
||||
let validator = out.output().includes('Added Step "invert"');
|
||||
t.true(validator, 'Steps parsed successfully');
|
||||
});
|
||||
|
||||
|
||||
// let err = stderr.read();
|
||||
// await cli([
|
||||
// 'node', 'test',
|
||||
// '-i', 'examples/images/test.png',
|
||||
// '-s', 'invalidStep',
|
||||
// ]);
|
||||
// err.restore();
|
||||
// t.equal(err.output, 'Please ensure all steps are valid.');
|
||||
});
|
||||
26
test/cli/util/readConsole.js
Normal file
26
test/cli/util/readConsole.js
Normal file
@@ -0,0 +1,26 @@
|
||||
function ReadLog(stream) {
|
||||
this._stream = stream;
|
||||
}
|
||||
|
||||
ReadLog.prototype.read = function(options) {
|
||||
|
||||
let output = '';
|
||||
let stream = this._stream;
|
||||
|
||||
let originalStreamWrite = stream.write;
|
||||
stream.write = function(string) {
|
||||
output += string;
|
||||
};
|
||||
|
||||
return {
|
||||
output: function () {
|
||||
return output;
|
||||
},
|
||||
restore: function() {
|
||||
stream.write = originalStreamWrite;
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
exports.stdout = new ReadLog(process.stdout);
|
||||
exports.stderr = new ReadLog(process.stderr);
|
||||
40
test/core/modules/mask.js
Normal file
40
test/core/modules/mask.js
Normal file
File diff suppressed because one or more lines are too long
@@ -1,4 +1,21 @@
|
||||
const testModule = require('../templates/module-test'),
|
||||
benchmark = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABUAAAAVCAYAAACpF6WWAAAAAklEQVR4AewaftIAAAAzSURBVLXBAQEAMAiAME7/zN4Ssr2BzzEJSEACEpCABCQgAQlIQAISkIAEJCABCUhAAhJYclMCJyy7k2QAAAAASUVORK5CYII=';
|
||||
benchmark = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABUAAAAVCAYAAACpF6WWAAAAAklEQVR4AewaftIAAAAzSURBVLXBAQEAMAiAME7/zN4Ssr2BzzEJSEACEpCABCQgAQlIQAISkIAEJCABCUhAAhJYclMCJyy7k2QAAAAASUVORK5CYII=',
|
||||
|
||||
testModule('resize', {resize: '129%'}, benchmark);
|
||||
benchmark1 = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAsAAAALCAYAAACprHcmAAAAAklEQVR4AewaftIAAAAkSURBVJXBAQEAMAiAME7/zN4Ksr2Bz5EEEkgggQQSSCCBBBIs6poCE8Zr7KAAAAAASUVORK5CYII=',
|
||||
|
||||
benchmark2 = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAAAklEQVR4AewaftIAAAAiSURBVI3BAQEAAAiAIPP/5uqCMAtHIJFEEkkkkUQSSSTRAzwDAhGkYPRhAAAAAElFTkSuQmCC',
|
||||
|
||||
benchmark3 = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAYAAAAGCAYAAADgzO9IAAAAAklEQVR4AewaftIAAAAdSURBVHXBAQEAMAiAME7/zN4Csr2Bz0GCBAkSJCwpbQIJAvmJUgAAAABJRU5ErkJggg==',
|
||||
|
||||
options1 = {
|
||||
resize: '70.85%'
|
||||
},
|
||||
options2 = {
|
||||
resize: '60 %'
|
||||
},
|
||||
options3 = {
|
||||
resize: '40'
|
||||
};
|
||||
|
||||
testModule('resize', {resize: '129%'}, benchmark);
|
||||
require('../templates/options-test')('resize', [options1, options2, options3], [benchmark1, benchmark2, benchmark3]);
|
||||
8
test/core/modules/shadow.js
Normal file
8
test/core/modules/shadow.js
Normal file
File diff suppressed because one or more lines are too long
37
test/core/sequencer/sw.js
Normal file
37
test/core/sequencer/sw.js
Normal file
@@ -0,0 +1,37 @@
|
||||
var setUpCache = new require('../../../examples/lib/cache')();
|
||||
var test = require('tape');
|
||||
|
||||
function SWInstallation(){
|
||||
return new Promise(() => {
|
||||
return setupCache();
|
||||
});
|
||||
}
|
||||
|
||||
function UnRegisterSW(){
|
||||
|
||||
function unregister() {
|
||||
return navigator.serviceWorker.getRegistrations()
|
||||
.then(function(registrations) {
|
||||
var unRegisteredWorker = registrations.map(function(registration) {
|
||||
return registration.unregister();
|
||||
});
|
||||
return Promise.all(unRegisteredWorker);
|
||||
});
|
||||
}
|
||||
|
||||
return Promise.all([
|
||||
unregister(),
|
||||
setUpCache.clearCache()
|
||||
]);
|
||||
}
|
||||
|
||||
test('Register service worker',function(t) {
|
||||
|
||||
t.test('unregister service worker',function(st) {
|
||||
st.equal(UnRegisterSW(),true,'unregistered successfully and cleared the cache')
|
||||
})
|
||||
|
||||
t.test('install service worker',function(st) {
|
||||
st.equal(SWInstallation(),true,'successfully installed new service worker')
|
||||
});
|
||||
});
|
||||
@@ -1,6 +1,8 @@
|
||||
const test = require('tape'),
|
||||
base64Img = require('base64-img');
|
||||
|
||||
const compare = require('resemblejs').compare;
|
||||
|
||||
const ImageSequencer = require('../../../src/ImageSequencer');
|
||||
|
||||
const test_gif = require('../images/test.gif.js');
|
||||
@@ -15,19 +17,37 @@ target = 'test_outputs';
|
||||
* @param {String} [input="test_gif"] optional input image. Default is a test gif.
|
||||
*/
|
||||
module.exports = (moduleName, options, benchmark, input) => {
|
||||
let sequencer = ImageSequencer({ui: false});
|
||||
let sequencer = ImageSequencer({ ui: false });
|
||||
sequencer.loadImages(input || test_gif);
|
||||
sequencer.addSteps(moduleName, options);
|
||||
test(`${moduleName} module works correctly`, t => {
|
||||
sequencer.run({mode: 'test'}, () => {
|
||||
test(`${moduleName} module works correctly`, (t) => {
|
||||
sequencer.run({ mode: 'test' }, () => {
|
||||
let result = sequencer.steps[1].output.src;
|
||||
|
||||
base64Img.imgSync(result, target, `${moduleName}-result`);
|
||||
base64Img.imgSync(benchmark, target, `${moduleName}-benchmark`);
|
||||
|
||||
t.equal(result === benchmark, true, `${moduleName} module works correctly with Gif`);
|
||||
let mismatch = 100;
|
||||
compare(
|
||||
result,
|
||||
benchmark,
|
||||
{ returnEarlyThreshold: 5 },
|
||||
(err, { rawMisMatchPercentage }) => {
|
||||
if (err) {
|
||||
console.log('An error while comparing!');
|
||||
} else {
|
||||
mismatch = rawMisMatchPercentage;
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
t.equal(
|
||||
mismatch < 5,
|
||||
true,
|
||||
`${moduleName} module works correctly with Gif`
|
||||
);
|
||||
sequencer = null;
|
||||
t.end();
|
||||
});
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
@@ -24,6 +24,9 @@ module.exports = (moduleName, options, benchmark, input) => {
|
||||
sequencer.loadImages(input || red);
|
||||
// Add the step.
|
||||
sequencer.addSteps(moduleName, options[0]);
|
||||
|
||||
t.plan(2);
|
||||
|
||||
// Run the ImageSequencer with initial option.
|
||||
sequencer.run(() => {
|
||||
let result = sequencer.steps[1].output.src;
|
||||
@@ -37,7 +40,7 @@ module.exports = (moduleName, options, benchmark, input) => {
|
||||
looksSame(result, benchmark[0], function(err, res) {
|
||||
if (err) console.log(err);
|
||||
|
||||
t.equal(res.equal, true, `${moduleName} module works correctly with initial option ${options[0][moduleName]}`);
|
||||
t.equal(res.equal, true, `${moduleName} module works correctly with initial option ${JSON.stringify(options[0])}`);
|
||||
});
|
||||
// Change the option of the given module.
|
||||
sequencer.steps[1].setOptions(options[1]);
|
||||
@@ -54,9 +57,8 @@ module.exports = (moduleName, options, benchmark, input) => {
|
||||
looksSame(newResult, benchmark[1], function(err, res) {
|
||||
if (err) console.log(err);
|
||||
|
||||
t.equal(res.equal, true, `${moduleName} module works correctly when the option is changed to ${options[1][moduleName]}`);
|
||||
t.equal(res.equal, true, `${moduleName} module works correctly when the option is changed to ${JSON.stringify(options[1])}`);
|
||||
sequencer = null;
|
||||
t.end();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -9,7 +9,7 @@ test('convolve works with 1x1 array', t => {
|
||||
[1, 1, 1]
|
||||
],
|
||||
expectedOut = [
|
||||
[9]
|
||||
new Float32Array([9])
|
||||
];
|
||||
|
||||
const out = convolve([array], kernel);
|
||||
@@ -34,10 +34,10 @@ test('convolve works with 3x4 array', t => {
|
||||
[1, 1, 1]
|
||||
],
|
||||
expectedOut = [
|
||||
[12, 19, 26],
|
||||
[13, 20, 27],
|
||||
[13, 20, 27],
|
||||
[13, 19, 25]
|
||||
new Float32Array([12, 19, 26]),
|
||||
new Float32Array([13, 20, 27]),
|
||||
new Float32Array([13, 20, 27]),
|
||||
new Float32Array([13, 19, 25])
|
||||
];
|
||||
|
||||
const out = convolve([array], kernel);
|
||||
@@ -68,16 +68,16 @@ test('convolve works with multiple 3x4 arrays', t => {
|
||||
[1, 1, 1]
|
||||
],
|
||||
expectedOut1 = [
|
||||
[12, 19, 26],
|
||||
[13, 20, 27],
|
||||
[13, 20, 27],
|
||||
[13, 19, 25]
|
||||
new Float32Array([12, 19, 26]),
|
||||
new Float32Array([13, 20, 27]),
|
||||
new Float32Array([13, 20, 27]),
|
||||
new Float32Array([13, 19, 25])
|
||||
],
|
||||
expectedOut2 = [
|
||||
[14, 19, 24],
|
||||
[12, 13, 14],
|
||||
[15, 12, 9],
|
||||
[16, 13, 10]
|
||||
new Float32Array([14, 19, 24]),
|
||||
new Float32Array([12, 13, 14]),
|
||||
new Float32Array([15, 12, 9]),
|
||||
new Float32Array([16, 13, 10])
|
||||
];
|
||||
|
||||
const out = convolve([array1, array2], kernel);
|
||||
|
||||
@@ -6,10 +6,12 @@ describe('HTML Types Mapping Function', function() {
|
||||
expect(mapHtmlTypes({type: 'percentage'})).toEqual({type: 'number'});
|
||||
|
||||
expect(mapHtmlTypes({type: 'integer'})).toEqual({type: 'number'});
|
||||
expect(mapHtmlTypes({type: 'integer', min: 20, max: 100})).toEqual({type: 'range', min: 20, max: 100});
|
||||
expect(mapHtmlTypes({type: 'integer', min: 20, max: 100})).toEqual({type: 'range', min: 20, max: 100, step: 1});
|
||||
expect(mapHtmlTypes({type: 'float', min: 20, max: 100})).toEqual({type: 'range', min: 20, max: 100, step: 0.1}); // should default to step = 1
|
||||
|
||||
expect(mapHtmlTypes({type: 'float'})).toEqual({type: 'text'});
|
||||
expect(mapHtmlTypes({type: 'float', min: 20, max: 100})).toEqual({type: 'range', min: 20, max: 100});
|
||||
expect(mapHtmlTypes({type: 'float', min: 20, max: 100})).toEqual({type: 'range', min: 20, max: 100, step: 0.1});
|
||||
expect(mapHtmlTypes({type: 'float', min: 20, max: 100})).toEqual({type: 'range', min: 20, max: 100, step: 0.1}); // should default to step = 0.1
|
||||
});
|
||||
|
||||
it('maps text type', function() {
|
||||
@@ -21,4 +23,4 @@ describe('HTML Types Mapping Function', function() {
|
||||
expect(mapHtmlTypes({type: 'select'})).toEqual({type: 'select'});
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user