First commit.
@@ -0,0 +1,23 @@
|
||||
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
||||
|
||||
# dependencies
|
||||
/node_modules
|
||||
/.pnp
|
||||
.pnp.js
|
||||
|
||||
# testing
|
||||
/coverage
|
||||
|
||||
# production
|
||||
/build
|
||||
|
||||
# misc
|
||||
.DS_Store
|
||||
.env.local
|
||||
.env.development.local
|
||||
.env.test.local
|
||||
.env.production.local
|
||||
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
@@ -0,0 +1,142 @@
|
||||
|
||||
|
||||
find_program(NPM_COMMAND npm)
|
||||
|
||||
|
||||
if (CMAKE_BUILD_TYPE MATCHES Debug)
|
||||
set(BUILD_REACT "echo")
|
||||
else()
|
||||
set(BUILD_REACT ${NPM_COMMAND})
|
||||
endif()
|
||||
|
||||
set (BUILD_DIRECTORY ${PROJECT_SOURCE_DIR}/build/react/build)
|
||||
message(STATUS "React Build...")
|
||||
|
||||
add_custom_command(
|
||||
OUTPUT ${BUILD_DIRECTORY}/index.html
|
||||
COMMAND ${BUILD_REACT}
|
||||
ARGS run build
|
||||
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}/react
|
||||
|
||||
DEPENDS
|
||||
src/JackServerSettingsDialog.tsx
|
||||
src/JackServerSettings.tsx
|
||||
|
||||
src/AboutDialog.tsx
|
||||
src/App.test.tsx
|
||||
src/App.tsx
|
||||
src/Banks.tsx
|
||||
src/ControlViewFactory.tsx
|
||||
src/DraggableGrid.tsx
|
||||
src/Draggable.tsx
|
||||
src/FullScreenIME.tsx
|
||||
src/GxTunerControl.tsx
|
||||
src/IControlViewFactory.tsx
|
||||
src/index.tsx
|
||||
src/JackHostStatus.tsx
|
||||
src/JackStatusView.tsx
|
||||
src/Jack.tsx
|
||||
src/LoadPluginDialogBackup.tsx
|
||||
src/LoadPluginDialog.tsx
|
||||
src/Lv2Plugin.tsx
|
||||
src/MainPage.tsx
|
||||
src/ObservableProperty.tsx
|
||||
src/PedalBoard.tsx
|
||||
src/PedalBoardView.tsx
|
||||
src/Pedal.tsx
|
||||
src/PiPedalError.tsx
|
||||
src/PiPedalModel.tsx
|
||||
src/PiPedalSocket.tsx
|
||||
src/PluginClass.tsx
|
||||
src/PluginControl.tsx
|
||||
src/PluginControlView.tsx
|
||||
src/PluginIcon.tsx
|
||||
src/PluginInfoDialog.tsx
|
||||
src/PresetDialog.tsx
|
||||
src/PresetSelector.tsx
|
||||
src/Rect.tsx
|
||||
src/RenameDialog.tsx
|
||||
src/ResizeResponsiveComponent.tsx
|
||||
src/SelectChannelsDialog.tsx
|
||||
src/SelectHoverBackground.tsx
|
||||
src/SelectMidiChannelsDialog.tsx
|
||||
src/SettingsDialog.tsx
|
||||
src/SplitControlView.tsx
|
||||
src/SplitUiControls.tsx
|
||||
src/StringBuilder.tsx
|
||||
src/SvgPathBuilder.tsx
|
||||
src/TemporaryDrawer.tsx
|
||||
src/ToobCabSimView.tsx
|
||||
src/ToobFrequencyResponseView.tsx
|
||||
src/ToobInputStageView.tsx
|
||||
src/ToobToneStackView.tsx
|
||||
src/Units.tsx
|
||||
src/Utility.tsx
|
||||
src/VuMeter.tsx
|
||||
src/PluginPreset.tsx
|
||||
|
||||
public/logo512.png
|
||||
public/index.html
|
||||
public/manifest.json
|
||||
public/var
|
||||
public/var/current_pedalboard.json
|
||||
public/var/uiplugins.json
|
||||
public/var/config.json
|
||||
public/var/plugin_classes.json
|
||||
public/logo192.png
|
||||
public/serve.json
|
||||
public/robots.txt
|
||||
public/sample_lv2_plugins.json
|
||||
public/logo192x.png
|
||||
public/img/fx_analyzer.svg
|
||||
public/img/fx_flanger.svg
|
||||
public/img/cx_stereo.svg
|
||||
public/img/fx_split_a.svg
|
||||
public/img/fx_dial.svg
|
||||
public/img/drive_file_rename_outline_black_24dp.svg
|
||||
public/img/fx_filter.svg
|
||||
public/img/cx_mono.svg
|
||||
public/img/fx_limiter.svg
|
||||
public/img/fx_converter.svg
|
||||
public/img/fx_simulator.svg
|
||||
public/img/fx_delay.svg
|
||||
public/img/fx_reverb.svg
|
||||
public/img/fx_empty.svg
|
||||
public/img/fx_eq.svg
|
||||
public/img/edit_banks.svg
|
||||
public/img/fx_gate.svg
|
||||
public/img/settings_black_24dp.svg
|
||||
public/img/help_outline_black_24dp.svg
|
||||
public/img/fx_oscillator.svg
|
||||
public/img/fx_split_b.svg
|
||||
public/img/fx_phaser.svg
|
||||
public/img/fx_amplifier.svg
|
||||
public/img/fx_plugin.svg
|
||||
public/img/ic_drawer.svg
|
||||
public/img/fx_terminal.svg
|
||||
public/img/fx_spatial.svg
|
||||
public/img/fx_pitch.svg
|
||||
public/img/ic_logo.svg
|
||||
public/img/fx_flanger2.svg
|
||||
public/img/fx_function.svg
|
||||
public/img/fx_lr.svg
|
||||
public/img/fx_generator.svg
|
||||
public/img/fx_spectral.svg
|
||||
public/img/fx_chorus.svg
|
||||
public/img/fx_utility.svg
|
||||
public/img/fx_mixer.svg
|
||||
public/img/ic_presets.svg
|
||||
public/img/fx_instrument.svg
|
||||
public/img/fx_modulator.svg
|
||||
public/img/fx_compressor.svg
|
||||
public/img/fx_constant.svg
|
||||
public/img/fx_distortion.svg
|
||||
public/img/save_bank_as.svg
|
||||
public/favicon.ico
|
||||
|
||||
)
|
||||
|
||||
add_custom_target (
|
||||
ReactBuild ALL
|
||||
DEPENDS ${BUILD_DIRECTORY}/index.html
|
||||
)
|
||||
@@ -0,0 +1,46 @@
|
||||
# Getting Started with Create React App
|
||||
|
||||
This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app).
|
||||
|
||||
## Available Scripts
|
||||
|
||||
In the project directory, you can run:
|
||||
|
||||
### `npm start`
|
||||
|
||||
Runs the app in the development mode.\
|
||||
Open [http://localhost:3000](http://localhost:3000) to view it in the browser.
|
||||
|
||||
The page will reload if you make edits.\
|
||||
You will also see any lint errors in the console.
|
||||
|
||||
### `npm test`
|
||||
|
||||
Launches the test runner in the interactive watch mode.\
|
||||
See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information.
|
||||
|
||||
### `npm run build`
|
||||
|
||||
Builds the app for production to the `build` folder.\
|
||||
It correctly bundles React in production mode and optimizes the build for the best performance.
|
||||
|
||||
The build is minified and the filenames include the hashes.\
|
||||
Your app is ready to be deployed!
|
||||
|
||||
See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information.
|
||||
|
||||
### `npm run eject`
|
||||
|
||||
**Note: this is a one-way operation. Once you `eject`, you can’t go back!**
|
||||
|
||||
If you aren’t satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project.
|
||||
|
||||
Instead, it will copy all the configuration files and the transitive dependencies (webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you’re on your own.
|
||||
|
||||
You don’t have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn’t feel obligated to use this feature. However we understand that this tool wouldn’t be useful if you couldn’t customize it when you are ready for it.
|
||||
|
||||
## Learn More
|
||||
|
||||
You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started).
|
||||
|
||||
To learn React, check out the [React documentation](https://reactjs.org/).
|
||||
@@ -0,0 +1,45 @@
|
||||
{
|
||||
"name": "my-app",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@material-ui/core": "^4.11.4",
|
||||
"@material-ui/icons": "^4.11.2",
|
||||
"@testing-library/jest-dom": "^5.14.1",
|
||||
"@testing-library/react": "^11.2.7",
|
||||
"@testing-library/user-event": "^12.8.3",
|
||||
"@types/jest": "^26.0.23",
|
||||
"@types/node": "^12.20.15",
|
||||
"@types/react": "^17.0.11",
|
||||
"@types/react-dom": "^17.0.8",
|
||||
"react": "^17.0.2",
|
||||
"react-dom": "^17.0.2",
|
||||
"react-scripts": "4.0.3",
|
||||
"typescript": "^4.3.4",
|
||||
"web-vitals": "^1.1.2"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "react-scripts start",
|
||||
"build": "BUILD_PATH='../../build/react/src/build' react-scripts build",
|
||||
"test": "react-scripts test",
|
||||
"eject": "react-scripts eject"
|
||||
},
|
||||
"eslintConfig": {
|
||||
"extends": [
|
||||
"react-app",
|
||||
"react-app/jest"
|
||||
]
|
||||
},
|
||||
"browserslist": {
|
||||
"production": [
|
||||
">0.2%",
|
||||
"not dead",
|
||||
"not op_mini all"
|
||||
],
|
||||
"development": [
|
||||
"last 1 chrome version",
|
||||
"last 1 firefox version",
|
||||
"last 1 safari version"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 15 KiB |
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
width="48px" height="48px" viewBox="0 0 48 48" enable-background="new 0 0 48 48" xml:space="preserve">
|
||||
<line fill="none" stroke="#000000" stroke-width="2" x1="0" y1="24" x2="48" y2="24"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 563 B |
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
width="48px" height="48px" viewBox="0 0 48 48" enable-background="new 0 0 48 48" xml:space="preserve">
|
||||
<rect y="21" width="48" height="6"/>
|
||||
<rect y="23" fill="#FFFFFF" width="48" height="2"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 568 B |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24" width="24px" fill="#000000"><path d="M0 0h24v24H0V0z" fill="none"/><path d="M6 19c0 1.1.9 2 2 2h8c1.1 0 2-.9 2-2V7H6v12zM8 9h8v10H8V9zm7.5-5l-1-1h-5l-1 1H5v2h14V4h-3.5z"/></svg>
|
||||
|
After Width: | Height: | Size: 251 B |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" enable-background="new 0 0 24 24" height="24px" viewBox="0 0 24 24" width="24px" fill="#000000"><g><rect fill="none" height="24" width="24"/></g><g><g><polygon points="15,16 11,20 21,20 21,16"/><path d="M12.06,7.19L3,16.25V20h3.75l9.06-9.06L12.06,7.19z M5.92,18H5v-0.92l7.06-7.06l0.92,0.92L5.92,18z"/><path d="M18.71,8.04c0.39-0.39,0.39-1.02,0-1.41l-2.34-2.34C16.17,4.09,15.92,4,15.66,4c-0.25,0-0.51,0.1-0.7,0.29l-1.83,1.83 l3.75,3.75L18.71,8.04z"/></g></g></svg>
|
||||
|
After Width: | Height: | Size: 503 B |
@@ -0,0 +1,20 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
width="24px" height="24px" viewBox="0 0 24 24" enable-background="new 0 0 24 24" xml:space="preserve">
|
||||
<rect x="6" y="9" width="2" height="2"/>
|
||||
<rect x="6" y="13" width="2" height="2"/>
|
||||
<rect x="6" y="17" width="2" height="2"/>
|
||||
<rect x="10" y="17" width="3" height="2"/>
|
||||
<g>
|
||||
<g>
|
||||
<path d="M17.059,5.19l-7.061,7.06V16h3.75l7.061-7.061L17.059,5.19z M12.918,14h-0.92v-0.92l5.061-5.061l0.92,0.92L12.918,14z"/>
|
||||
<path d="M23.708,6.04c0.39-0.39,0.39-1.02,0-1.41l-2.34-2.34c-0.2-0.2-0.45-0.29-0.71-0.29c-0.25,0-0.51,0.1-0.7,0.29l-1.83,1.83
|
||||
l3.75,3.75L23.708,6.04z"/>
|
||||
</g>
|
||||
</g>
|
||||
<path d="M15,16.641V21H4v-9V7h5h1h3.672l1.904-1.904C15.393,5.04,15.202,5,15,5h-4H8H4C2.9,5,2.01,5.9,2.01,7l-0.003,3.993L2,11v10
|
||||
c0,1.1,0.891,2,1.99,2H4h11h0.01c1.1,0,1.99-0.9,1.99-2v-6.359L15,16.641z"/>
|
||||
<polygon points="11.672,9 10,9 10,10.672 "/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.2 KiB |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" enable-background="new 0 0 24 24" height="24px" viewBox="0 0 24 24" width="24px" fill="#000000"><g><rect fill="none" height="24" width="24"/></g><g><path d="M18,15v3H6v-3H4v3c0,1.1,0.9,2,2,2h12c1.1,0,2-0.9,2-2v-3H18z M17,11l-1.41-1.41L13,12.17V4h-2v8.17L8.41,9.59L7,11l5,5 L17,11z"/></g></svg>
|
||||
|
After Width: | Height: | Size: 333 B |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" enable-background="new 0 0 24 24" height="24px" viewBox="0 0 24 24" width="24px" fill="#000000"><g><rect fill="none" height="24" width="24"/></g><g><path d="M18,15v3H6v-3H4v3c0,1.1,0.9,2,2,2h12c1.1,0,2-0.9,2-2v-3H18z M7,9l1.41,1.41L11,7.83V16h2V7.83l2.59,2.58L17,9l-5-5L7,9z"/></g></svg>
|
||||
|
After Width: | Height: | Size: 327 B |
@@ -0,0 +1,20 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="48px"
|
||||
height="48px" viewBox="0 0 48 48" enable-background="new 0 0 48 48" xml:space="preserve">
|
||||
<g id="Layer_1">
|
||||
<g>
|
||||
<path d="M47,13V7c0-3.313-2.687-6-6-6H7C3.687,1,1,3.687,1,7v6H47z M24.186,4.766c1.524,0,2.761,1.236,2.761,2.76
|
||||
c0,1.525-1.236,2.761-2.761,2.761s-2.761-1.236-2.761-2.761C21.425,6.002,22.661,4.766,24.186,4.766z M16.497,4.766
|
||||
c1.525,0,2.761,1.236,2.761,2.76c0,1.525-1.236,2.761-2.761,2.761c-1.524,0-2.76-1.236-2.76-2.761
|
||||
C13.736,6.002,14.972,4.766,16.497,4.766z M8.808,4.766c1.525,0,2.761,1.236,2.761,2.76c0,1.525-1.236,2.761-2.761,2.761
|
||||
c-1.524,0-2.76-1.236-2.76-2.761C6.047,6.002,7.283,4.766,8.808,4.766z"/>
|
||||
<path d="M1,16v25c0,3.313,2.687,6,6,6h34c3.313,0,6-2.687,6-6V16H1z M24.394,43.261c-6.613,0-11.975-5.361-11.975-11.975
|
||||
s5.361-11.975,11.975-11.975s11.975,5.361,11.975,11.975S31.007,43.261,24.394,43.261z"/>
|
||||
</g>
|
||||
<circle cx="24.394" cy="31.286" r="3.993"/>
|
||||
</g>
|
||||
<g id="Combined">
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.3 KiB |
@@ -0,0 +1,16 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="48px"
|
||||
height="48px" viewBox="0 0 48 48" enable-background="new 0 0 48 48" xml:space="preserve">
|
||||
<g id="Layer_1" display="none">
|
||||
<path display="inline" d="M47,41c0,3.313-2.687,6-6,6H7c-3.313,0-6-2.687-6-6V7c0-3.313,2.687-6,6-6h34c3.313,0,6,2.687,6,6V41z"/>
|
||||
<line display="inline" fill="none" stroke="#FFFFFF" stroke-width="5" x1="23.5" y1="10" x2="23.5" y2="22"/>
|
||||
<line display="inline" fill="none" stroke="#FFFFFF" stroke-width="5" x1="43" y1="13.546" x2="19.938" y2="53.489"/>
|
||||
<line display="inline" fill="none" stroke="#FFFFFF" stroke-width="5" x1="5" y1="13.545" x2="11.904" y2="25.504"/>
|
||||
</g>
|
||||
<g id="Combined">
|
||||
<path d="M41,1H7C3.687,1,1,3.687,1,7v34c0,3.313,2.687,6,6,6h13.798l20.037-34.704l4.33,2.5L26.571,47H41c3.313,0,6-2.687,6-6V7
|
||||
C47,3.687,44.313,1,41,1z M9.739,26.754L2.835,14.795l4.33-2.5l6.904,11.958L9.739,26.754z M26,22h-5V10h5V22z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.2 KiB |
@@ -0,0 +1,72 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="48px"
|
||||
height="48px" viewBox="0 0 48 48" enable-background="new 0 0 48 48" xml:space="preserve">
|
||||
<g id="Layer_1" display="none">
|
||||
<circle cx="24" cy="23.999" r="23"/>
|
||||
<path display="inline" d="M47,41c0,3.313-2.687,6-6,6H7c-3.313,0-6-2.687-6-6V7c0-3.313,2.687-6,6-6h34c3.313,0,6,2.687,6,6V41z"/>
|
||||
<path id="full_sin_1_" display="inline" fill="none" stroke="#FFFFFF" stroke-width="3" stroke-linecap="round" d="M-6.638,31.942
|
||||
M-6.64,31.94c1.398,3.74,2.795,7.481,4.388,7.481 M2.129,31.94c-1.397,3.739-2.793,7.481-4.386,7.481 M2.128,31.94
|
||||
c1.397-3.74,2.795-7.481,4.387-7.481 M10.896,31.941C9.5,28.2,8.104,24.459,6.511,24.459 M10.896,31.942
|
||||
c1.398,3.741,2.795,7.481,4.385,7.481 M19.665,31.942c-1.398,3.741-2.795,7.481-4.387,7.481 M19.665,31.943
|
||||
c1.396-3.741,2.792-7.481,4.385-7.481 M28.432,31.943c-1.397-3.741-2.794-7.481-4.386-7.481 M28.432,31.941
|
||||
c1.397,3.74,2.792,7.481,4.386,7.481 M37.197,31.941c-1.396,3.74-2.792,7.481-4.386,7.481 M37.197,31.941
|
||||
c1.399-3.74,2.796-7.48,4.387-7.48 M45.966,31.942c-1.396-3.741-2.794-7.481-4.385-7.481 M45.966,31.941
|
||||
c1.396,3.74,2.793,7.481,4.389,7.481 M54.733,31.941c-1.396,3.74-2.791,7.481-4.385,7.481 M54.733,31.942
|
||||
c1.398-3.741,2.793-7.481,4.387-7.481 M63.501,31.942c-1.398-3.741-2.793-7.481-4.387-7.481 M63.501,31.94
|
||||
c1.395,3.74,2.793,7.481,4.387,7.481 M72.269,31.94c-1.395,3.739-2.793,7.481-4.385,7.481 M72.269,31.94
|
||||
c1.396-3.74,2.791-7.481,4.385-7.481 M81.038,31.941c-1.398-3.741-2.797-7.482-4.387-7.482 M81.038,31.942
|
||||
c1.396,3.741,2.795,7.481,4.385,7.481 M89.806,31.942c-1.396,3.741-2.795,7.481-4.385,7.481 M89.806,31.943"/>
|
||||
<path id="full_sin_2_" display="inline" fill="none" stroke="#FFFFFF" stroke-width="3" stroke-linecap="round" d="M-4.638,16.942
|
||||
M-4.64,16.94c1.398,3.74,2.795,7.481,4.388,7.481 M4.129,16.94c-1.397,3.739-2.793,7.481-4.386,7.481 M4.128,16.94
|
||||
c1.397-3.74,2.795-7.481,4.387-7.481 M12.896,16.941C11.5,13.2,10.104,9.459,8.511,9.459 M12.896,16.942
|
||||
c1.398,3.741,2.795,7.481,4.385,7.481 M21.665,16.942c-1.398,3.741-2.795,7.481-4.387,7.481 M21.665,16.943
|
||||
c1.396-3.741,2.792-7.481,4.385-7.481 M30.432,16.943c-1.397-3.741-2.794-7.481-4.386-7.481 M30.432,16.941
|
||||
c1.397,3.74,2.792,7.481,4.386,7.481 M39.197,16.941c-1.396,3.74-2.792,7.481-4.386,7.481 M39.197,16.941
|
||||
c1.399-3.74,2.796-7.48,4.387-7.48 M47.966,16.942c-1.396-3.741-2.794-7.481-4.385-7.481 M47.966,16.941
|
||||
c1.396,3.74,2.793,7.481,4.389,7.481 M56.733,16.941c-1.396,3.74-2.791,7.481-4.385,7.481 M56.733,16.942
|
||||
c1.398-3.741,2.793-7.481,4.387-7.481 M65.501,16.942c-1.398-3.741-2.793-7.481-4.387-7.481 M65.501,16.94
|
||||
c1.395,3.74,2.793,7.481,4.387,7.481 M74.269,16.94c-1.395,3.739-2.793,7.481-4.385,7.481 M74.269,16.94
|
||||
c1.396-3.74,2.791-7.481,4.385-7.481 M83.038,16.941c-1.398-3.741-2.797-7.482-4.387-7.482 M83.038,16.942
|
||||
c1.396,3.741,2.795,7.481,4.385,7.481 M91.806,16.942c-1.396,3.741-2.795,7.481-4.385,7.481 M91.806,16.943"/>
|
||||
</g>
|
||||
<g id="Composed">
|
||||
<g>
|
||||
<polygon points="2.15,32 2.148,31.998 2.147,32 "/>
|
||||
<path d="M35.904,31.415C37.664,26.709,38.952,23,41.573,23c0.002,0,0.005,0,0.01,0h0.001c0.003,0,0.006,0,0.008,0
|
||||
c2.533,0,3.745,3.462,5.408,7.938V18.679c-0.141-0.374-0.283-0.753-0.428-1.141c-0.004-0.01-0.008-0.056-0.012-0.066
|
||||
c-0.801-2.146-2.11-5.649-2.978-6.419c-0.864,0.768-2.173,4.273-2.974,6.413c-0.002,0.006-0.004,0.032-0.007,0.039
|
||||
C38.845,22.216,37.455,26,34.834,26c-0.006,0-0.014,0-0.02,0c-0.008,0-0.014,0-0.02,0c-2.613,0-4-3.763-5.752-8.451
|
||||
c-0.006-0.014-0.011-0.067-0.017-0.081l-0.002-0.004c-0.801-2.146-2.11-5.652-2.977-6.42c-0.868,0.768-2.177,4.277-2.978,6.424
|
||||
c-0.004,0.01-0.008,0.021-0.012,0.031c-1.759,4.705-3.15,8.418-5.773,8.425c-0.002,0-0.003-0.002-0.006,0
|
||||
c-0.002-0.002-0.004,0-0.006,0c-2.625-0.007-4.016-3.73-5.778-8.444c-0.004-0.011-0.008-0.021-0.011-0.032
|
||||
c-0.802-2.148-2.105-5.639-2.971-6.406c-0.866,0.769-2.177,4.277-2.979,6.423l-1.405-0.52l0.166,0.067l-0.147-0.045l1.378,0.559
|
||||
c-1.458,3.9-2.664,7.153-4.526,8.152v5.03c1.636-4.362,3.016-7.735,5.5-7.749c0.004,0,0.008,0.002,0.013,0
|
||||
c0.003,0.001,0.009,0,0.013,0c2.625,0.014,4.016,3.742,5.775,8.458c0.004,0.01,0.007,0.02,0.011,0.029
|
||||
c0.802,2.145,1.978,5.631,2.84,6.396C16.017,37.076,17,33.576,18,31.434v0.005c0-0.003,0.13,0.003,0.132-0.001
|
||||
C19.891,26.723,21.409,23,24.035,23c0.004,0,0.007,0,0.013,0c0.002,0,0.009-0.019,0.013-0.019
|
||||
c2.615,0.014,4.005,3.699,5.759,8.393c0.006,0.014,0.011,0.029,0.017,0.043l0.009,0.023c0.799,2.139,2.209,5.636,3.072,6.403
|
||||
C33.785,37.074,35,33.568,36,31.422v0.004C36,31.419,35.902,31.422,35.904,31.415z"/>
|
||||
<polygon points="2.11,32 2.108,31.999 2.108,32 "/>
|
||||
<path d="M44.572,32.497c-0.004-0.01-0.008-0.02-0.012-0.03c-0.801-2.146-2.11-5.654-2.978-6.424
|
||||
c-0.864,0.768-2.173,4.264-2.974,6.403c-0.002,0.007-0.004,0.013-0.007,0.02c-1.758,4.711-3.147,8.436-5.769,8.457
|
||||
c-0.006,0-0.014,0.002-0.02,0c-0.008,0.002-0.014,0.001-0.02,0.001c-2.613-0.021-4-3.959-5.752-8.646
|
||||
C27.037,32.264,27.032,32,27.026,32h-0.002c-0.801-2-2.111-5.413-2.977-6.181c-0.867,0.768-2.176,4.403-2.977,6.55
|
||||
c-0.004,0.01-0.008,0.099-0.012,0.109C19.3,37.183,17.908,41,15.286,41c-0.002,0-0.005,0-0.006,0
|
||||
c-0.004,0-0.004-0.037-0.006-0.037c-2.625-0.007-4.016-3.769-5.778-8.482c-0.004-0.01-0.008-0.02-0.011-0.03
|
||||
c-0.802-2.148-2.105-5.636-2.971-6.402c-0.866,0.769-2.177,4.282-2.979,6.429l-1.228-0.445l1.219,0.495
|
||||
C2.658,34.848,1.88,36.94,1,38.466V41c0,3.313,2.687,6,6,6h34c3.313,0,6-2.687,6-6v-2.769
|
||||
C46.159,36.735,45.406,34.731,44.572,32.497z"/>
|
||||
<path d="M2.716,16.453l0.011,0.004C4.487,11.748,5.879,8,8.5,8c0.004,0,0.008,0,0.013,0c0.003,0,0.009-0.02,0.013-0.02
|
||||
c2.625,0.014,4.016,3.721,5.775,8.438c0.004,0.01,0.007,0.02,0.011,0.03c0.802,2.145,1.978,5.631,2.84,6.397
|
||||
C18.017,22.077,19,18.577,20,16.433v0.005c0-0.004,0.13,0.003,0.132,0C21.891,11.722,23.409,8,26.035,8c0.004,0,0.007,0,0.013,0
|
||||
c0.004,0,0.009,0,0.013,0c2.615,0,4.019,3.699,5.772,8.393c0.006,0.014,0.024,0.009,0.03,0.023l0.034,0.023
|
||||
c0.799,2.139,2.157,5.634,3.021,6.401C35.785,22.072,37,18.563,38,16.417c0,0,0-0.001,0-0.001c0-0.007-0.098,0.006-0.096-0.001
|
||||
C39.664,11.709,40.952,8,43.573,8c0.002,0,0.005,0,0.01,0h0.001c0.003,0,0.006,0,0.008,0c1.464,0,2.485,1.156,3.408,2.995V7
|
||||
c0-3.313-2.687-6-6-6H7C3.687,1,1,3.687,1,7v13.737C1.619,19.383,2.252,17.693,2.716,16.453z"/>
|
||||
<polygon points="1,31.557 1,31.558 1.046,31.575 "/>
|
||||
<polygon points="4.11,17 4.108,17 4.108,17 "/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 6.6 KiB |
@@ -0,0 +1,20 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="48px"
|
||||
height="48px" viewBox="0 0 48 48" enable-background="new 0 0 48 48" xml:space="preserve">
|
||||
<g id="Layer_1" display="none">
|
||||
<path display="inline" d="M47,41c0,3.313-2.688,6-6,6H7c-3.313,0-6-2.688-6-6V7c0-3.313,2.687-6,6-6h34c3.313,0,6,2.687,6,6V41z"/>
|
||||
<polyline id="Limiter_Path" fill="none" stroke="#FFFFFF" stroke-width="5" points="5.5,53.25 24,12.5 65.5,12.5 "/>
|
||||
<path id="Compressor_Path" display="inline" fill="none" stroke="#FFFFFF" stroke-width="5" d="M8.5,53.25
|
||||
c0,0,15.445-34.126,16.146-35.565c1.929-3.964,2.947-5.192,8.477-5.186c1.39,0.001,35.378,0,35.378,0"/>
|
||||
</g>
|
||||
<g id="Composed">
|
||||
<g>
|
||||
<path d="M8.585,47c4.368-9.646,13.269-29.291,13.813-30.409C24.576,12.113,26.345,10,33.088,10c0.013,0,0.024,0,0.037,0
|
||||
C33.703,10,39.93,10,47,10V7c0-3.313-2.688-6-6-6H7C3.687,1,1,3.687,1,7v34c0,3.313,2.687,6,6,6H8.585z"/>
|
||||
<path d="M33.119,15c-0.021,0-0.043,0-0.063,0c-4.323,0-4.574,0.515-6.162,3.779C26.449,19.691,19.506,35.006,14.074,47H41
|
||||
c3.313,0,6-2.688,6-6V15C39.928,15,33.698,15,33.119,15z"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.4 KiB |
@@ -0,0 +1,28 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="48px"
|
||||
height="48px" viewBox="0 0 48 48" enable-background="new 0 0 48 48" xml:space="preserve">
|
||||
<g id="Layer_1" display="none">
|
||||
<path display="inline" d="M47,41c0,3.313-2.688,6-6,6H7c-3.313,0-6-2.688-6-6V7c0-3.313,2.687-6,6-6h34c3.313,0,6,2.687,6,6V41z"/>
|
||||
<text transform="matrix(1 0 0 1 9.8389 32.5723)" fill="#FFFFFF" font-family="'PalatinoLinotype-BoldItalic'" font-size="34">fx</text>
|
||||
|
||||
<text id="k" transform="matrix(1 0 0 1 14.5454 35.5723)" display="inline" fill="#FFFFFF" font-family="'PalatinoLinotype-BoldItalic'" font-size="34">k</text>
|
||||
</g>
|
||||
<g id="Composed">
|
||||
<path d="M41,1H7C3.687,1,1,3.687,1,7v34c0,3.313,2.687,6,6,6h34c3.313,0,6-2.688,6-6V7C47,3.687,44.313,1,41,1z M26.805,26.91
|
||||
c0.648,1.788,1.178,3.092,1.586,3.91c0.41,0.819,0.711,1.303,0.904,1.453c0.195,0.148,0.375,0.224,0.541,0.224
|
||||
c0.42,0,1.211-0.398,2.373-1.195l0.184,0.017l0.365,0.647l-0.051,0.232c-0.441,0.276-0.885,0.57-1.328,0.88
|
||||
c-2.113,1.472-3.529,2.208-4.25,2.208c-0.199,0-0.381-0.044-0.547-0.133s-0.369-0.302-0.607-0.64
|
||||
c-0.236-0.337-0.492-0.852-0.763-1.543c-0.271-0.692-0.681-1.777-1.229-3.254c-0.548-1.479-0.911-2.493-1.087-3.047
|
||||
c-0.62,0.643-1.054,1.148-1.303,1.52s-0.429,0.708-0.54,1.012c-0.111,0.305-0.26,0.922-0.448,1.852
|
||||
c-0.188,0.93-0.315,1.613-0.382,2.051c-0.066,0.437-0.1,0.882-0.1,1.336c-1.118,0.056-2.374,0.276-3.769,0.664l-0.282-0.415
|
||||
c0.93-3.243,1.887-7.509,2.872-12.8c0.985-5.29,1.478-8.19,1.478-8.699c0-0.266-0.075-0.465-0.224-0.598s-0.379-0.208-0.689-0.224
|
||||
c-0.31-0.017-0.896-0.03-1.76-0.042L17.6,12.177l0.116-0.714l0.149-0.149c2.413-0.299,4.665-0.819,6.757-1.561l0.414,0.365
|
||||
c-0.553,2.214-1.776,7.836-3.668,16.867c0.044-0.044,0.47-0.559,1.278-1.544c1.04-1.272,1.961-2.341,2.764-3.204
|
||||
c0.802-0.863,1.612-1.607,2.433-2.233c0.818-0.625,1.537-1.054,2.158-1.287c0.619-0.232,1.268-0.349,1.941-0.349
|
||||
c0.299,0,0.604,0.033,0.914,0.1l0.066,0.199c-0.332,1.118-0.537,1.959-0.615,2.523l-0.182,0.133
|
||||
c-0.654-0.21-1.156-0.315-1.512-0.315c-0.719,0-1.521,0.21-2.406,0.631c-0.887,0.421-1.738,1.107-2.557,2.059
|
||||
C25.773,24.053,26.158,25.123,26.805,26.91z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.4 KiB |
@@ -0,0 +1,19 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="48px"
|
||||
height="48px" viewBox="0 0 48 48" enable-background="new 0 0 48 48" xml:space="preserve">
|
||||
<g id="Layer_1" display="none">
|
||||
<path display="inline" d="M47,41c0,3.313-2.687,6-6,6H7c-3.313,0-6-2.687-6-6V7c0-3.313,2.687-6,6-6h34c3.313,0,6,2.687,6,6V41z"/>
|
||||
<line display="inline" fill="none" stroke="#FFFFFF" stroke-width="5" x1="-11" y1="18.5" x2="28" y2="18.5"/>
|
||||
<line display="inline" fill="none" stroke="#FFFFFF" stroke-width="5" x1="-18" y1="29.5" x2="28" y2="29.5"/>
|
||||
<polyline display="inline" fill="none" stroke="#FFFFFF" stroke-width="5" points="19.25,9.504 33.746,24 19.25,38.496 "/>
|
||||
</g>
|
||||
<g id="Combined">
|
||||
<g>
|
||||
<polygon points="30.211,24 27.211,21 1,21 1,27 27.211,27 "/>
|
||||
<path d="M41,1H7C3.687,1,1,3.687,1,7v9h21.21l-4.728-4.728l3.535-3.535L37.281,24L21.018,40.264l-3.535-3.535L22.211,32H1v9
|
||||
c0,3.313,2.687,6,6,6h34c3.313,0,6-2.687,6-6V7C47,3.687,44.313,1,41,1z"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.3 KiB |
@@ -0,0 +1,22 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="48px"
|
||||
height="48px" viewBox="0 0 48 48" enable-background="new 0 0 48 48" xml:space="preserve">
|
||||
<g id="Layer_1" display="none">
|
||||
<path display="inline" d="M47,41c0,3.313-2.687,6-6,6H7c-3.313,0-6-2.687-6-6V7c0-3.313,2.687-6,6-6h34c3.313,0,6,2.687,6,6V41z"/>
|
||||
<polyline display="inline" fill="none" stroke="#FFFFFF" stroke-width="3" stroke-miterlimit="10" points="-47,33.5 9,33.5
|
||||
11.353,22.952 15.154,40.249 19.336,33.5 83,33.5 "/>
|
||||
<polyline display="inline" fill="none" stroke="#FFFFFF" stroke-width="3" stroke-miterlimit="10" points="-27,20.5 29,20.5
|
||||
31.354,9.952 35.154,27.249 39.336,20.5 103,20.5 "/>
|
||||
</g>
|
||||
<g id="Combined">
|
||||
<g>
|
||||
<polygon points="31.343,16.89 30.202,22 12.68,22 15.826,36.317 18.501,32 47,32 47,22 40.171,22 34.482,31.182 "/>
|
||||
<path d="M10.698,19l0.666-2.986L12.02,19h15.778l3.566-15.986l4.462,20.303L38.501,19H47V7c0-3.313-2.687-6-6-6H7
|
||||
C3.687,1,1,3.687,1,7v12H10.698z"/>
|
||||
<path d="M20.171,35l-5.688,9.181l-3.14-14.29L10.202,35H1v6c0,3.313,2.687,6,6,6h34c3.313,0,6-2.687,6-6v-6H20.171z"/>
|
||||
<polygon points="7.798,32 10.028,22 1,22 1,32 "/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.4 KiB |
@@ -0,0 +1,14 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="48px"
|
||||
height="48px" viewBox="0 0 48 48" enable-background="new 0 0 48 48" xml:space="preserve">
|
||||
<g id="Layer_1">
|
||||
<path d="M31.5,2.262v8.767C35.976,13.628,39,18.462,39,24c0,8.271-6.729,15-15,15c-8.271,0-15-6.729-15-15
|
||||
c0-5.538,3.024-10.372,7.5-12.971V2.262C7.49,5.38,1,13.943,1,24c0,12.682,10.318,23,23,23c12.683,0,23-10.318,23-23
|
||||
C47,13.943,40.51,5.379,31.5,2.262z"/>
|
||||
<path d="M20,25.5h8V1.354C26.7,1.126,25.365,1,24,1s-2.7,0.126-4,0.354V25.5z"/>
|
||||
</g>
|
||||
<g id="Composed">
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 857 B |
@@ -0,0 +1,42 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="48px"
|
||||
height="48px" viewBox="0 0 48 48" enable-background="new 0 0 48 48" xml:space="preserve">
|
||||
<g id="Original" display="none">
|
||||
<circle display="inline" cx="24" cy="23.999" r="23"/>
|
||||
<path display="inline" fill="none" stroke="#FFFFFF" stroke-width="4" stroke-linecap="round" d="M-29.056,27.166 M-15.749,27.162
|
||||
c-0.524,2.555-1.05,5.108-1.578,7.51l-10.135,0.07c-0.534-2.42-1.064-4.999-1.594-7.58 M-2.44,27.164
|
||||
c-1.035-5.03-2.067-10.06-3.134-13.896l-7.039-0.01c-1.068,3.837-2.102,8.872-3.136,13.904 M-2.44,27.162 M-2.44,27.166
|
||||
M10.868,27.162c-0.524,2.555-1.05,5.108-1.579,7.51l-10.134,0.07c-0.534-2.42-1.063-4.999-1.595-7.58 M24.176,27.164
|
||||
c-1.034-5.031-2.066-10.06-3.133-13.896l-7.04-0.01c-1.067,3.837-2.101,8.872-3.136,13.904 M24.176,27.162 M24.176,27.166
|
||||
M37.483,27.162c-0.525,2.555-1.052,5.11-1.58,7.51l-10.132,0.07c-0.536-2.418-1.065-4.999-1.596-7.58 M50.791,27.164
|
||||
c-1.035-5.031-2.065-10.06-3.134-13.896l-7.04-0.01c-1.065,3.837-2.099,8.872-3.134,13.904 M50.791,27.162 M50.791,27.166
|
||||
M64.102,27.162c-0.524,2.555-1.052,5.11-1.581,7.51l-10.133,0.07c-0.536-2.418-1.064-4.999-1.597-7.58 M77.408,27.164
|
||||
c-1.037-5.031-2.066-10.06-3.137-13.896l-7.037-0.01c-1.063,3.837-2.1,8.872-3.133,13.904 M77.408,27.162 M77.408,27.166
|
||||
M90.714,27.162c-0.523,2.555-1.048,5.108-1.575,7.51l-10.137,0.07c-0.534-2.42-1.063-4.999-1.594-7.58 M104.024,27.164
|
||||
c-1.037-5.031-2.066-10.06-3.136-13.896l-7.039-0.01c-1.069,3.837-2.1,8.872-3.136,13.904 M104.024,27.162 M104.024,27.166
|
||||
M117.33,27.162c-0.521,2.555-1.048,5.108-1.574,7.51l-10.137,0.07c-0.536-2.42-1.063-4.999-1.595-7.58 M130.641,27.164
|
||||
c-1.037-5.031-2.065-10.06-3.137-13.896l-7.037-0.01c-1.067,3.837-2.1,8.872-3.137,13.904 M130.641,27.162 M130.641,27.166
|
||||
M143.947,27.162c-0.524,2.555-1.052,5.112-1.58,7.51l-10.133,0.07c-0.534-2.416-1.065-4.999-1.594-7.58 M157.253,27.164
|
||||
c-1.032-5.031-2.063-10.06-3.132-13.896l-7.037-0.01c-1.071,3.837-2.101,8.872-3.137,13.904 M157.253,27.162 M157.253,27.166
|
||||
M170.563,27.162c-0.519,2.555-1.052,5.112-1.578,7.51l-10.133,0.07c-0.535-2.416-1.067-4.999-1.599-7.58 M183.869,27.164
|
||||
c-1.033-5.031-2.062-10.06-3.133-13.896l-7.037-0.01c-1.066,3.837-2.1,8.872-3.137,13.904 M183.869,27.162"/>
|
||||
</g>
|
||||
<g id="Combined">
|
||||
<g>
|
||||
<path d="M7.674,32.683c0.413-1.925,0.824-3.923,1.234-5.922c0-0.003,0.001-0.005,0.001-0.008l0.02-0.094
|
||||
c1.017-4.945,2.069-10.059,3.147-13.937l0.408-1.467l10.079,0.015l0.406,1.462c1.079,3.879,2.13,8.994,3.146,13.94
|
||||
c0.003,0.012,0.005,0.022,0.007,0.034c0.004,0.018,0.008,0.035,0.012,0.053l0.038,0.183c0.401,1.956,0.803,3.909,1.207,5.789
|
||||
l7.147-0.049C34.939,30.759,35,28.759,36,26.759c0-0.002,0-0.004,0-0.006l-0.212-0.12c1.015-4.936,1.946-10.039,3.021-13.91
|
||||
l0.349-1.467l4.022,0.006C39.058,5.077,32.006,1,24.015,1C11.312,1,1.007,11.296,1.007,23.999c0,3.087,0.609,6.029,1.711,8.719
|
||||
L7.674,32.683z"/>
|
||||
<path d="M42.153,15.261c-0.907,3.534-1.809,7.919-2.685,12.178l-0.026,0.127c-0.001,0.002-0.001,0.005-0.002,0.007
|
||||
c-0.526,2.562-1.054,5.124-1.584,7.529l-0.343,1.56l-13.345,0.092l-0.35-1.578c-0.525-2.37-1.045-4.896-1.564-7.427l-0.029-0.143
|
||||
c-0.003-0.013-0.006-0.025-0.008-0.039l-0.018-0.088c-0.878-4.271-1.782-8.669-2.692-12.212l-3.967-0.005
|
||||
c-0.91,3.541-1.814,7.936-2.692,12.204l-0.021,0.101c0,0.002-0.001,0.004-0.001,0.006c-0.525,2.563-1.053,5.123-1.583,7.53
|
||||
l-0.343,1.56l-6.071,0.042C8.947,42.908,15.994,47,24,47c12.703,0,23-10.297,23-23.001c0-3.093-0.614-6.041-1.722-8.734
|
||||
L42.153,15.261z"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.8 KiB |
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
width="48px" height="48px" viewBox="0 0 48 48" enable-background="new 0 0 48 48" xml:space="preserve">
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 477 B |
@@ -0,0 +1,20 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="48px"
|
||||
height="48px" viewBox="0 0 48 48" enable-background="new 0 0 48 48" xml:space="preserve">
|
||||
<g id="Layer_1" display="none">
|
||||
<path display="inline" d="M47,41c0,3.313-2.687,6-6,6H7c-3.313,0-6-2.687-6-6V7c0-3.313,2.687-6,6-6h34c3.313,0,6,2.687,6,6V41z"/>
|
||||
<path display="inline" fill="none" stroke="#FFFFFF" stroke-width="5" d="M-5,12.5c0,0,11.333,0.167,21,0
|
||||
c6.754-0.116,11.413,19.888,18.167,20c10,0.167,24.833,0,24.833,0"/>
|
||||
</g>
|
||||
<g id="Combined">
|
||||
<g>
|
||||
<path d="M15.957,10c4.848-0.12,8.015,5.42,11.34,11.248c1.981,3.473,4.976,8.72,6.912,8.752c3.898,0.066,8.564,0.08,12.791,0.072
|
||||
V7c0-3.313-2.687-6-6-6H7C3.687,1,1,3.687,1,7v3.057C5.137,10.082,10.806,10.089,15.957,10z"/>
|
||||
<path d="M45.021,35.073c-3.692,0-7.569-0.018-10.896-0.073c-4.784-0.08-7.887-5.518-11.171-11.273
|
||||
C20.97,20.249,17.975,15,16.061,15c-0.006,0-0.012,0-0.018,0C10.861,15,5.165,15,1,15.001V41c0,3.313,2.687,6,6,6h34
|
||||
c3.313,0,6-2.687,6-6v-5.929C46.349,35.072,45.689,35.073,45.021,35.073z"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.3 KiB |
@@ -0,0 +1,20 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="48px"
|
||||
height="48px" viewBox="0 0 48 48" enable-background="new 0 0 48 48" xml:space="preserve">
|
||||
<g id="Layer_1" display="none">
|
||||
<path display="inline" d="M47,41c0,3.313-2.687,6-6,6H7c-3.313,0-6-2.687-6-6V7c0-3.313,2.687-6,6-6h34c3.313,0,6,2.687,6,6V41z"/>
|
||||
<path display="inline" fill="none" stroke="#FFFFFF" stroke-width="5" d="M-7,20.5c0,0,6.632,0,14.601,0
|
||||
c7.815,0,10.993-10.467,13.093-10.467c3.335,0,5.959,26.467,36.474,26.467c10.001,0,24.833,0,24.833,0"/>
|
||||
</g>
|
||||
<g id="Combined">
|
||||
<g>
|
||||
<path d="M7.601,18c3.835,0,6.455-3.792,8.367-6.561c1.507-2.182,2.698-3.906,4.726-3.906c2.401,0,3.453,2.191,5.047,5.507
|
||||
c2.885,6.005,7.812,16.245,21.26,19.743V7c0-3.313-2.687-6-6-6H7C3.687,1,1,3.687,1,7v11H7.601z"/>
|
||||
<path d="M21.233,15.206c-0.235-0.49-0.494-1.028-0.74-1.518c-0.143,0.204-0.283,0.407-0.412,0.593
|
||||
C17.829,17.542,14.059,23,7.601,23H1v18c0,3.313,2.687,6,6,6h34c3.313,0,6-2.687,6-6v-3.056
|
||||
C30.383,34.247,24.371,21.74,21.233,15.206z"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.3 KiB |
@@ -0,0 +1,72 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
width="48px" height="48px" viewBox="0 0 48 48" enable-background="new 0 0 48 48" xml:space="preserve">
|
||||
<circle display="none" cx="24" cy="23.999" r="23"/>
|
||||
<path d="M47,41c0,3.313-2.687,6-6,6H7c-3.313,0-6-2.687-6-6V7c0-3.313,2.687-6,6-6h34c3.313,0,6,2.687,6,6V41z"/>
|
||||
<path fill="#FFFFFF" d="M87.423,64.516L87.423,64.516l-0.002-0.001c-0.002-0.001-0.004-0.001-0.006-0.002
|
||||
c-2.628-0.964-4.021-5.2-5.782-10.562c-0.004-0.012-0.007-0.022-0.011-0.033c-0.804-2.443-2.107-6.399-2.971-7.479
|
||||
c-0.864,0.451-2.172,3.476-2.972,5.327l0,0c-0.002,0.004-0.004,0.009-0.006,0.014c-1.759,4.075-3.148,7.297-5.775,6.354
|
||||
c-0.004-0.001-0.009-0.003-0.013-0.004c-0.006-0.003-0.009-0.004-0.013-0.005c-2.621-0.968-4.011-5.178-5.761-10.51
|
||||
c-0.006-0.017-0.011-0.031-0.017-0.048l-0.009-0.028c-0.8-2.429-2.105-6.399-2.97-7.479c-0.864,0.451-2.17,3.472-2.97,5.318
|
||||
l-1.414-1.016l0.05,0.036l-0.021-0.015l1.37,1.028c-1.756,4.063-3.144,7.274-5.762,6.344c-0.006-0.003-0.013-0.003-0.02-0.007
|
||||
c-0.007-0.001-0.013-0.005-0.02-0.008c-2.616-0.974-4.007-5.188-5.76-10.521c-0.004-0.012-0.008-0.023-0.012-0.035
|
||||
c-0.801-2.438-2.11-6.422-2.978-7.508c-0.864,0.453-2.173,3.473-2.974,5.321l0,0c-0.002,0.006-0.004,0.011-0.007,0.017
|
||||
c-1.758,4.071-3.147,7.291-5.769,6.357c-0.006-0.002-0.014-0.002-0.02-0.007c-0.008-0.001-0.014-0.005-0.02-0.007
|
||||
c-2.613-0.973-4-5.181-5.752-10.506c-0.006-0.016-0.011-0.032-0.017-0.049L29.024,34.8c-0.801-2.437-2.11-6.421-2.978-7.503
|
||||
c-0.867,0.451-2.176,3.484-2.977,5.34c-0.004,0.009-0.008,0.018-0.012,0.027c-1.759,4.063-3.15,7.271-5.773,6.323
|
||||
c-0.002-0.001-0.005-0.004-0.006-0.002c-0.004-0.004-0.004-0.002-0.006-0.003c-2.625-0.962-4.016-5.193-5.778-10.548
|
||||
c-0.004-0.012-0.008-0.023-0.011-0.035c-0.802-2.44-2.105-6.406-2.971-7.487c-0.866,0.453-2.177,3.482-2.979,5.337l-1.405-1.034
|
||||
l0.166,0.123l-0.147-0.109l1.378,1.042c-1.757,4.062-3.146,7.272-5.764,6.337c-0.006-0.002-0.011-0.003-0.017-0.006
|
||||
c-0.005,0-0.01-0.004-0.017-0.006C-2.89,31.625-4.28,27.4-6.039,22.057c-0.29-0.882,0.101-1.611,0.876-1.619
|
||||
c0.774-0.01,1.637,0.691,1.927,1.572l0.006,0.02c0.8,2.432,2.109,6.406,2.974,7.49c0.864-0.453,2.171-3.474,2.97-5.322l1.412,1.018
|
||||
L4.108,25.2l0.002,0.001l-1.386-1.024c1.762-4.073,3.153-7.292,5.776-6.353c0.004,0.003,0.008,0.006,0.013,0.006
|
||||
c0.003,0.002,0.009,0.003,0.013,0.004c2.625,0.971,4.016,5.203,5.775,10.561c0.004,0.011,0.007,0.021,0.011,0.033
|
||||
c0.802,2.436,2.106,6.396,2.968,7.476c0.865-0.452,2.175-3.479,2.976-5.33h0c0.001-0.002,0.002-0.006,0.003-0.008
|
||||
c1.759-4.076,3.149-7.297,5.775-6.355c0.004,0.001,0.007,0.002,0.013,0.005c0.003-0.001,0.009,0.003,0.013,0.005
|
||||
c2.615,0.966,4.006,5.175,5.76,10.508c0.006,0.017,0.011,0.032,0.017,0.048l0.009,0.027c0.799,2.43,2.104,6.399,2.969,7.481
|
||||
c0.867-0.452,2.177-3.485,2.978-5.34v-0.001l1.405,1.037l-1.405-1.037c0.003-0.006,0.006-0.012,0.008-0.018l0,0
|
||||
c1.761-4.065,3.152-7.277,5.773-6.333c0.002,0.001,0.005,0.001,0.01,0.003l0.001,0.001c0.003,0.001,0.006,0.002,0.008,0.003
|
||||
c2.621,0.964,4.012,5.183,5.768,10.524c0.004,0.012,0.008,0.022,0.012,0.034l0.001,0.003c0.802,2.439,2.111,6.425,2.979,7.506
|
||||
c0.865-0.452,2.172-3.476,2.972-5.327l1.411,1.023l-0.071-0.052l0.043,0.03l-1.367-1.035c1.756-4.059,3.144-7.267,5.76-6.336
|
||||
c0.006,0.003,0.014,0.004,0.02,0.008c0.008,0.001,0.014,0.005,0.02,0.007c2.613,0.973,4,5.179,5.753,10.505
|
||||
c0.006,0.017,0.012,0.033,0.017,0.05c0.801,2.438,2.109,6.425,2.979,7.509c0.869-0.452,2.178-3.484,2.979-5.341
|
||||
c0.002-0.004,0.004-0.009,0.006-0.015l0,0c1.76-4.07,3.149-7.287,5.776-6.338c0.002,0.001,0.004,0.002,0.006,0.002l0.001,0.001
|
||||
h0.001c0.002,0.001,0.004,0.002,0.006,0.003c2.625,0.962,4.02,5.199,5.784,10.562c0.004,0.013,0.008,0.023,0.011,0.035
|
||||
c0.803,2.442,2.104,6.395,2.968,7.475c0.868-0.453,2.178-3.484,2.979-5.339c0.29-0.672,1.154-0.75,1.93-0.179
|
||||
c0.776,0.572,1.171,1.579,0.881,2.25c-1.762,4.079-3.154,7.302-5.782,6.353C87.427,64.517,87.425,64.516,87.423,64.516z
|
||||
M21.665,31.6l1.405,1.037L21.665,31.6z M72.863,49.708l1.405,1.036L72.863,49.708z"/>
|
||||
<path fill="#FFFFFF" d="M89.423,50.243L89.423,50.243l-0.002-0.001c-0.002,0-0.004-0.001-0.006-0.002
|
||||
c-2.628-0.963-4.021-5.199-5.782-10.562c-0.004-0.011-0.007-0.022-0.011-0.034c-0.804-2.441-2.107-6.398-2.971-7.477
|
||||
c-0.864,0.45-2.172,3.476-2.972,5.326l0,0c-0.002,0.004-0.004,0.01-0.006,0.014c-1.759,4.076-3.148,7.298-5.775,6.355
|
||||
c-0.004-0.002-0.009-0.004-0.013-0.005c-0.006-0.002-0.009-0.003-0.013-0.005c-2.621-0.968-4.011-5.178-5.761-10.51
|
||||
c-0.006-0.016-0.011-0.031-0.017-0.047l-0.009-0.028c-0.8-2.429-2.105-6.399-2.97-7.48c-0.864,0.452-2.17,3.472-2.97,5.319
|
||||
l-1.414-1.017l0.05,0.036l-0.021-0.014l1.37,1.027c-1.756,4.063-3.144,7.275-5.762,6.344c-0.006-0.002-0.014-0.005-0.02-0.007
|
||||
c-0.008-0.003-0.013-0.005-0.02-0.007c-2.616-0.975-4.007-5.188-5.76-10.522c-0.004-0.011-0.008-0.022-0.012-0.034
|
||||
c-0.801-2.438-2.11-6.423-2.978-7.508c-0.864,0.453-2.173,3.473-2.974,5.32l0,0c-0.002,0.006-0.004,0.012-0.007,0.018
|
||||
c-1.758,4.071-3.147,7.29-5.769,6.357c-0.006-0.002-0.014-0.005-0.02-0.007c-0.008-0.003-0.014-0.005-0.02-0.008
|
||||
c-2.613-0.972-4-5.18-5.752-10.505c-0.006-0.017-0.011-0.033-0.017-0.049l-0.002-0.005c-0.801-2.438-2.11-6.42-2.977-7.504
|
||||
c-0.868,0.452-2.177,3.485-2.978,5.341c-0.004,0.008-0.008,0.018-0.012,0.026c-1.759,4.064-3.15,7.271-5.773,6.323
|
||||
c-0.002,0-0.003-0.003-0.006-0.002c-0.002-0.002-0.004-0.001-0.006-0.002c-2.625-0.962-4.016-5.193-5.778-10.548
|
||||
c-0.004-0.013-0.008-0.024-0.011-0.036c-0.802-2.44-2.105-6.406-2.971-7.487c-0.866,0.453-2.177,3.483-2.979,5.338l-1.405-1.034
|
||||
l0.166,0.122l-0.147-0.108l1.378,1.041c-1.757,4.063-3.146,7.272-5.764,6.338c-0.006-0.003-0.01-0.003-0.017-0.007
|
||||
c-0.004,0.001-0.011-0.004-0.017-0.006C-0.89,17.353-2.28,13.129-4.039,7.784c-0.29-0.882,0.101-1.612,0.876-1.619
|
||||
c0.776-0.009,1.637,0.691,1.927,1.573l0.006,0.018c0.8,2.433,2.109,6.408,2.974,7.491c0.864-0.453,2.171-3.474,2.97-5.322
|
||||
l1.412,1.019l-0.02-0.015L6.11,10.93L4.724,9.904C6.485,5.831,7.877,2.612,10.5,3.553c0.004,0.002,0.008,0.002,0.013,0.005
|
||||
c0.003-0.001,0.009,0.003,0.013,0.005c2.625,0.97,4.016,5.203,5.775,10.56c0.004,0.011,0.007,0.022,0.011,0.033
|
||||
c0.802,2.437,2.106,6.396,2.968,7.477c0.865-0.453,2.175-3.479,2.976-5.331l0,0.001c0.001-0.003,0.002-0.006,0.003-0.009
|
||||
c1.759-4.076,3.148-7.297,5.775-6.355c0.004,0.002,0.007,0.002,0.013,0.005c0.004,0.001,0.009,0.003,0.013,0.005
|
||||
c2.615,0.966,4.006,5.176,5.76,10.509c0.006,0.017,0.011,0.032,0.017,0.048l0.009,0.026c0.799,2.43,2.104,6.4,2.969,7.481
|
||||
c0.867-0.453,2.177-3.484,2.978-5.34c0,0,0,0,0-0.001l1.405,1.037l-1.405-1.037c0.003-0.006,0.006-0.012,0.008-0.018l0,0
|
||||
c1.761-4.065,3.152-7.276,5.773-6.333c0.002,0.001,0.005,0.002,0.01,0.004h0.001c0.003,0.001,0.006,0.002,0.008,0.003
|
||||
c2.621,0.964,4.012,5.183,5.768,10.524c0.004,0.012,0.008,0.023,0.012,0.035l0.001,0.003c0.802,2.438,2.111,6.424,2.979,7.505
|
||||
c0.865-0.451,2.172-3.476,2.972-5.326l1.411,1.023l-0.071-0.053l0.043,0.031l-1.367-1.036c1.756-4.059,3.144-7.267,5.76-6.335
|
||||
c0.006,0.002,0.014,0.005,0.02,0.007c0.008,0.003,0.014,0.005,0.02,0.007c2.613,0.973,4,5.18,5.753,10.506
|
||||
c0.006,0.017,0.012,0.033,0.017,0.05c0.801,2.438,2.109,6.425,2.979,7.508c0.869-0.452,2.178-3.484,2.979-5.34
|
||||
c0.002-0.005,0.004-0.01,0.006-0.015l0,0c1.76-4.071,3.149-7.287,5.776-6.338c0.002,0,0.004,0.001,0.006,0.002h0.001l0.001,0.001
|
||||
c0.002,0,0.004,0.001,0.006,0.002c2.625,0.962,4.02,5.199,5.784,10.562c0.004,0.013,0.008,0.024,0.011,0.035
|
||||
c0.803,2.441,2.104,6.395,2.968,7.476c0.868-0.454,2.178-3.485,2.979-5.34c0.29-0.673,1.156-0.749,1.93-0.179
|
||||
c0.776,0.572,1.171,1.58,0.881,2.25c-1.762,4.08-3.154,7.303-5.782,6.353C89.427,50.244,89.425,50.244,89.423,50.243z
|
||||
M23.665,17.328l1.405,1.037L23.665,17.328z M74.863,35.436l1.405,1.037L74.863,35.436z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 7.8 KiB |
@@ -0,0 +1,194 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="48px"
|
||||
height="48px" viewBox="0 0 48 48" enable-background="new 0 0 48 48" xml:space="preserve">
|
||||
<g id="Original" display="none">
|
||||
<circle display="inline" cx="24" cy="23.999" r="23"/>
|
||||
<path display="inline" fill="#FFFFFF" d="M70.464,39.718c-0.002,0-0.002,0-0.004,0c-0.006-0.002-0.012-0.003-0.017-0.003
|
||||
c-2.013-0.42-2.751-3.039-4.492-10.979c-0.002-0.009-0.004-0.016-0.005-0.024c-0.614-2.793-1.485-6.752-2.235-8.506
|
||||
c-0.751,1.455-1.621,5.067-2.233,7.609l-1.143-0.348l-1.131-0.396c1.745-7.239,2.48-9.554,4.498-9.159c0.002,0,0.006,0,0.008,0.001
|
||||
h0.001h0.001c0.003,0.001,0.005,0.001,0.008,0.002c2.021,0.411,2.756,3.027,4.503,10.982c0.002,0.006,0.004,0.016,0.005,0.022
|
||||
c0.614,2.794,1.483,6.755,2.234,8.506c0.75-1.451,1.618-5.06,2.231-7.6c0.001-0.012,0.003-0.021,0.007-0.034
|
||||
c1.748-7.271,2.485-9.587,4.514-9.182c0.637,0.127,1.153,0.613,1.153,1.088c0,0.376-0.325,0.631-0.776,0.657
|
||||
c-0.821,0.709-1.898,5.186-2.618,8.18l-1.135-0.372l1.135,0.372c-0.001,0-0.001,0-0.001,0c-0.001,0.007-0.003,0.016-0.005,0.022
|
||||
h0.002c-1.74,7.228-2.478,9.548-4.487,9.164C70.476,39.722,70.47,39.72,70.464,39.718z M16.458,28.942c-0.001,0-0.002,0-0.004,0
|
||||
c-0.004-0.002-0.008-0.002-0.012-0.003c-2.013-0.414-2.75-3.023-4.488-10.939c-0.003-0.015-0.008-0.028-0.01-0.043
|
||||
c-0.613-2.795-1.486-6.772-2.239-8.529c-0.752,1.456-1.625,5.081-2.238,7.628l-2.272-0.743c1.745-7.248,2.481-9.57,4.498-9.178
|
||||
c0.005,0,0.01,0.003,0.013,0.002c0.001,0.001,0.007,0.001,0.012,0.003c2.015,0.413,2.752,3.022,4.491,10.94
|
||||
c0.003,0.015,0.007,0.028,0.011,0.043c0.613,2.794,1.485,6.77,2.239,8.528c0.753-1.457,1.625-5.086,2.237-7.636
|
||||
c0.002-0.005,0.003-0.01,0.004-0.015c1.749-7.259,2.483-9.571,4.51-9.167C23.24,9.841,23.27,9.848,23.3,9.856
|
||||
c1.955,0.466,2.695,3.113,4.413,10.937c0.002,0.009,0.004,0.016,0.006,0.023c0.613,2.794,1.485,6.77,2.239,8.527
|
||||
c0.753-1.458,1.626-5.084,2.239-7.634c0-0.001,0-0.001,0-0.002c1.741-7.244,2.479-9.569,4.49-9.185
|
||||
c0.007,0.001,0.012,0.001,0.02,0.004c0.004-0.002,0.015,0.002,0.021,0.003c2.011,0.42,2.747,3.036,4.488,10.964
|
||||
c0.001,0.006,0.002,0.011,0.003,0.016c0.613,2.796,1.486,6.771,2.239,8.527c0.753-1.456,1.626-5.083,2.239-7.633
|
||||
c0.001-0.006,0.003-0.011,0.004-0.016c1.745-7.259,2.482-9.572,4.51-9.167c0.024,0.004,0.048,0.01,0.068,0.015
|
||||
c1.974,0.455,2.712,3.096,4.439,10.956c0.002,0.011,0.004,0.02,0.005,0.028c0.614,2.794,1.484,6.755,2.235,8.51
|
||||
c0.752-1.457,1.624-5.081,2.236-7.629l2.272,0.743c-1.742,7.246-2.479,9.567-4.495,9.179c-0.004,0-0.008-0.001-0.014-0.001
|
||||
c0-0.002-0.01-0.002-0.015-0.004c-2.017-0.416-2.754-3.033-4.496-10.98c-0.002-0.009-0.006-0.019-0.007-0.028
|
||||
c-0.613-2.789-1.482-6.745-2.232-8.497c-0.753,1.457-1.626,5.085-2.239,7.634c-0.001,0.005-0.003,0.01-0.004,0.016
|
||||
c-1.742,7.246-2.48,9.563-4.497,9.17c-0.004-0.003-0.006-0.003-0.01-0.003h-0.001l-0.001-0.001c-0.003,0-0.005,0-0.01-0.002
|
||||
c-2.017-0.411-2.754-3.021-4.497-10.963c0-0.005-0.001-0.01-0.003-0.017l-0.003-0.013c-0.613-2.79-1.485-6.759-2.238-8.515
|
||||
c-0.752,1.456-1.624,5.082-2.237,7.633c0,0,0,0,0,0.001c-1.744,7.247-2.48,9.571-4.494,9.186c-0.006-0.002-0.007,0.002-0.018-0.004
|
||||
c-0.006,0.002-0.012-0.003-0.017-0.003c-2.013-0.418-2.751-3.031-4.49-10.955c-0.002-0.008-0.004-0.016-0.006-0.023
|
||||
c-0.613-2.794-1.485-6.771-2.238-8.526c-0.752,1.455-1.623,5.075-2.236,7.622l0,0c-0.001,0.003-0.003,0.007-0.003,0.01
|
||||
c-1.744,7.252-2.48,9.576-4.497,9.188C16.467,28.944,16.463,28.944,16.458,28.942z M-37.549,18.168c-0.001,0-0.001,0-0.002-0.001
|
||||
c-0.003-0.001-0.005-0.001-0.009-0.002c-2.018-0.411-2.754-3.023-4.499-10.97c-0.002-0.008-0.004-0.016-0.005-0.022
|
||||
c-0.613-2.795-1.484-6.765-2.236-8.519c-0.753,1.456-1.626,5.083-2.24,7.633l-1.136-0.372l1.136,0.372
|
||||
c-0.002,0.008-0.004,0.017-0.007,0.025h0.001c-1.741,7.233-2.477,9.55-4.49,9.162c-0.005,0-0.008-0.002-0.015-0.003
|
||||
c-0.004-0.003-0.01-0.002-0.015-0.003c-2.015-0.415-2.752-3.032-4.498-10.981c-0.001-0.009-0.004-0.018-0.005-0.026
|
||||
c-0.613-2.792-1.482-6.749-2.233-8.499c-0.752,1.456-1.625,5.082-2.238,7.63l0,0c0,0.002-0.001,0.003-0.001,0.004
|
||||
c-1.744,7.25-2.48,9.574-4.497,9.185c-0.005-0.001-0.007-0.003-0.014-0.003c-0.004-0.003-0.01-0.002-0.015-0.003
|
||||
c-2.014-0.415-2.752-3.028-4.493-10.963c-0.001-0.005-0.003-0.011-0.004-0.016l-0.001-0.005c-0.613-2.793-1.486-6.768-2.238-8.523
|
||||
c-0.752,1.457-1.625,5.08-2.237,7.627l-2.273-0.743c1.745-7.249,2.481-9.569,4.498-9.178c0.005,0,0.011,0.001,0.012,0.002
|
||||
c0.003,0,0.009,0.001,0.014,0.002c2.016,0.414,2.753,3.026,4.495,10.964c0.002,0.006,0.003,0.011,0.004,0.017
|
||||
c0.613,2.795,1.486,6.77,2.239,8.528c0.752-1.458,1.625-5.086,2.238-7.635c0.001-0.005,0.002-0.009,0.004-0.014
|
||||
c1.747-7.26,2.482-9.573,4.509-9.168c0.022,0.004,0.044,0.009,0.066,0.014c1.976,0.451,2.715,3.091,4.443,10.964
|
||||
c0.002,0.01,0.004,0.019,0.006,0.028c0.613,2.793,1.483,6.749,2.233,8.502c0.75-1.454,1.621-5.068,2.233-7.609
|
||||
c0.002-0.008,0.004-0.017,0.005-0.025c1.745-7.253,2.483-9.576,4.5-9.185c0.004,0.001,0.008,0.002,0.013,0.002
|
||||
c0,0,0.008,0.002,0.013,0.002c2.017,0.415,2.754,3.031,4.498,10.981c0.002,0.008,0.003,0.015,0.005,0.023
|
||||
c0.613,2.792,1.483,6.753,2.234,8.507c0.753-1.456,1.626-5.085,2.239-7.635c0.001-0.005,0.003-0.01,0.004-0.015
|
||||
c1.746-7.259,2.483-9.572,4.51-9.167c0.031,0.006,0.062,0.014,0.092,0.021c1.955,0.468,2.695,3.115,4.412,10.938
|
||||
c0.002,0.008,0.003,0.015,0.005,0.023c0.613,2.794,1.486,6.77,2.239,8.527c0.751-1.452,1.621-5.062,2.233-7.61
|
||||
c0.002-0.009,0.003-0.017,0.005-0.025c1.746-7.253,2.482-9.575,4.499-9.185c0.004,0,0.007,0,0.013,0.002
|
||||
c-0.002,0,0.007,0.002,0.012,0.003c2.016,0.414,2.752,3.027,4.497,10.97c0.002,0.006,0.004,0.015,0.005,0.021
|
||||
c0.613,2.794,1.484,6.762,2.236,8.517c0.754-1.456,1.626-5.084,2.24-7.634c0.001-0.005,0.001-0.01,0.003-0.015
|
||||
c1.747-7.259,2.483-9.572,4.509-9.168C-3.773,4.45-3.751,4.455-3.729,4.46c1.976,0.45,2.714,3.09,4.442,10.963
|
||||
c0.001,0.002,0.001,0.003,0.001,0.003c0.613,2.796,1.485,6.771,2.239,8.528c0.752-1.454,1.625-5.079,2.238-7.628l2.272,0.743
|
||||
c-1.746,7.252-2.482,9.572-4.5,9.179c-0.003-0.001-0.006-0.001-0.01-0.001c-0.002-0.002-0.006-0.002-0.009-0.004
|
||||
c-2.02-0.41-2.757-3.024-4.501-10.974c0-0.002,0-0.004-0.001-0.005l0-0.005c-0.614-2.793-1.486-6.766-2.239-8.523
|
||||
c-0.753,1.457-1.625,5.085-2.239,7.635c-0.001,0.006-0.002,0.01-0.004,0.015c-1.74,7.236-2.477,9.557-4.491,9.17
|
||||
c-0.006,0-0.012,0.002-0.017-0.003c-0.011,0.001-0.012-0.002-0.017-0.003c-2.015-0.417-2.751-3.037-4.495-10.979
|
||||
c-0.002-0.008-0.003-0.015-0.005-0.022c-0.613-2.79-1.483-6.753-2.234-8.506c-0.75,1.452-1.62,5.061-2.232,7.609
|
||||
c-0.002,0.009-0.004,0.017-0.006,0.025c-1.745,7.25-2.482,9.574-4.498,9.185c-0.004,0-0.008-0.003-0.014-0.002
|
||||
c-0.004-0.003-0.01-0.003-0.015-0.003c-2.015-0.416-2.751-3.027-4.492-10.956c-0.002-0.007-0.003-0.016-0.006-0.022
|
||||
c-0.613-2.795-1.485-6.771-2.238-8.528c-0.752,1.457-1.625,5.084-2.239,7.635c-0.001,0.005-0.003,0.009-0.003,0.014
|
||||
c-1.743,7.245-2.479,9.564-4.499,9.17C-37.543,18.168-37.547,18.168-37.549,18.168z M-62.314,2.853l1.137,0.372L-62.314,2.853z
|
||||
M-78.053,10.083c-0.001,0-0.001,0-0.001,0c-0.001-0.001-0.009-0.001-0.013-0.002c-2.017-0.414-2.753-3.028-4.497-10.973
|
||||
c-0.001-0.005-0.002-0.01-0.004-0.016c-0.613-2.793-1.485-6.763-2.237-8.517c-0.753,1.457-1.625,5.084-2.238,7.634
|
||||
c-0.107,0.446-0.698,0.642-1.331,0.437c-0.628-0.206-1.049-0.733-0.942-1.179c1.749-7.271,2.484-9.587,4.513-9.183
|
||||
c0.031,0.006,0.061,0.013,0.091,0.02c1.957,0.468,2.696,3.118,4.418,10.958c0.001,0.005,0.002,0.01,0.003,0.016
|
||||
c0.613,2.792,1.484,6.76,2.236,8.515c0.752-1.456,1.625-5.081,2.238-7.63l2.272,0.743c-1.744,7.251-2.481,9.572-4.498,9.181
|
||||
C-78.046,10.085-78.049,10.084-78.053,10.083z"/>
|
||||
<path display="inline" fill="#FFFFFF" d="M144.627,63.129c-0.002,0-0.002,0-0.004,0c-0.006-0.002-0.012-0.004-0.017-0.004
|
||||
c-2.012-0.419-2.747-3.033-4.487-10.955c-0.002-0.009-0.003-0.016-0.006-0.023c0-0.002,0-0.003,0-0.003
|
||||
c-0.613-2.795-1.486-6.77-2.239-8.527c-0.749,1.449-1.618,5.052-2.228,7.583h-0.004c-0.001,0.017-0.005,0.032-0.009,0.049
|
||||
c-1.744,7.258-2.48,9.58-4.501,9.186c-0.003-0.001-0.005-0.001-0.009-0.001h-0.001l-0.001-0.003c-0.002,0-0.005,0-0.007-0.001
|
||||
c-2.019-0.41-2.757-3.024-4.504-10.978c-0.003-0.017-0.007-0.033-0.009-0.05c-0.613-2.791-1.481-6.73-2.229-8.478
|
||||
c-0.753,1.456-1.626,5.085-2.241,7.633c-0.001,0.006-0.002,0.011-0.004,0.014c-1.739,7.246-2.478,9.565-4.496,9.171
|
||||
c-0.002-0.001-0.006-0.001-0.009-0.001l-0.001-0.002h-0.002c-0.002,0-0.005,0-0.008-0.002c-2.016-0.411-2.754-3.021-4.499-10.964
|
||||
c-0.001-0.004-0.002-0.01-0.003-0.016l-0.005-0.025c-0.613-2.787-1.483-6.75-2.234-8.504c-0.754,1.457-1.626,5.085-2.24,7.636
|
||||
c0,0.001,0,0.003-0.001,0.003c-1.742,7.241-2.479,9.564-4.489,9.183c-0.008-0.002-0.012-0.002-0.02-0.003
|
||||
c-0.005,0-0.013-0.004-0.019-0.006c-2.014-0.419-2.752-3.033-4.488-10.954c-0.001-0.007-0.004-0.016-0.005-0.022
|
||||
c-0.614-2.795-1.486-6.77-2.24-8.527c-0.752,1.458-1.625,5.086-2.24,7.634c-0.001,0.005-0.002,0.01-0.003,0.015
|
||||
c-1.74,7.247-2.479,9.563-4.497,9.17c-0.003,0-0.006-0.001-0.009-0.001H90.62h-0.001c-0.002,0-0.005-0.003-0.009-0.003
|
||||
c-2.019-0.41-2.755-3.028-4.502-10.981c-0.002-0.009-0.003-0.015-0.006-0.023c-0.613-2.793-1.484-6.755-2.235-8.507
|
||||
c-0.751,1.452-1.622,5.067-2.232,7.61l-0.001-0.002c-0.002,0.008-0.004,0.017-0.006,0.025c-1.744,7.258-2.479,9.58-4.5,9.186
|
||||
c-0.003-0.001-0.006-0.001-0.01-0.001l0,0l-0.001-0.003c-0.002,0-0.007,0-0.008-0.001c-2.021-0.411-2.759-3.021-4.497-10.951
|
||||
c-0.003-0.009-0.006-0.02-0.007-0.026L72.6,38.649c-0.611-2.786-1.482-6.748-2.233-8.502c-0.75,1.454-1.62,5.069-2.232,7.61
|
||||
l-1.143-0.35l0.039,0.013l-0.021-0.005l1.113,0.389c-1.739,7.226-2.476,9.545-4.487,9.163c-0.006-0.002-0.014-0.003-0.019-0.005
|
||||
c-0.007,0-0.013-0.002-0.02-0.004c-2.014-0.419-2.749-3.036-4.488-10.963c-0.001-0.003-0.002-0.009-0.002-0.016
|
||||
c-0.616-2.795-1.488-6.77-2.241-8.528c-0.752,1.457-1.625,5.081-2.239,7.632c0,0,0,0.002-0.001,0.002
|
||||
c-1.74,7.246-2.477,9.571-4.493,9.188c-0.006-0.001-0.013-0.003-0.019-0.006c-0.007,0-0.012-0.002-0.019-0.003
|
||||
c-2.011-0.42-2.747-3.031-4.486-10.951v-0.002c-0.002-0.008-0.004-0.018-0.006-0.023c-0.613-2.795-1.486-6.771-2.239-8.528
|
||||
c-0.753,1.457-1.626,5.085-2.239,7.635c-0.001,0.005-0.002,0.011-0.003,0.015c-1.745,7.246-2.481,9.563-4.5,9.17
|
||||
c-0.002-0.002-0.006-0.002-0.007-0.002h-0.001l-0.002-0.001c-0.003,0-0.006,0-0.008,0c-2.019-0.413-2.754-3.025-4.5-10.972
|
||||
c-0.002-0.007-0.003-0.017-0.005-0.024c-0.612-2.793-1.484-6.762-2.236-8.518c-0.753,1.457-1.627,5.084-2.239,7.635l-1.136-0.372
|
||||
l1.136,0.372c-0.002,0.008-0.005,0.017-0.008,0.025h0.002c-1.741,7.232-2.478,9.549-4.491,9.16c-0.004,0-0.01-0.003-0.015-0.001
|
||||
c-0.006-0.006-0.009-0.004-0.014-0.004c-2.016-0.415-2.752-3.034-4.499-10.981c-0.001-0.009-0.002-0.017-0.004-0.026
|
||||
c-0.613-2.793-1.483-6.749-2.233-8.499c-0.753,1.455-1.625,5.082-2.238,7.63l0,0c-0.001,0.001-0.001,0.003-0.001,0.005
|
||||
c-1.745,7.25-2.481,9.573-4.497,9.185c-0.004-0.001-0.01,0-0.015-0.003c-0.006,0-0.01-0.003-0.015-0.003
|
||||
c-2.014-0.415-2.751-3.029-4.493-10.964C5.102,25.216,5.1,25.21,5.099,25.205l-0.001-0.006c-0.613-2.793-1.485-6.768-2.238-8.523
|
||||
c-0.753,1.456-1.624,5.081-2.237,7.629l-2.273-0.744c1.745-7.248,2.48-9.568,4.497-9.177c0.005,0,0.014,0.003,0.013,0.002
|
||||
c0.005,0.001,0.008,0.002,0.013,0.002c2.016,0.413,2.752,3.026,4.496,10.965c0.001,0.006,0.002,0.011,0.004,0.015
|
||||
c0.613,2.795,1.485,6.773,2.238,8.529c0.753-1.457,1.625-5.085,2.239-7.635c0.001-0.004,0.003-0.009,0.003-0.013
|
||||
c1.748-7.261,2.484-9.574,4.509-9.169c0.023,0.004,0.045,0.009,0.067,0.014c1.977,0.451,2.715,3.09,4.443,10.965
|
||||
c0.003,0.011,0.004,0.019,0.005,0.029c0.613,2.793,1.484,6.748,2.234,8.499c0.751-1.453,1.621-5.067,2.234-7.607
|
||||
c0.001-0.009,0.003-0.017,0.004-0.025c1.746-7.254,2.483-9.575,4.5-9.185c0.005,0.002,0.007,0,0.013,0.002
|
||||
c-0.001-0.002,0.009,0.003,0.013,0.003c2.018,0.414,2.754,3.03,4.499,10.982c0.002,0.007,0.003,0.014,0.004,0.021
|
||||
c0.613,2.79,1.484,6.754,2.235,8.507c0.751-1.455,1.623-5.078,2.236-7.625h0.001c0-0.003,0.001-0.005,0.002-0.009
|
||||
c1.749-7.271,2.484-9.587,4.513-9.183c0.032,0.007,0.063,0.013,0.093,0.021c1.955,0.468,2.694,3.114,4.411,10.938
|
||||
c0.002,0.008,0.004,0.016,0.005,0.021l0.008,0.027c0.61,2.785,1.48,6.748,2.231,8.501c0.755-1.455,1.626-5.082,2.239-7.632
|
||||
c0,0,0.001-0.001,0.001-0.003c1.747-7.257,2.482-9.578,4.501-9.185c0.004,0.002,0.006,0.002,0.01,0.002l0.001,0.001h0.001
|
||||
c0.004,0.001,0.006,0.001,0.009,0.002c2.018,0.411,2.754,3.023,4.498,10.966c0.001,0.007,0.004,0.011,0.005,0.016
|
||||
c0.613,2.794,1.484,6.771,2.238,8.528c0.751-1.454,1.621-5.068,2.233-7.611l1.142,0.347l-0.036-0.011l0.018,0.007l-1.111-0.389
|
||||
c1.746-7.253,2.481-9.565,4.508-9.159c0.023,0.003,0.047,0.008,0.069,0.015c1.97,0.452,2.709,3.09,4.432,10.937
|
||||
c0.004,0.011,0.007,0.02,0.008,0.028c0.613,2.797,1.484,6.773,2.239,8.53c0.753-1.456,1.626-5.087,2.238-7.637
|
||||
c0.002-0.009,0.003-0.017,0.006-0.023l0,0c1.743-7.24,2.479-9.555,4.498-9.161c0.002,0.002,0.005,0.002,0.007,0.002h0.001h0.003
|
||||
c0.002,0.001,0.004,0.001,0.008,0.003c2.019,0.411,2.755,3.026,4.502,10.98c0.003,0.009,0.004,0.017,0.005,0.023
|
||||
c0.614,2.795,1.484,6.755,2.235,8.507c0.753-1.454,1.625-5.085,2.237-7.634c0.002-0.006,0.004-0.01,0.005-0.015
|
||||
c1.746-7.26,2.484-9.571,4.51-9.167c0.03,0.005,0.061,0.013,0.09,0.02c1.955,0.467,2.696,3.112,4.415,10.938
|
||||
c0.003,0.009,0.005,0.018,0.006,0.023c0.612,2.796,1.483,6.769,2.238,8.528c0.752-1.458,1.625-5.083,2.238-7.629
|
||||
c0-0.002,0-0.004,0.002-0.005c1.74-7.246,2.477-9.571,4.491-9.188c0.007,0.003,0.012,0.001,0.02,0.005
|
||||
c0.006,0,0.013,0.002,0.019,0.003c2.012,0.419,2.747,3.033,4.487,10.954c0.001,0.003,0.001,0.005,0.002,0.008
|
||||
c0.001,0.006,0.002,0.012,0.003,0.017c0.615,2.795,1.486,6.771,2.24,8.528c0.753-1.457,1.625-5.085,2.238-7.634
|
||||
c0.001-0.004,0.002-0.009,0.004-0.014c1.747-7.26,2.483-9.572,4.51-9.168c0.023,0.004,0.047,0.01,0.07,0.015
|
||||
c1.973,0.454,2.712,3.097,4.44,10.968c0.003,0.018,0.007,0.033,0.009,0.047c0.613,2.792,1.481,6.73,2.229,8.479
|
||||
c0.753-1.458,1.625-5.085,2.239-7.636l0,0l1.135,0.372l-1.135-0.372c0.003-0.016,0.007-0.033,0.014-0.049h-0.004
|
||||
c1.744-7.219,2.478-9.528,4.492-9.136c0.002,0.002,0.007,0.002,0.009,0.002h0.001l0.001,0.002c0.003,0,0.006,0,0.009,0.002
|
||||
c2.019,0.41,2.754,3.026,4.502,10.979c0.002,0.009,0.003,0.017,0.006,0.025c0,0.002,0,0.002,0,0.002
|
||||
c0.612,2.785,1.482,6.748,2.233,8.504c0.751-1.455,1.621-5.07,2.233-7.612l0,0c0.002-0.006,0.004-0.014,0.007-0.022
|
||||
c1.744-7.271,2.481-9.588,4.513-9.182c0.638,0.126,1.152,0.613,1.152,1.087c0,0.376-0.324,0.631-0.775,0.657
|
||||
c-0.823,0.709-1.899,5.188-2.618,8.181c-0.001,0.007-0.005,0.016-0.006,0.022h0.001c-1.741,7.228-2.478,9.548-4.487,9.164
|
||||
C144.639,63.13,144.633,63.13,144.627,63.129z M39.988,32.022l1.136,0.372L39.988,32.022z M11.849,26.263l1.137,0.371
|
||||
L11.849,26.263z M-3.89,33.494c0,0-0.001-0.002-0.001-0.002c0.001,0.002-0.008-0.002-0.013-0.002
|
||||
c-2.017-0.414-2.753-3.027-4.498-10.973c-0.001-0.006-0.001-0.011-0.003-0.017c-0.613-2.792-1.485-6.762-2.237-8.517
|
||||
c-0.753,1.457-1.625,5.085-2.239,7.634c-0.107,0.447-0.7,0.645-1.331,0.437c-0.627-0.205-1.048-0.731-0.942-1.178
|
||||
c1.749-7.271,2.485-9.589,4.514-9.184c0.031,0.007,0.061,0.014,0.091,0.021c1.957,0.467,2.697,3.118,4.418,10.958
|
||||
c0.001,0.006,0.002,0.011,0.004,0.016c0.613,2.792,1.484,6.76,2.236,8.515c0.752-1.456,1.624-5.081,2.237-7.63l2.273,0.743
|
||||
c-1.745,7.251-2.48,9.571-4.498,9.181C-3.882,33.494-3.885,33.494-3.89,33.494z"/>
|
||||
</g>
|
||||
<g id="Combined">
|
||||
<g>
|
||||
<path d="M38.945,23.334c-0.613-2.79-1.485-6.759-2.238-8.515c-0.752,1.456-1.624,5.082-2.237,7.633c0,0,0,0,0,0.001
|
||||
c-0.391,1.622-0.729,2.984-1.044,4.138c0.288,1.193,0.598,2.567,0.948,4.166c0.002,0.007,0.003,0.014,0.004,0.021
|
||||
c0.613,2.79,1.484,6.754,2.235,8.507c0.751-1.455,1.623-5.078,2.236-7.625h0.001c0-0.003,0.001-0.005,0.002-0.009
|
||||
c0.39-1.622,0.729-2.984,1.043-4.138c-0.287-1.189-0.595-2.558-0.944-4.15c0-0.005-0.001-0.01-0.003-0.017L38.945,23.334z"/>
|
||||
<path d="M5.092,16.735c0.033-0.136,0.067-0.279,0.099-0.409l2.272,0.743c-0.389,1.617-0.727,2.977-1.042,4.128
|
||||
c0.288,1.19,0.596,2.561,0.946,4.155c0.001,0.006,0.002,0.011,0.004,0.015c0.613,2.795,1.485,6.773,2.238,8.529
|
||||
c0.753-1.457,1.625-5.085,2.239-7.635c0.001-0.004,0.003-0.009,0.003-0.013c0.389-1.615,0.726-2.973,1.04-4.124
|
||||
c-0.285-1.183-0.592-2.544-0.939-4.125c-0.003-0.015-0.008-0.028-0.01-0.043c-0.613-2.795-1.486-6.772-2.239-8.529
|
||||
c-0.752,1.456-1.625,5.081-2.238,7.628l-2.272-0.743c1.745-7.248,2.481-9.57,4.498-9.178c0.005,0,0.01,0.003,0.013,0.002
|
||||
c0.001,0.001,0.007,0.001,0.012,0.003c2.015,0.413,2.752,3.022,4.491,10.94c0.003,0.015,0.007,0.028,0.011,0.043
|
||||
c0.012,0.053,0.025,0.113,0.037,0.167c0.584-1.045,1.223-1.387,2.107-1.21c0.023,0.004,0.045,0.009,0.067,0.014
|
||||
c0.913,0.208,1.562,0.894,2.166,2.337c0.033-0.137,0.068-0.282,0.1-0.414c0.002-0.005,0.003-0.01,0.004-0.015
|
||||
c1.749-7.259,2.483-9.571,4.51-9.167c0.031,0.006,0.06,0.013,0.09,0.021c1.955,0.466,2.694,3.113,4.412,10.937
|
||||
c0.002,0.009,0.003,0.016,0.005,0.023c0.012,0.053,0.022,0.113,0.034,0.168c0.581-1.04,1.21-1.385,2.087-1.215
|
||||
c0.005,0.002-0.005,0,0.001,0.002c-0.001-0.002-0.016,0.003-0.012,0.003c0.941,0.193,1.555,0.878,2.172,2.353
|
||||
C32.031,21.989,32,21.843,32,21.71c0-0.001,0-0.001,0-0.002c2-7.244,2.577-9.569,4.589-9.185c0.007,0.001,0.11,0.001,0.118,0.004
|
||||
c0.004-0.002,0.015,0.002,0.021,0.003c2.011,0.42,2.747,3.036,4.488,10.964c0.001,0.006,0.002,0.011,0.003,0.016
|
||||
c0.012,0.054,0.025,0.115,0.037,0.17c0.585-1.045,1.224-1.387,2.108-1.211c0.032,0.007,0.063,0.013,0.093,0.021
|
||||
c0.899,0.215,1.541,0.901,2.14,2.331c0.033-0.138,0.068-0.284,0.101-0.417c0.001-0.006,0.003-0.011,0.004-0.016
|
||||
c0.377-1.566,0.705-2.889,1.011-4.018C44.973,9.393,35.469,1,24,1C14.707,1,6.707,6.514,3.079,14.446
|
||||
C3.911,14.694,4.523,15.375,5.092,16.735z"/>
|
||||
<path d="M42.244,27.984c0.402,1.631,0.824,3.142,1.215,4.053c0.359-0.695,0.746-1.892,1.119-3.224
|
||||
c-0.402-1.632-0.824-3.143-1.215-4.054C43.004,25.456,42.617,26.652,42.244,27.984z"/>
|
||||
<path d="M28.742,25.288c0.402,1.632,0.824,3.144,1.216,4.056c0.359-0.696,0.747-1.893,1.12-3.226
|
||||
c-0.402-1.632-0.825-3.144-1.216-4.056C29.503,22.759,29.115,23.956,28.742,25.288z"/>
|
||||
<path d="M4.074,20.726c-0.402-1.63-0.824-3.139-1.214-4.05c-0.36,0.697-0.748,1.896-1.121,3.23
|
||||
c0.401,1.629,0.823,3.137,1.214,4.048C3.313,23.258,3.7,22.06,4.074,20.726z"/>
|
||||
<path d="M23.207,12.127c-0.752,1.455-1.623,5.075-2.236,7.622c-0.001,0.003-0.003,0.007-0.003,0.01
|
||||
c-0.39,1.622-0.728,2.984-1.043,4.138c0.288,1.192,0.597,2.564,0.947,4.162c0.003,0.011,0.004,0.019,0.005,0.029
|
||||
c0.613,2.793,1.484,6.748,2.234,8.499c0.751-1.453,1.621-5.067,2.234-7.607c0.001-0.009,0.003-0.017,0.004-0.025
|
||||
c0.391-1.622,0.729-2.984,1.044-4.137c-0.286-1.187-0.594-2.552-0.942-4.14c-0.002-0.008-0.004-0.016-0.006-0.023
|
||||
C24.832,17.859,23.96,13.883,23.207,12.127z"/>
|
||||
<path d="M43.47,34.332c-0.004-0.003-0.006-0.002-0.01-0.002l-0.001,0.001h-0.001c-0.003,0-0.005,0.004-0.01,0.002
|
||||
c-0.943-0.192-1.605-0.869-2.223-2.344c-0.034,0.139-0.069,0.298-0.102,0.432c-0.001,0.005-0.002,0.037-0.003,0.041
|
||||
c-1.745,7.246-2.289,9.616-4.307,9.223C36.813,41.683,37,41.789,37,41.789V42h-0.389c-0.003,0-0.006,0-0.008,0
|
||||
c-2.019-1-2.754-3.236-4.5-11.183c-0.002-0.007-0.003-0.228-0.005-0.235c-0.011-0.05-0.023-0.104-0.034-0.154
|
||||
c-0.58,1.036-1.214,1.388-2.089,1.22c-0.006-0.002-0.007,0.011-0.018,0.005c-0.006,0.002-0.012,0.014-0.017,0.014
|
||||
c-0.94-0.195-1.602-0.845-2.218-2.316c-0.033,0.139-0.068,0.354-0.101,0.486C27.621,29.844,27.618,30,27.615,30h0.002
|
||||
c-1.741,7-2.478,9.41-4.491,9.021c-0.004,0-0.01-0.141-0.015-0.139c-0.006-0.006-0.009-0.003-0.014-0.003
|
||||
c-2.016-0.415-2.752-3.032-4.499-10.979c-0.001-0.009-0.002-0.014-0.004-0.023c-0.01-0.045-0.021-0.087-0.031-0.132
|
||||
c-0.581,1.039-1.215,1.399-2.092,1.229C16.467,28.973,16.463,29,16.458,29c-0.001,0-0.002,0-0.004,0
|
||||
c-0.004,0-0.008-0.031-0.012-0.032c-0.941-0.193-1.603-0.906-2.219-2.378c-0.033,0.137-0.067,0.28-0.099,0.411
|
||||
c-0.001,0.001-0.001,0.002-0.001,0.004c-1.745,7.25-2.481,9.572-4.497,9.184c-0.004-0.001-0.01-0.002-0.015-0.005
|
||||
c-0.006,0-0.01-0.007-0.015-0.007c-2.014-0.415-2.751-3.037-4.493-10.972C5.102,25.2,5.1,25.179,5.099,25.174l-0.001-0.037
|
||||
c-0.012-0.053-0.025-0.175-0.037-0.229C4.479,25.95,3.843,26,2.963,26c-0.003,0-0.006,0-0.01,0c-0.002,0-0.02,0.121-0.023,0.119
|
||||
c-0.783-0.159-1.382-0.58-1.916-1.558C1.314,37.005,11.486,47,24,47c9.046,0,16.868-5.225,20.624-12.818
|
||||
C44.284,34.374,43.907,34.417,43.47,34.332z"/>
|
||||
<path d="M15.241,22.597c0.402,1.632,0.825,3.144,1.216,4.056c0.36-0.696,0.747-1.894,1.119-3.227
|
||||
c-0.402-1.632-0.824-3.143-1.215-4.054C16.001,20.067,15.614,21.264,15.241,22.597z"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 20 KiB |
@@ -0,0 +1,41 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="48px"
|
||||
height="48px" viewBox="0 0 48 48" enable-background="new 0 0 48 48" xml:space="preserve">
|
||||
<g id="Layer_1" display="none">
|
||||
<path display="inline" d="M47,41c0,3.313-2.688,6-6,6H7c-3.313,0-6-2.688-6-6V7c0-3.313,2.687-6,6-6h34c3.313,0,6,2.687,6,6V41z"/>
|
||||
|
||||
<text transform="matrix(1 0 0 1 9.8389 32.5723)" display="inline" fill="#FFFFFF" font-family="'PalatinoLinotype-BoldItalic'" font-size="34">fx</text>
|
||||
</g>
|
||||
<g id="Composed">
|
||||
<path d="M41,1H7C3.688,1,1,3.687,1,7v34c0,3.313,2.688,6,6,6h34c3.313,0,6-2.688,6-6V7C47,3.687,44.313,1,41,1z M18.538,18.776
|
||||
l-1.295,5.761c-0.531,2.369-1.052,4.46-1.561,6.275c-0.675,2.435-1.336,4.28-1.984,5.537c-0.647,1.256-1.469,2.385-2.465,3.387
|
||||
c-0.996,1.001-1.893,1.69-2.689,2.066c-0.498,0.232-0.974,0.349-1.428,0.349c-0.398,0-0.819-0.1-1.262-0.299
|
||||
c0.531-1.372,0.896-2.601,1.096-3.686l0.531-0.1c0.432,1.007,0.985,1.511,1.66,1.511c0.232,0,0.457-0.059,0.672-0.174
|
||||
c0.216-0.117,0.417-0.308,0.606-0.573c0.188-0.266,0.423-0.946,0.706-2.042s0.623-2.695,1.021-4.798l1.229-6.425l1.295-6.79
|
||||
c-0.631-0.022-1.135-0.033-1.511-0.033c-0.576,0-1.085,0.011-1.527,0.033l-0.116-0.149l0.116-0.598l0.183-0.183
|
||||
c0.254-0.088,0.758-0.243,1.511-0.465c0.752-0.221,1.333-0.426,1.743-0.614c0.033-0.077,0.105-0.382,0.216-0.913
|
||||
c0.188-0.819,0.354-1.408,0.498-1.768c0.144-0.359,0.393-0.755,0.747-1.187c0.354-0.432,1.001-1.046,1.942-1.843
|
||||
c0.94-0.797,1.812-1.48,2.615-2.05c0.802-0.57,1.405-0.932,1.81-1.087c0.404-0.155,0.849-0.232,1.336-0.232
|
||||
c0.52,0,1.018,0.094,1.494,0.282c-0.432,1.472-0.747,2.905-0.946,4.3l-0.498,0.116c-0.853-1.306-1.705-1.959-2.557-1.959
|
||||
c-0.31,0-0.601,0.097-0.872,0.291c-0.271,0.194-0.481,0.454-0.631,0.78c-0.149,0.327-0.363,1.06-0.639,2.2
|
||||
c-0.277,1.14-0.509,2.252-0.697,3.337h0.382c0.221,0,0.838-0.027,1.851-0.083c1.013-0.055,1.674-0.116,1.984-0.183l0.166,0.266
|
||||
c-0.232,0.631-0.393,1.157-0.481,1.577l-0.183,0.166c-0.388-0.022-1.151-0.033-2.291-0.033
|
||||
C19.396,18.743,18.804,18.754,18.538,18.776z M37.364,20.403l-0.714,0.133l-0.315-0.946c-0.232-0.11-0.448-0.166-0.647-0.166
|
||||
c-0.598,0-1.195,0.269-1.793,0.805c-0.598,0.537-1.483,1.658-2.656,3.362c0.343,2.258,0.642,3.833,0.896,4.724
|
||||
s0.515,1.475,0.78,1.751s0.537,0.415,0.813,0.415c0.498,0,1.345-0.515,2.54-1.544l0.199,0.017l0.382,0.614l-0.033,0.199
|
||||
c-1.937,1.693-3.201,2.703-3.794,3.03c-0.592,0.326-1.109,0.489-1.552,0.489c-0.509,0-0.969-0.199-1.378-0.598
|
||||
s-0.705-0.94-0.889-1.627c-0.182-0.687-0.423-2.003-0.722-3.951c-1.295,1.882-2.216,3.157-2.765,3.827
|
||||
c-0.547,0.669-0.995,1.164-1.344,1.485c-0.349,0.321-0.687,0.545-1.013,0.673c-0.327,0.127-0.667,0.19-1.021,0.19
|
||||
c-0.222,0-0.396-0.02-0.523-0.058c-0.127-0.039-0.435-0.169-0.921-0.391c0.177-0.487,0.321-1.106,0.432-1.859
|
||||
c0.11-0.753,0.166-1.192,0.166-1.32c0-0.127-0.006-0.251-0.017-0.373l0.83-0.083l0.199,1.063c0.332,0.133,0.625,0.199,0.88,0.199
|
||||
c0.387,0,0.808-0.142,1.262-0.423c0.454-0.283,1.096-0.966,1.926-2.051c0.83-1.084,1.367-1.82,1.61-2.208
|
||||
c-0.343-2.059-0.606-3.505-0.788-4.341c-0.184-0.835-0.449-1.383-0.797-1.644c-0.35-0.26-0.689-0.39-1.021-0.39
|
||||
c-0.421,0-0.853,0.144-1.295,0.432c-0.443,0.288-0.775,0.637-0.996,1.046l-0.199,0.066l-0.548-0.315l-0.033-0.183
|
||||
c1.106-1.749,1.749-2.883,1.926-3.403c0.886-0.454,1.721-0.681,2.507-0.681c1.052,0,1.849,0.285,2.391,0.855
|
||||
c0.542,0.57,0.907,1.201,1.096,1.893c0.188,0.692,0.371,1.729,0.548,3.113c1.594-2.335,2.676-3.791,3.245-4.366
|
||||
c0.57-0.575,1.077-0.963,1.52-1.162s0.863-0.299,1.262-0.299c0.321,0,0.703,0.089,1.146,0.266
|
||||
C37.757,17.609,37.497,18.854,37.364,20.403z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.8 KiB |
@@ -0,0 +1,16 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="48px"
|
||||
height="48px" viewBox="0 0 48 48" enable-background="new 0 0 48 48" xml:space="preserve">
|
||||
<g id="Layer_1" display="none">
|
||||
<path display="inline" d="M47,41c0,3.313-2.687,6-6,6H7c-3.313,0-6-2.687-6-6V7c0-3.313,2.687-6,6-6h34c3.313,0,6,2.687,6,6V41z"/>
|
||||
<polyline display="inline" fill="none" stroke="#FFFFFF" stroke-width="5" points="-22,12.5 24.5,12.5 24.5,32.5 62,32.5 "/>
|
||||
</g>
|
||||
<g id="Combined">
|
||||
<g>
|
||||
<path d="M27,10v20h20V7c0-3.313-2.687-6-6-6H7C3.687,1,1,3.687,1,7v3H27z"/>
|
||||
<path d="M22,35V15H1v26c0,3.313,2.687,6,6,6h34c3.313,0,6-2.687,6-6v-6H22z"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 954 B |
@@ -0,0 +1,41 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
width="48px" height="48px" viewBox="0 0 48 48" enable-background="new 0 0 48 48" xml:space="preserve">
|
||||
<g id="Layer_1_1_" display="none">
|
||||
<circle display="inline" cx="24" cy="23.999" r="23"/>
|
||||
<path display="inline" d="M47,41c0,3.313-2.688,6-6,6H7c-3.313,0-6-2.688-6-6V7c0-3.313,2.687-6,6-6h34c3.313,0,6,2.687,6,6V41z"/>
|
||||
<path id="full_sin_2_" fill="none" stroke="#FFFFFF" stroke-width="3" stroke-linecap="round" d="M-4.64,16.94
|
||||
c1.398,3.74,2.795,7.481,4.388,7.481 M4.129,16.94c-1.397,3.739-2.793,7.481-4.386,7.481 M4.128,16.94
|
||||
c1.397-3.74,2.795-7.481,4.387-7.481 M12.896,16.941C11.5,13.2,10.104,9.459,8.511,9.459 M12.896,16.942
|
||||
c1.398,3.741,2.795,7.481,4.385,7.481 M21.665,16.942c-1.398,3.741-2.795,7.481-4.387,7.481 M21.665,16.943
|
||||
c1.396-3.741,2.792-7.481,4.385-7.481 M30.432,16.943c-1.396-3.741-2.793-7.481-4.386-7.481 M30.432,16.941
|
||||
c1.397,3.74,2.793,7.481,4.387,7.481 M39.197,16.941c-1.396,3.74-2.793,7.481-4.387,7.481 M39.197,16.941
|
||||
c1.398-3.74,2.796-7.48,4.387-7.48 M47.966,16.942c-1.396-3.741-2.794-7.481-4.385-7.481 M47.966,16.941
|
||||
c1.396,3.74,2.793,7.481,4.39,7.481 M56.732,16.941c-1.396,3.74-2.791,7.481-4.385,7.481 M56.732,16.942
|
||||
c1.398-3.741,2.793-7.481,4.388-7.481 M65.501,16.942c-1.397-3.741-2.793-7.481-4.388-7.481 M65.501,16.94
|
||||
c1.396,3.74,2.793,7.481,4.388,7.481 M74.27,16.94c-1.396,3.739-2.793,7.481-4.386,7.481 M74.27,16.94
|
||||
c1.396-3.74,2.791-7.481,4.385-7.481 M83.038,16.941c-1.397-3.741-2.797-7.482-4.388-7.482 M83.038,16.942
|
||||
c1.396,3.741,2.795,7.481,4.385,7.481 M91.807,16.942c-1.396,3.741-2.795,7.481-4.386,7.481"/>
|
||||
</g>
|
||||
<g>
|
||||
<path d="M2.705,16.465c0.006-0.017,0.012-0.033,0.018-0.05c1.761-4.714,3.153-8.439,5.775-8.456c0.006,0,0.01,0.001,0.015,0
|
||||
c0.003,0.001,0.01,0,0.015,0c2.623,0.017,4.014,3.742,5.773,8.456c0.004,0.01,0.007,0.02,0.011,0.03
|
||||
c0.803,2.147,2.106,5.629,2.969,6.396c0.866-0.769,2.177-4.275,2.979-6.421h0c0,0,0-0.001,0-0.001
|
||||
c1.76-4.716,3.15-8.442,5.775-8.457c0.004,0,0.007,0,0.013,0c0.004,0,0.009,0,0.013,0c2.616,0.014,4.007,3.717,5.76,8.412
|
||||
c0.006,0.014,0.011,0.028,0.017,0.042l0.006,0.015c0.8,2.141,2.106,5.639,2.972,6.408c0.867-0.77,2.176-4.276,2.978-6.421
|
||||
c0.001-0.003,0.002-0.007,0.003-0.01c1.763-4.712,3.155-8.436,5.778-8.446c0.002,0,0.005,0,0.01,0h0.001c0.003,0,0.006,0,0.008,0
|
||||
c1.41,0.005,2.463,1.087,3.408,2.811V7c0-3.313-2.688-6-6-6H7C3.687,1,1,3.687,1,7v13.725C1.614,19.38,2.242,17.704,2.705,16.465z"
|
||||
/>
|
||||
<path d="M46.572,17.496c-0.004-0.01-0.008-0.02-0.012-0.03c-0.801-2.146-2.109-5.654-2.978-6.423
|
||||
c-0.866,0.768-2.176,4.271-2.978,6.415c-0.001,0.003-0.002,0.006-0.004,0.009c-1.757,4.706-3.146,8.427-5.761,8.455
|
||||
c-0.009,0-0.018,0.002-0.026,0c-0.01,0.001-0.018,0-0.026,0c-2.607-0.028-3.995-3.728-5.745-8.411
|
||||
c-0.006-0.014-0.011-0.029-0.017-0.043c-0.802-2.146-2.111-5.656-2.979-6.424c-0.867,0.768-2.176,4.277-2.978,6.424
|
||||
c-0.004,0.01-0.008,0.021-0.012,0.031c-1.757,4.701-3.148,8.412-5.767,8.425c-0.004,0-0.006-0.001-0.012,0
|
||||
c-0.006-0.001-0.009,0-0.012,0c-2.622-0.013-4.014-3.738-5.775-8.453c-0.004-0.01-0.008-0.021-0.011-0.031
|
||||
c-0.802-2.149-2.104-5.633-2.968-6.398c-0.867,0.768-2.178,4.277-2.98,6.423c0,0,0,0,0,0c-0.002,0.004-0.003,0.009-0.005,0.013
|
||||
h0.001C4.071,21.382,2.864,24.6,1,25.596V41c0,3.313,2.687,6,6,6h34c3.313,0,6-2.688,6-6V18.637
|
||||
C46.859,18.263,46.717,17.885,46.572,17.496z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.6 KiB |
@@ -0,0 +1,33 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="48px"
|
||||
height="48px" viewBox="0 0 48 48" enable-background="new 0 0 48 48" xml:space="preserve">
|
||||
<g id="x">
|
||||
</g>
|
||||
<g id="Layer_1">
|
||||
<g id="Layer_1_1_" display="none">
|
||||
<path display="inline" d="M47,41c0,3.313-2.688,6-6,6H7c-3.313,0-6-2.688-6-6V7c0-3.313,2.687-6,6-6h34c3.313,0,6,2.687,6,6V41z"
|
||||
/>
|
||||
<g display="inline">
|
||||
<rect x="5" y="10.828" fill="#FFFFFF" width="4.673" height="21.398"/>
|
||||
<rect x="10.404" y="10.828" fill="#FFFFFF" width="4.674" height="21.398"/>
|
||||
<rect x="15.81" y="10.828" fill="#FFFFFF" width="4.673" height="21.398"/>
|
||||
<rect x="21.215" y="10.828" fill="#FFFFFF" width="4.673" height="21.398"/>
|
||||
<rect x="8.417" y="10.828" width="3.243" height="12.333"/>
|
||||
<rect x="13.823" y="10.828" width="3.243" height="12.333"/>
|
||||
<rect x="26.619" y="10.828" fill="#FFFFFF" width="4.673" height="21.398"/>
|
||||
<rect x="19.227" y="10.828" width="3.244" height="12.333"/>
|
||||
<rect x="32.023" y="10.828" fill="#FFFFFF" width="4.674" height="21.398"/>
|
||||
<rect x="30.036" y="10.828" width="3.243" height="12.333"/>
|
||||
<rect x="37.429" y="10.828" fill="#FFFFFF" width="4.673" height="21.398"/>
|
||||
<rect x="35.441" y="10.828" width="3.243" height="12.333"/>
|
||||
</g>
|
||||
</g>
|
||||
<path d="M41,1H7C3.687,1,1,3.687,1,7v34c0,3.313,2.687,6,6,6h34c3.313,0,6-2.688,6-6V7C47,3.687,44.313,1,41,1z M5,32.227V10.828
|
||||
h3.417V23.5h1.256v8.727H5z M10.404,32.227V23.5h1.256V10.828h2.163V23.5h1.256v8.727H10.404z M15.81,32.227V23.5h1.256V10.828
|
||||
h2.162V23.5h1.255v8.727H15.81z M25.888,32.227h-4.673V23.5h1.256V10.828h3.417V32.227z M26.619,32.227V10.828h3.417V23.5h1.256
|
||||
v8.727H26.619z M32.023,32.227V23.5h1.256V10.828h2.162V23.5h1.256v8.727H32.023z M42.102,32.227h-4.673V23.5h1.256V10.828h3.417
|
||||
V32.227z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.1 KiB |
@@ -0,0 +1,15 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
width="48px" height="48px" viewBox="0 0 48 48" enable-background="new 0 0 48 48" xml:space="preserve">
|
||||
<g id="Layer_1_1_" display="none">
|
||||
<path display="inline" d="M47,41c0,3.313-2.688,6-6,6H7c-3.313,0-6-2.688-6-6V7c0-3.313,2.687-6,6-6h34c3.313,0,6,2.687,6,6V41z"/>
|
||||
<polyline display="inline" fill="none" stroke="#FFFFFF" stroke-width="5" points="5.5,53.25 24,12.5 65.5,12.5 "/>
|
||||
</g>
|
||||
<g>
|
||||
<path d="M22.389,10H47V7c0-3.313-2.688-6-6-6H7C3.687,1,1,3.687,1,7v34c0,2.853,1.994,5.236,4.662,5.845L22.389,10z"/>
|
||||
<path d="M25.61,15L11.083,47H41c3.313,0,6-2.688,6-6V15H25.61z"/>
|
||||
</g>
|
||||
<path d="M109,15"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 982 B |
@@ -0,0 +1,19 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
width="48px" height="48px" viewBox="0 0 48 48" enable-background="new 0 0 48 48" xml:space="preserve">
|
||||
<g id="Layer_1_1_" display="none">
|
||||
<path display="inline" d="M47,41c0,3.313-2.688,6-6,6H7c-3.313,0-6-2.688-6-6V7c0-3.313,2.687-6,6-6h34c3.313,0,6,2.687,6,6V41z"/>
|
||||
<path display="inline" fill="none" stroke="#FFFFFF" stroke-width="6" d="M24,9.328c0,4.971-4.029,9-9,9H-0.605
|
||||
c-4.971,0-9-4.029-9-9V-9.328c0-4.971,4.029-9,9-9H15c4.971,0,9,4.029,9,9V9.328z"/>
|
||||
<path display="inline" fill="none" stroke="#FFFFFF" stroke-width="6" d="M24,57.328c0,4.971-4.029,9-9,9H-0.605
|
||||
c-4.971,0-9-4.029-9-9V38.672c0-4.971,4.029-9,9-9H15c4.971,0,9,4.029,9,9V57.328z"/>
|
||||
</g>
|
||||
<g>
|
||||
<path d="M41,1H28v8.328c0,6.617-5.383,12-12,12H1v5.344h15c6.617,0,12,5.383,12,12V47h13c3.313,0,6-2.688,6-6V7
|
||||
C47,3.687,44.313,1,41,1z"/>
|
||||
<path d="M16,32.672H1V41c0,3.313,2.687,6,6,6h15v-8.328C22,35.363,19.309,32.672,16,32.672z"/>
|
||||
<path d="M22,9.328V1H7C3.687,1,1,3.687,1,7v8.328h15C19.309,15.328,22,12.636,22,9.328z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.3 KiB |
@@ -0,0 +1,37 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="48px"
|
||||
height="48px" viewBox="0 0 48 48" enable-background="new 0 0 48 48" xml:space="preserve">
|
||||
<g id="x">
|
||||
</g>
|
||||
<g id="Layer_1" display="none">
|
||||
<path display="inline" d="M47,41c0,3.313-2.688,6-6,6H7c-3.313,0-6-2.688-6-6V7c0-3.313,2.687-6,6-6h34c3.313,0,6,2.687,6,6V41z"/>
|
||||
<g display="inline">
|
||||
<line fill="none" stroke="#FFFFFF" stroke-width="5" stroke-linecap="round" x1="11.5" y1="29" x2="11.5" y2="38.5"/>
|
||||
<circle fill="#FFFFFF" cx="11.5" cy="13.5" r="2.5"/>
|
||||
<circle fill="#FFFFFF" cx="11.5" cy="20.5" r="2.5"/>
|
||||
</g>
|
||||
<g display="inline">
|
||||
<line fill="none" stroke="#FFFFFF" stroke-width="5" stroke-linecap="round" x1="21.5" y1="29" x2="21.5" y2="38.5"/>
|
||||
<circle fill="#FFFFFF" cx="21.5" cy="13.5" r="2.5"/>
|
||||
<circle fill="#FFFFFF" cx="21.5" cy="20.5" r="2.5"/>
|
||||
</g>
|
||||
<g display="inline">
|
||||
<line fill="none" stroke="#FFFFFF" stroke-width="5" stroke-linecap="round" x1="31.5" y1="29" x2="31.5" y2="38.5"/>
|
||||
<circle fill="#FFFFFF" cx="31.5" cy="13.5" r="2.5"/>
|
||||
<circle fill="#FFFFFF" cx="31.5" cy="20.5" r="2.5"/>
|
||||
</g>
|
||||
</g>
|
||||
<g id="Compound">
|
||||
<path d="M41,1H7C3.687,1,1,3.687,1,7v34c0,3.313,2.687,6,6,6h34c3.313,0,6-2.688,6-6V7C47,3.687,44.313,1,41,1z M14,38.5
|
||||
c0,1.381-1.119,2.5-2.5,2.5S9,39.881,9,38.5V29c0-1.381,1.119-2.5,2.5-2.5S14,27.619,14,29V38.5z M11.5,23
|
||||
C10.119,23,9,21.88,9,20.5s1.119-2.5,2.5-2.5s2.5,1.12,2.5,2.5S12.881,23,11.5,23z M11.5,16C10.119,16,9,14.88,9,13.5
|
||||
s1.119-2.5,2.5-2.5s2.5,1.12,2.5,2.5S12.881,16,11.5,16z M24,38.5c0,1.381-1.119,2.5-2.5,2.5S19,39.881,19,38.5V29
|
||||
c0-1.381,1.119-2.5,2.5-2.5S24,27.619,24,29V38.5z M21.5,23c-1.381,0-2.5-1.12-2.5-2.5s1.119-2.5,2.5-2.5s2.5,1.12,2.5,2.5
|
||||
S22.881,23,21.5,23z M21.5,16c-1.381,0-2.5-1.12-2.5-2.5s1.119-2.5,2.5-2.5s2.5,1.12,2.5,2.5S22.881,16,21.5,16z M34,38.5
|
||||
c0,1.381-1.119,2.5-2.5,2.5S29,39.881,29,38.5V29c0-1.381,1.119-2.5,2.5-2.5S34,27.619,34,29V38.5z M31.5,23
|
||||
c-1.381,0-2.5-1.12-2.5-2.5s1.119-2.5,2.5-2.5s2.5,1.12,2.5,2.5S32.881,23,31.5,23z M31.5,16c-1.381,0-2.5-1.12-2.5-2.5
|
||||
s1.119-2.5,2.5-2.5s2.5,1.12,2.5,2.5S32.881,16,31.5,16z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.4 KiB |
@@ -0,0 +1,75 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="48px"
|
||||
height="48px" viewBox="0 0 48 48" enable-background="new 0 0 48 48" xml:space="preserve">
|
||||
<g id="Layer_1" display="none">
|
||||
<circle cx="24" cy="23.999" r="23"/>
|
||||
<path display="inline" d="M47,41c0,3.313-2.687,6-6,6H7c-3.313,0-6-2.687-6-6V7c0-3.313,2.687-6,6-6h34c3.313,0,6,2.687,6,6V41z"/>
|
||||
<path id="full_sin_1_" display="inline" fill="none" stroke="#FFFFFF" stroke-width="3" stroke-linecap="round" d="M-12.638,28.917
|
||||
M-12.64,28.914c1.498,5.253,2.994,10.507,4.7,10.507 M-3.248,28.914c-1.497,5.251-2.992,10.507-4.698,10.507 M-3.249,28.914
|
||||
c1.497-5.253,2.994-10.507,4.699-10.507 M6.143,28.915c-1.495-5.254-2.991-10.508-4.697-10.508 M6.143,28.917
|
||||
c1.498,5.254,2.994,10.507,4.696,10.507 M15.536,28.917c-1.498,5.254-2.994,10.507-4.699,10.507 M15.536,28.918
|
||||
c1.495-5.254,2.99-10.507,4.697-10.507 M24.926,28.918c-1.497-5.254-2.993-10.507-4.698-10.507 M24.926,28.915
|
||||
c1.496,5.253,2.99,10.508,4.697,10.508 M34.314,28.915c-1.495,5.253-2.99,10.508-4.697,10.508 M34.314,28.915
|
||||
c1.499-5.252,2.994-10.505,4.698-10.505 M43.706,28.917c-1.495-5.254-2.992-10.507-4.696-10.507 M43.706,28.915
|
||||
c1.496,5.253,2.992,10.508,4.701,10.508 M53.098,28.915c-1.496,5.253-2.99,10.508-4.697,10.508 M53.098,28.917
|
||||
c1.498-5.254,2.991-10.507,4.698-10.507 M62.488,28.917C60.99,23.663,59.497,18.41,57.79,18.41 M62.488,28.914
|
||||
c1.494,5.253,2.991,10.507,4.698,10.507 M71.879,28.914c-1.493,5.251-2.991,10.507-4.696,10.507 M71.879,28.914
|
||||
c1.496-5.253,2.99-10.507,4.697-10.507 M81.272,28.915c-1.498-5.254-2.996-10.508-4.698-10.508 M81.272,28.917
|
||||
c1.496,5.254,2.993,10.507,4.696,10.507 M90.663,28.917c-1.495,5.254-2.993,10.507-4.696,10.507 M90.663,28.918"/>
|
||||
<path id="full_sin_2_" display="inline" fill="none" stroke="#FFFFFF" stroke-width="3" stroke-linecap="round" d="M-3.998,19.851
|
||||
M-4,19.848c1.498,5.253,2.994,10.508,4.7,10.508 M5.392,19.848C3.896,25.1,2.4,30.355,0.694,30.355 M5.391,19.848
|
||||
C6.888,14.595,8.385,9.341,10.09,9.341 M14.782,19.849c-1.495-5.254-2.991-10.508-4.697-10.508 M14.782,19.851
|
||||
c1.498,5.254,2.994,10.507,4.696,10.507 M24.176,19.851c-1.498,5.254-2.994,10.507-4.699,10.507 M24.176,19.852
|
||||
c1.494-5.254,2.99-10.507,4.695-10.507 M33.564,19.852c-1.496-5.254-2.992-10.507-4.697-10.507 M33.564,19.849
|
||||
c1.498,5.252,2.992,10.506,4.699,10.506 M42.953,19.849c-1.494,5.252-2.99,10.506-4.697,10.506 M42.953,19.849
|
||||
c1.5-5.253,2.996-10.506,4.699-10.506 M52.346,19.851C50.85,14.596,49.354,9.343,47.65,9.343 M52.346,19.849
|
||||
c1.496,5.252,2.992,10.506,4.701,10.506 M61.736,19.849c-1.494,5.252-2.988,10.506-4.695,10.506 M61.736,19.851
|
||||
c1.498-5.254,2.992-10.507,4.699-10.507 M71.129,19.851c-1.498-5.254-2.992-10.507-4.699-10.507 M71.129,19.848
|
||||
c1.492,5.253,2.99,10.508,4.697,10.508 M80.52,19.848c-1.494,5.252-2.992,10.508-4.697,10.508 M80.52,19.848
|
||||
c1.496-5.253,2.988-10.507,4.695-10.507 M89.912,19.849c-1.498-5.254-2.996-10.508-4.699-10.508 M89.912,19.851
|
||||
c1.496,5.254,2.994,10.507,4.697,10.507 M99.303,19.851c-1.496,5.254-2.992,10.507-4.695,10.507 M99.303,19.852"/>
|
||||
</g>
|
||||
<g id="Composed">
|
||||
<g>
|
||||
<path d="M32.137,20.311c-0.006-0.016-0.011-0.032-0.015-0.048c-0.858-3.013-2.245-7.882-3.253-9.208
|
||||
c-1.01,1.325-2.394,6.195-3.251,9.207c-0.003,0.012-0.006,0.023-0.01,0.035c-0.293,1.029-0.569,1.985-0.835,2.88
|
||||
c0.492,1.501,1.004,3.257,1.581,5.28c0.005,0.016,0.01,0.031,0.014,0.047l0.007,0.023c0.855,3.005,2.239,7.861,3.245,9.185
|
||||
c1.008-1.325,2.394-6.191,3.25-9.202l2.239,0.637c-0.006-0.01-0.011-0.021-0.017-0.031l-0.778-0.2l-1.432-0.449
|
||||
c0.293-1.027,0.569-1.982,0.835-2.877C33.225,24.089,32.714,22.333,32.137,20.311z"/>
|
||||
<path d="M36.87,25.533c0.485,1.389,0.97,2.558,1.39,3.11c0.64-0.841,1.43-3.104,2.141-5.415c-0.484-1.389-0.969-2.557-1.389-3.108
|
||||
C38.373,20.96,37.582,23.225,36.87,25.533z"/>
|
||||
<path d="M1.433,17c0.005,0,0.009,0,0.015,0c0.009,0,0.011,0,0.015,0c1.069,0,1.911,0.599,2.674,1.826
|
||||
c2.052-7.168,3.316-10.967,5.936-10.984c0.004,0,0.007,0.004,0.015,0.001c0.007,0.003,0.01,0.003,0.015,0.002
|
||||
c2.702,0.017,3.959,3.999,6.123,11.604c0.003,0.011,0.006,0.033,0.009,0.044c0.109,0.383,0.227,0.818,0.351,1.249
|
||||
C17.616,18.25,18.683,17,20.213,17c0.006,0,0.011,0,0.017,0c0.007,0,0.012,0,0.017,0c1.069,0,1.911,0.68,2.673,1.909
|
||||
C24.969,11.737,26.232,8,28.855,8c0.005,0,0.009,0,0.014,0s0.009,0,0.014,0c2.694,0,3.953,3.901,6.11,11.475
|
||||
c0.005,0.015,0.01-0.042,0.014-0.026l0.012,0.051c0.108,0.381,0.226,0.814,0.35,1.243C36.401,18.247,37.469,17,39.001,17
|
||||
c0.004,0,0.011,0,0.01,0c0.005,0,0.007,0,0.011,0c1.071,0,1.915,0.675,2.679,1.903c1.88-6.559,3.102-10.227,5.3-10.811V7
|
||||
c0-3.313-2.687-6-6-6H7C3.687,1,1,3.687,1,7v10.046C1.142,17.022,1.283,17,1.433,17z"/>
|
||||
<path d="M44.396,20.26c-0.296,1.041-0.574,2.006-0.842,2.91c0.493,1.504,1.006,3.266,1.584,5.296
|
||||
c0.004,0.013,0.008,0.025,0.011,0.038l0.002,0.006c0.497,1.747,1.173,4.115,1.85,6.058V12.255
|
||||
c-0.894,2.018-1.911,5.579-2.595,7.97C44.402,20.236,44.399,20.248,44.396,20.26z"/>
|
||||
<path d="M1.448,20.117C1.306,20.305,1.155,20.571,1,20.885v7.27c0.576-1.054,1.235-2.969,1.838-4.927
|
||||
C2.353,21.838,1.868,20.669,1.448,20.117z"/>
|
||||
<path d="M4.602,19.619c0.003,0.007,0.007,0.014,0.011,0.021l0.778,0.208l-0.015-0.004l0.003,0.001L4.602,19.619z"/>
|
||||
<path d="M18.089,25.536c0.485,1.391,0.97,2.56,1.389,3.111c0.639-0.84,1.43-3.103,2.142-5.414
|
||||
c-0.485-1.391-0.971-2.56-1.391-3.112C19.59,20.961,18.8,23.225,18.089,25.536z"/>
|
||||
<path d="M42.275,29.424c-0.004-0.014-0.009-0.047-0.012-0.061c-0.111-0.392-0.232-0.779-0.359-1.223
|
||||
C40.876,30.627,39.812,32,38.286,32c-0.009,0-0.019,0-0.026,0c-0.011,0-0.019,0-0.026,0c-1.064,0-1.903-0.722-2.663-1.947
|
||||
c-2.049,7.167-3.313,10.85-5.93,10.871c-0.007,0-0.013,0.003-0.021,0.001c-0.006,0.002-0.014,0.003-0.021,0.003
|
||||
c-2.691-0.022-3.947-3.977-6.102-11.543c-0.005-0.016-0.01-0.022-0.014-0.038l-0.002,0.011c-0.111-0.391-0.231-0.776-0.359-1.217
|
||||
C22.088,30.64,21.02,32,19.484,32c-0.002,0-0.004,0-0.006,0c-0.003,0-0.005,0-0.006,0c-1.073,0-1.917-0.674-2.681-1.905
|
||||
C14.734,37.272,13.471,41,10.844,41c-0.001,0-0.003,0-0.005,0c-0.001,0-0.001,0-0.002,0c-0.002,0-0.004,0-0.005,0
|
||||
c-2.703,0-3.96-4-6.128-11.605c-0.004-0.012-0.007-0.046-0.011-0.059c-0.109-0.385-0.228-0.763-0.352-1.195
|
||||
C3.376,30.472,2.376,31.804,1,31.967V41c0,3.313,2.687,6,6,6h34c3.313,0,6-2.687,6-6v-0.422
|
||||
C45.185,39.368,44.025,35.57,42.275,29.424z"/>
|
||||
<path d="M7.585,28.505c0.003,0.013,0.007,0.025,0.01,0.038c0.858,3.011,2.239,7.85,3.243,9.171
|
||||
c1.006-1.323,2.392-6.183,3.249-9.188h0c0.001-0.006,0.003-0.012,0.005-0.018c0.296-1.042,0.575-2.009,0.843-2.914
|
||||
c-0.496-1.511-1.012-3.28-1.593-5.321c-0.003-0.012-0.006-0.023-0.009-0.035c-0.857-3.015-2.239-7.865-3.246-9.187
|
||||
c-1.008,1.325-2.395,6.19-3.252,9.2l-1.444-0.404l0.053,0.015l-0.041-0.012l1.424,0.431c-0.294,1.032-0.571,1.992-0.837,2.889
|
||||
C6.487,24.686,7.003,26.459,7.585,28.505z"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 7.0 KiB |
@@ -0,0 +1,30 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
width="48px" height="48px" viewBox="0 0 48 48" enable-background="new 0 0 48 48" xml:space="preserve">
|
||||
<g id="Layer_1_1_" display="none">
|
||||
<circle cx="24" cy="23.999" r="23"/>
|
||||
<path display="inline" d="M47,41c0,3.313-2.688,6-6,6H7c-3.313,0-6-2.688-6-6V7c0-3.313,2.687-6,6-6h34c3.313,0,6,2.687,6,6V41z"/>
|
||||
<path id="full_sin_2_" fill="none" stroke="#FFFFFF" stroke-width="3" stroke-linecap="round" d="M-4.64,16.94
|
||||
c1.398,3.74,2.795,7.481,4.388,7.481 M4.129,16.94c-1.397,3.739-2.793,7.481-4.386,7.481 M4.128,16.94
|
||||
c1.397-3.74,2.795-7.481,4.387-7.481 M12.896,16.941C11.5,13.2,10.104,9.459,8.511,9.459 M12.896,16.942
|
||||
c1.398,3.741,2.795,7.481,4.385,7.481 M21.665,16.942c-1.398,3.741-2.795,7.481-4.387,7.481 M21.665,16.943
|
||||
c1.396-3.741,2.792-7.481,4.385-7.481 M30.432,16.943c-1.396-3.741-2.793-7.481-4.386-7.481 M30.432,16.941
|
||||
c1.397,3.74,2.793,7.481,4.387,7.481 M39.197,16.941c-1.396,3.74-2.793,7.481-4.387,7.481 M39.197,16.941
|
||||
c1.398-3.74,2.796-7.48,4.387-7.48 M47.966,16.942c-1.396-3.741-2.794-7.481-4.385-7.481 M47.966,16.941
|
||||
c1.396,3.74,2.793,7.481,4.39,7.481 M56.732,16.941c-1.396,3.74-2.791,7.481-4.385,7.481 M56.732,16.942
|
||||
c1.398-3.741,2.793-7.481,4.388-7.481 M65.501,16.942c-1.397-3.741-2.793-7.481-4.388-7.481 M65.501,16.94
|
||||
c1.396,3.74,2.793,7.481,4.388,7.481 M74.27,16.94c-1.396,3.739-2.793,7.481-4.386,7.481 M74.27,16.94
|
||||
c1.396-3.74,2.791-7.481,4.385-7.481 M83.038,16.941c-1.397-3.741-2.797-7.482-4.388-7.482 M83.038,16.942
|
||||
c1.396,3.741,2.795,7.481,4.385,7.481 M91.807,16.942c-1.396,3.741-2.795,7.481-4.386,7.481"/>
|
||||
<polyline id="Saw" display="inline" fill="none" stroke="#FFFFFF" stroke-width="3" stroke-linecap="round" points="-6.75,27.423
|
||||
8.747,12.459 8.747,27.402 23.467,12.462 23.467,27.423 38.316,12.459 38.316,27 54.096,12.459 53.385,27.423 69.167,12.459 "/>
|
||||
</g>
|
||||
<g>
|
||||
<path d="M10.247,8.925v14.817l14.72-14.94v14.979l14.85-14.964v14.76L47,16.958V7c0-3.313-2.688-6-6-6H7C3.687,1,1,3.687,1,7
|
||||
v10.854L10.247,8.925z"/>
|
||||
<path d="M36.816,30.422V16.1L21.967,31.064V16.122L7.247,31.062V15.993L1,22.024V41c0,3.313,2.687,6,6,6h34c3.313,0,6-2.688,6-6
|
||||
V21.038L36.816,30.422z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.5 KiB |
@@ -0,0 +1,28 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="48px"
|
||||
height="48px" viewBox="0 0 48 48" enable-background="new 0 0 48 48" xml:space="preserve">
|
||||
<g id="Original" display="none">
|
||||
<circle display="inline" cx="24" cy="23.999" r="23"/>
|
||||
|
||||
<ellipse transform="matrix(0.9257 -0.3782 0.3782 0.9257 -6.8006 11.0334)" display="inline" fill="none" stroke="#FFFFFF" stroke-width="5" cx="24.693" cy="22.832" rx="8.86" ry="12.838"/>
|
||||
<line display="inline" fill="none" stroke="#FFFFFF" stroke-width="5" x1="35.209" y1="-0.569" x2="13.625" y2="48.241"/>
|
||||
</g>
|
||||
<g id="Combined">
|
||||
<g>
|
||||
<path d="M28.602,32.402c1.899-0.775,2.621-2.884,2.882-4.072c0.517-2.352,0.188-5.232-0.903-7.903
|
||||
c-0.302-0.739-0.646-1.417-1.017-2.049l-5.817,13.155c1.183,0.759,2.367,1.15,3.388,1.15
|
||||
C27.652,32.684,28.146,32.589,28.602,32.402z"/>
|
||||
<path d="M20.783,13.264c-1.898,0.775-2.62,2.884-2.881,4.071c-0.517,2.352-0.188,5.232,0.903,7.902
|
||||
c0.354,0.866,0.763,1.655,1.208,2.373l5.883-13.304c-1.268-0.873-2.551-1.323-3.647-1.323
|
||||
C21.733,12.983,21.24,13.077,20.783,13.264z"/>
|
||||
<path d="M35.807,4.261l-3.896,8.812c1.318,1.549,2.452,3.392,3.299,5.464c1.486,3.64,1.898,7.499,1.158,10.867
|
||||
c-0.803,3.65-2.943,6.431-5.875,7.628c-1.059,0.433-2.188,0.652-3.357,0.652c-1.847,0-3.696-0.549-5.431-1.532l-4.363,9.866
|
||||
C19.45,46.654,21.684,47,24,47c12.703,0,23-10.297,23-23.001C47,15.613,42.511,8.279,35.807,4.261z"/>
|
||||
<path d="M17.699,32.847c-1.414-1.601-2.628-3.531-3.522-5.719c-1.486-3.639-1.898-7.498-1.158-10.866
|
||||
c0.802-3.65,2.942-6.43,5.874-7.627c1.059-0.433,2.189-0.652,3.358-0.652c1.942,0,3.889,0.598,5.701,1.68l3.311-7.486
|
||||
C28.979,1.417,26.538,1,24,1C11.297,1,1,11.296,1,23.999c0,8.612,4.736,16.114,11.742,20.056L17.699,32.847z"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.0 KiB |
@@ -0,0 +1,28 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="48px"
|
||||
height="48px" viewBox="0 0 48 48" enable-background="new 0 0 48 48" xml:space="preserve">
|
||||
<g id="x">
|
||||
</g>
|
||||
<g id="Layer_1" display="none">
|
||||
<path display="inline" d="M47,41c0,3.313-2.688,6-6,6H7c-3.313,0-6-2.688-6-6V7c0-3.313,2.687-6,6-6h34c3.313,0,6,2.687,6,6V41z"/>
|
||||
<line display="inline" stroke="#FFFFFF" stroke-width="4" x1="-2.085" y1="35.585" x2="51.918" y2="35.585"/>
|
||||
<g display="inline">
|
||||
<line x1="-3.085" y1="17.585" x2="50.918" y2="17.585"/>
|
||||
<g>
|
||||
<line fill="none" stroke="#FFFFFF" stroke-width="4" x1="-3.085" y1="17.585" x2="-0.585" y2="17.585"/>
|
||||
|
||||
<line fill="none" stroke="#FFFFFF" stroke-width="4" stroke-dasharray="5.0003,4.0003" x1="3.415" y1="17.585" x2="46.418" y2="17.585"/>
|
||||
<line fill="none" stroke="#FFFFFF" stroke-width="4" x1="48.418" y1="17.585" x2="50.918" y2="17.585"/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="Compound">
|
||||
<g>
|
||||
<path d="M47,33.585V7c0-3.313-2.688-6-6-6H7C3.687,1,1,3.687,1,7v26.585H47z M39.418,15.585h5v4h-5V15.585z M30.417,15.585h5v4h-5
|
||||
V15.585z M21.417,15.585h5v4h-5V15.585z M12.416,15.585h5v4h-5V15.585z M3.415,15.585h5v4h-5V15.585z"/>
|
||||
<path d="M1,37.585V41c0,3.313,2.687,6,6,6h34c3.313,0,6-2.688,6-6v-3.415H1z"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.5 KiB |
@@ -0,0 +1,22 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="48px"
|
||||
height="48px" viewBox="0 0 48 48" enable-background="new 0 0 48 48" xml:space="preserve">
|
||||
<g id="Layer_1" display="none">
|
||||
<path display="inline" d="M47,41c0,3.313-2.687,6-6,6H7c-3.313,0-6-2.687-6-6V7c0-3.313,2.687-6,6-6h34c3.313,0,6,2.687,6,6V41z"/>
|
||||
<line display="inline" fill="none" stroke="#FFFFFF" stroke-width="4" stroke-miterlimit="10" x1="1" y1="25" x2="25" y2="25"/>
|
||||
<circle display="inline" fill="#FFFFFF" cx="24" cy="25" r="4.667"/>
|
||||
<line display="inline" fill="none" stroke="#FFFFFF" stroke-width="4" x1="34" y1="25" x2="48" y2="25"/>
|
||||
<path display="inline" fill="#FFFFFF" d="M24,13c-4.43,0-8.297,2.419-10.376,6h5.103c1.41-1.241,3.253-2,5.274-2
|
||||
c4.411,0,8,3.589,8,8s-3.589,8-8,8c-2.021,0-3.864-0.76-5.274-2h-5.103c2.079,3.581,5.946,6,10.376,6c6.617,0,12-5.383,12-12
|
||||
C36,18.383,30.617,13,24,13z"/>
|
||||
</g>
|
||||
<g id="Combined">
|
||||
<path d="M35.819,27C34.863,32.666,29.935,37,24,37c-4.43,0-8.298-2.419-10.376-6h5.103c1.41,1.24,3.253,2,5.274,2
|
||||
c4.411,0,8-3.589,8-8s-3.589-8-8-8c-2.021,0-3.864,0.759-5.274,2h-5.103c2.079-3.581,5.947-6,10.376-6
|
||||
c5.935,0,10.862,4.334,11.819,10H47V7c0-3.313-2.687-6-6-6H7C3.687,1,1,3.687,1,7v16h18.799c0.75-1.572,2.342-2.667,4.201-2.667
|
||||
c2.577,0,4.667,2.089,4.667,4.667c0,2.577-2.09,4.667-4.667,4.667c-1.858,0-3.45-1.095-4.201-2.667H1v14c0,3.313,2.687,6,6,6h34
|
||||
c3.313,0,6-2.687,6-6V27H35.819z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.7 KiB |
@@ -0,0 +1,57 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="48px"
|
||||
height="48px" viewBox="0 0 48 48" enable-background="new 0 0 48 48" xml:space="preserve">
|
||||
<g id="Layer_1" display="none">
|
||||
<circle cx="24" cy="23.999" r="23"/>
|
||||
<path display="inline" d="M47,41c0,3.313-2.687,6-6,6H7c-3.313,0-6-2.687-6-6V7c0-3.313,2.687-6,6-6h34c3.313,0,6,2.687,6,6V41z"/>
|
||||
<g display="inline">
|
||||
<circle fill="none" stroke="#FFFFFF" stroke-width="3" stroke-linecap="round" cx="9.938" cy="59.874" r="10.865"/>
|
||||
<circle fill="none" stroke="#FFFFFF" stroke-width="3" stroke-linecap="round" cx="9.938" cy="59.874" r="25.055"/>
|
||||
<circle fill="none" stroke="#FFFFFF" stroke-width="3" stroke-linecap="round" cx="9.938" cy="59.874" r="37.472"/>
|
||||
<circle fill="none" stroke="#FFFFFF" stroke-width="3" stroke-linecap="round" cx="9.938" cy="60.761" r="52.327"/>
|
||||
</g>
|
||||
<g display="inline">
|
||||
<circle fill="none" stroke="#FFFFFF" stroke-width="3" stroke-linecap="round" cx="50.938" cy="46.874" r="10.865"/>
|
||||
<circle fill="none" stroke="#FFFFFF" stroke-width="3" stroke-linecap="round" cx="50.938" cy="46.874" r="25.055"/>
|
||||
<circle fill="none" stroke="#FFFFFF" stroke-width="3" stroke-linecap="round" cx="50.938" cy="46.874" r="37.472"/>
|
||||
<circle fill="none" stroke="#FFFFFF" stroke-width="3" stroke-linecap="round" cx="50.938" cy="47.761" r="52.327"/>
|
||||
</g>
|
||||
</g>
|
||||
<g id="Composed">
|
||||
<g>
|
||||
<path d="M43.872,40.746c0.706,1.248,1.34,2.542,1.908,3.869C46.542,43.609,47,42.359,47,41v-2.618
|
||||
C45.798,38.944,44.733,39.755,43.872,40.746z"/>
|
||||
<path d="M34.719,29.817c2.885,2.382,5.421,5.171,7.522,8.277c1.326-1.313,2.952-2.32,4.759-2.931V26.06
|
||||
c-0.636-0.679-1.277-1.352-1.949-1.995C41.114,25.081,37.578,27.097,34.719,29.817z"/>
|
||||
<path d="M28.225,40.646c1.944,1.851,3.607,3.993,4.924,6.354h5.429c-0.001-0.042-0.005-0.084-0.005-0.126
|
||||
c0-2.287,0.635-4.423,1.722-6.263c-2.072-3.253-4.646-6.156-7.622-8.59C30.634,34.523,29.102,37.45,28.225,40.646z"/>
|
||||
<path d="M9.938,36.319c-3.144,0-6.144,0.624-8.889,1.747C1.03,38.161,1.018,38.259,1,38.354V41c0,3.313,2.687,6,6,6h4.969
|
||||
c0-0.042-0.003-0.084-0.003-0.126c0-3.566,0.491-7.019,1.393-10.304C12.241,36.407,11.1,36.319,9.938,36.319z"/>
|
||||
<path d="M24.383,46.874c0-1.773,0.178-3.506,0.512-5.184c-2.49-2.051-5.409-3.596-8.596-4.491
|
||||
c-0.862,3.08-1.333,6.323-1.333,9.675c0,0.042,0.003,0.084,0.003,0.126h9.417C24.386,46.958,24.383,46.916,24.383,46.874z"/>
|
||||
<path d="M45.951,20.795c0.347-0.066,0.697-0.123,1.049-0.175v-9.5c-3.709,0.408-7.249,1.382-10.535,2.832
|
||||
C39.874,15.893,43.058,18.185,45.951,20.795z"/>
|
||||
<path d="M42.103,43.797c-0.338,0.966-0.53,1.998-0.53,3.077c0,0.033,0.006,0.064,0.006,0.097c0.615-0.059,1.203-0.209,1.749-0.439
|
||||
C42.954,45.602,42.55,44.688,42.103,43.797z"/>
|
||||
<path d="M27.534,44.238c-0.097,0.866-0.151,1.744-0.151,2.636c0,0.042,0.006,0.084,0.006,0.126h2.236
|
||||
C28.989,46.032,28.301,45.101,27.534,44.238z"/>
|
||||
<path d="M33.163,12.209C37.389,10.034,42.061,8.618,47,8.116V7c0-3.313-2.687-6-6-6h-9.932c-4.141,1.77-8.006,4.064-11.5,6.813
|
||||
C24.346,8.681,28.911,10.167,33.163,12.209z"/>
|
||||
<path d="M7.774,20.965c0.716-0.04,1.437-0.063,2.163-0.063c3.709,0,7.295,0.532,10.696,1.504c2.637-3.26,5.789-6.083,9.331-8.354
|
||||
c-4.212-1.813-8.716-3.062-13.419-3.675C13.167,13.487,10.214,17.05,7.774,20.965z"/>
|
||||
<path d="M1,10.736v11.208c1.008-0.237,2.029-0.442,3.065-0.6c2.35-4.152,5.228-7.965,8.554-11.339
|
||||
c-0.888-0.046-1.782-0.072-2.682-0.072C6.887,9.934,3.904,10.219,1,10.736z"/>
|
||||
<path d="M9.938,6.934c1.904,0,3.785,0.103,5.639,0.297c2.699-2.358,5.641-4.441,8.771-6.23H7C3.687,1,1,3.687,1,7v0.689
|
||||
C3.909,7.201,6.892,6.934,9.938,6.934z"/>
|
||||
<path d="M5.96,24.127c-1.736,3.29-3.122,6.79-4.107,10.455c2.551-0.817,5.266-1.263,8.084-1.263c1.476,0,2.921,0.127,4.333,0.359
|
||||
c1.118-3.097,2.616-6.013,4.439-8.69c-2.809-0.707-5.747-1.086-8.772-1.086C8.593,23.902,7.267,23.982,5.96,24.127z"/>
|
||||
<path d="M1,27.711c0.406-1.01,0.834-2.007,1.299-2.985C1.863,24.821,1.431,24.927,1,25.038V27.711z"/>
|
||||
<path d="M17.221,34.344c3.104,0.887,5.979,2.319,8.508,4.197c1.013-3.056,2.563-5.866,4.544-8.318
|
||||
c-2.617-1.8-5.482-3.259-8.533-4.322C19.881,28.482,18.351,31.314,17.221,34.344z"/>
|
||||
<path d="M23.699,23.419c3.07,1.163,5.954,2.704,8.599,4.564c2.834-2.797,6.292-4.958,10.145-6.262
|
||||
c-2.834-2.364-5.933-4.417-9.237-6.126C29.603,17.646,26.389,20.299,23.699,23.419z"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 4.6 KiB |
@@ -0,0 +1,27 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="48px"
|
||||
height="48px" viewBox="0 0 48 48" enable-background="new 0 0 48 48" xml:space="preserve">
|
||||
<g id="Layer_1" display="none">
|
||||
<path display="inline" d="M47,41c0,3.313-2.687,6-6,6H7c-3.313,0-6-2.687-6-6V7c0-3.313,2.687-6,6-6h34c3.313,0,6,2.687,6,6V41z"/>
|
||||
<text transform="matrix(1 0 0 1 8.8398 22.1665)" display="inline" fill="#FFFFFF" font-family="'MyriadPro-Bold'" font-size="24">0</text>
|
||||
|
||||
<text transform="matrix(1 0 0 1 24.8398 22.1665)" display="inline" fill="#FFFFFF" font-family="'MyriadPro-Bold'" font-size="24">1</text>
|
||||
<text transform="matrix(1 0 0 1 25.3398 41.166)" display="inline" fill="#FFFFFF" font-family="'MyriadPro-Bold'" font-size="24">0</text>
|
||||
<text transform="matrix(1 0 0 1 8.3398 41.166)" display="inline" fill="#FFFFFF" font-family="'MyriadPro-Bold'" font-size="24">1</text>
|
||||
</g>
|
||||
<g id="Combined">
|
||||
<g>
|
||||
<path d="M32.013,28.062c-1.345,0-2.257,1.704-2.232,5.328c-0.024,3.577,0.84,5.28,2.256,5.28s2.185-1.775,2.185-5.328
|
||||
C34.221,29.886,33.477,28.062,32.013,28.062z"/>
|
||||
<path d="M41,1H7C3.687,1,1,3.687,1,7v34c0,3.313,2.687,6,6,6h34c3.313,0,6-2.687,6-6V7C47,3.687,44.313,1,41,1z M31.009,7H34v15
|
||||
h-3V10h-0.544l-2.976,1.428L26.88,8.703L31.009,7z M18,41h-4V29h-0.044l-2.976,1.428l-0.601-2.725L14.509,26H18V41z M15.44,22.431
|
||||
c-4.032,0-5.809-3.625-5.833-8.041c0-4.512,1.921-8.088,5.953-8.088c4.176,0,5.809,3.72,5.809,7.993
|
||||
C21.369,19.118,19.425,22.431,15.44,22.431z M31.94,41.431c-4.032,0-5.809-3.624-5.833-8.041c0-4.513,1.921-8.089,5.953-8.089
|
||||
c4.176,0,5.809,3.721,5.809,7.993C37.869,38.118,35.925,41.431,31.94,41.431z"/>
|
||||
<path d="M15.513,9.062c-1.345,0-2.257,1.704-2.232,5.328c-0.024,3.577,0.84,5.281,2.256,5.281s2.185-1.776,2.185-5.329
|
||||
C17.721,10.886,16.977,9.062,15.513,9.062z"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.1 KiB |
@@ -0,0 +1,30 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="48px"
|
||||
height="48px" viewBox="0 0 48 48" enable-background="new 0 0 48 48" xml:space="preserve">
|
||||
<g id="x">
|
||||
</g>
|
||||
<g id="Layer_1" display="none">
|
||||
<path display="inline" d="M47,41c0,3.313-2.688,6-6,6H7c-3.313,0-6-2.688-6-6V7c0-3.313,2.687-6,6-6h34c3.313,0,6,2.687,6,6V41z"/>
|
||||
<g display="inline">
|
||||
<line x1="24" y1="24" x2="31.413" y2="11.541"/>
|
||||
<g>
|
||||
<line fill="none" stroke="#FFFFFF" stroke-width="3" stroke-linecap="square" x1="24" y1="24" x2="28.11" y2="17.091"/>
|
||||
<g>
|
||||
<polygon fill="#FFFFFF" points="31.296,20.515 31.413,11.541 23.583,15.925 "/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<circle display="inline" fill="none" stroke="#FFFFFF" stroke-width="2" stroke-linecap="round" cx="24" cy="24" r="15.928"/>
|
||||
</g>
|
||||
<g id="Compound">
|
||||
<g>
|
||||
<path d="M41,1H7C3.687,1,1,3.687,1,7v34c0,3.313,2.687,6,6,6h34c3.313,0,6-2.688,6-6V7C47,3.687,44.313,1,41,1z M24,40.928
|
||||
c-9.334,0-16.928-7.594-16.928-16.928S14.666,7.072,24,7.072S40.928,14.666,40.928,24S33.334,40.928,24,40.928z"/>
|
||||
<path d="M24,9.072C15.769,9.072,9.072,15.769,9.072,24c0,8.231,6.697,14.928,14.928,14.928c8.231,0,14.928-6.696,14.928-14.928
|
||||
C38.928,15.769,32.231,9.072,24,9.072z M31.296,20.515l-2.568-1.528l-4.205,7.069l-2.579-1.533l4.206-7.07l-2.567-1.527
|
||||
l7.831-4.384L31.296,20.515z"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.6 KiB |
@@ -0,0 +1,25 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="48px"
|
||||
height="48px" viewBox="0 0 48 48" enable-background="new 0 0 48 48" xml:space="preserve">
|
||||
<g id="x">
|
||||
</g>
|
||||
<g id="Layer_1">
|
||||
<path d="M47,41c0,3.313-2.688,6-6,6H7c-3.313,0-6-2.688-6-6V7c0-3.313,2.687-6,6-6h34c3.313,0,6,2.687,6,6V41z"/>
|
||||
<line fill="none" stroke="#FFFFFF" stroke-width="3" stroke-linecap="square" x1="10.167" y1="32.584" x2="38.584" y2="32.584"/>
|
||||
<line fill="none" stroke="#FFFFFF" stroke-width="3" stroke-linecap="square" x1="12.5" y1="32.584" x2="12.5" y2="12"/>
|
||||
<line fill="none" stroke="#FFFFFF" stroke-width="3" stroke-linecap="square" x1="20.5" y1="32.584" x2="20.5" y2="19"/>
|
||||
<line fill="none" stroke="#FFFFFF" stroke-width="3" stroke-linecap="square" x1="28.5" y1="32.584" x2="28.5" y2="15"/>
|
||||
<line fill="none" stroke="#FFFFFF" stroke-width="3" stroke-linecap="square" x1="36.5" y1="32.584" x2="36.5" y2="23"/>
|
||||
</g>
|
||||
<g id="Compound" display="none">
|
||||
<g display="inline">
|
||||
<path d="M41,1H7C3.687,1,1,3.687,1,7v34c0,3.313,2.687,6,6,6h34c3.313,0,6-2.688,6-6V7C47,3.687,44.313,1,41,1z M24,40.928
|
||||
c-9.334,0-16.928-7.594-16.928-16.928S14.666,7.072,24,7.072S40.928,14.666,40.928,24S33.334,40.928,24,40.928z"/>
|
||||
<path d="M24,9.072C15.769,9.072,9.072,15.769,9.072,24c0,8.231,6.697,14.928,14.928,14.928c8.231,0,14.928-6.696,14.928-14.928
|
||||
C38.928,15.769,32.231,9.072,24,9.072z M31.296,20.515l-2.568-1.528l-4.205,7.069l-2.579-1.533l4.206-7.07l-2.567-1.527
|
||||
l7.831-4.384L31.296,20.515z"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.8 KiB |
@@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="48px"
|
||||
height="48px" viewBox="0 0 48 48" enable-background="new 0 0 48 48" xml:space="preserve">
|
||||
<g id="Layer_1" display="none">
|
||||
<path display="inline" fill="#004000" d="M47,41c0,3.313-2.688,6-6,6H7c-3.313,0-6-2.688-6-6V7c0-3.313,2.687-6,6-6h34
|
||||
c3.313,0,6,2.687,6,6V41z"/>
|
||||
<text transform="matrix(1 0 0 1 11.7305 36.5205)" display="inline" fill="#FFFFFF" font-family="'Roboto-Black'" font-size="36">A</text>
|
||||
</g>
|
||||
<g id="Composed">
|
||||
<g>
|
||||
<polygon fill="#004000" points="21.223,26.976 26.725,26.976 23.965,18.099 "/>
|
||||
<path fill="#004000" d="M41,1H7C3.687,1,1,3.687,1,7v34c0,3.313,2.687,6,6,6h34c3.313,0,6-2.688,6-6V7C47,3.687,44.313,1,41,1z
|
||||
M29.695,36.521l-1.494-4.781h-8.455l-1.477,4.781h-6.592l9.387-25.594h5.801l9.457,25.594H29.695z"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.1 KiB |
@@ -0,0 +1,23 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="48px"
|
||||
height="48px" viewBox="0 0 48 48" enable-background="new 0 0 48 48" xml:space="preserve">
|
||||
<g id="Layer_1" display="none">
|
||||
<path display="inline" fill="#8A0000" d="M47,41c0,3.313-2.688,6-6,6H7c-3.313,0-6-2.688-6-6V7c0-3.313,2.687-6,6-6h34
|
||||
c3.313,0,6,2.687,6,6V41z"/>
|
||||
<text transform="matrix(1 0 0 1 12.3809 36.5205)" display="inline" fill="#FFFFFF" font-family="'Roboto-Black'" font-size="36">B</text>
|
||||
</g>
|
||||
<g id="Composed">
|
||||
<g>
|
||||
<path fill="#8A0000" d="M24.809,25.64h-4.324v6.135h4.043c1.113,0,1.969-0.255,2.566-0.765s0.896-1.228,0.896-2.153
|
||||
C27.99,26.724,26.93,25.651,24.809,25.64z"/>
|
||||
<path fill="#8A0000" d="M26.584,20.856c0.574-0.482,0.861-1.193,0.861-2.133c0-1.082-0.311-1.86-0.932-2.336
|
||||
c-0.621-0.476-1.623-0.714-3.006-0.714h-3.023v5.924h3.217C25.049,21.585,26.01,21.338,26.584,20.856z"/>
|
||||
<path fill="#8A0000" d="M41,1H7C3.687,1,1,3.687,1,7v34c0,3.313,2.687,6,6,6h34c3.313,0,6-2.688,6-6V7C47,3.687,44.313,1,41,1z
|
||||
M31.761,34.596c-1.588,1.26-3.94,1.901-7.058,1.925H14.314V10.927h9.193c3.293,0,5.798,0.604,7.515,1.811
|
||||
s2.575,2.959,2.575,5.256c0,1.324-0.305,2.455-0.914,3.393s-1.506,1.629-2.689,2.074c1.336,0.352,2.361,1.008,3.076,1.969
|
||||
s1.072,2.133,1.072,3.516C34.143,31.452,33.349,33.336,31.761,34.596z"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.6 KiB |
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
width="48px" height="48px" viewBox="0 0 48 48" enable-background="new 0 0 48 48" xml:space="preserve">
|
||||
<circle cx="24" cy="24" r="14"/>
|
||||
<path display="none" fill="none" stroke="#000000" stroke-width="3" d="M45.5,39.392c0,3.098-2.011,6.108-5.108,6.108H8.609
|
||||
c-3.098,0-6.109-3.011-6.109-6.108V7.609C2.5,4.511,5.511,2.5,8.609,2.5h31.783c3.098,0,5.108,2.011,5.108,5.109V39.392z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 755 B |
@@ -0,0 +1,19 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="48px"
|
||||
height="48px" viewBox="0 0 48 48" enable-background="new 0 0 48 48" xml:space="preserve">
|
||||
<g id="Layer_1" display="none">
|
||||
<path display="inline" d="M47,41c0,3.313-2.687,6-6,6H7c-3.313,0-6-2.687-6-6V7c0-3.313,2.687-6,6-6h34c3.313,0,6,2.687,6,6V41z"/>
|
||||
<path display="inline" fill="#FFFFFF" d="M37.636,33.571L25.244,21.105c1.226-3.152,0.545-6.849-2.042-9.453
|
||||
c-2.723-2.739-6.808-3.288-10.075-1.78l5.855,5.89l-4.085,4.11l-5.992-5.891c-1.633,3.287-0.952,7.397,1.771,10.136
|
||||
c2.586,2.603,6.263,3.289,9.394,2.055L32.461,38.64c0.545,0.547,1.362,0.547,1.906,0l3.131-3.15
|
||||
C38.18,34.94,38.18,33.981,37.636,33.571z"/>
|
||||
</g>
|
||||
<g id="Combined">
|
||||
<path d="M41,1H7C3.687,1,1,3.687,1,7v34c0,3.313,2.687,6,6,6h34c3.313,0,6-2.687,6-6V7C47,3.687,44.313,1,41,1z M37.498,35.489
|
||||
l-3.131,3.15c-0.544,0.547-1.361,0.547-1.906,0L20.07,26.173c-3.131,1.233-6.808,0.548-9.394-2.055
|
||||
c-2.723-2.739-3.404-6.849-1.771-10.136l5.992,5.891l4.085-4.11l-5.855-5.89c3.267-1.507,7.352-0.959,10.075,1.78
|
||||
c2.587,2.604,3.268,6.301,2.042,9.453l12.392,12.466C38.18,33.981,38.18,34.94,37.498,35.489z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.4 KiB |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24" width="24px" fill="#000000"><path d="M0 0h24v24H0V0z" fill="none"/><path d="M11 18h2v-2h-2v2zm1-16C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8zm0-14c-2.21 0-4 1.79-4 4h2c0-1.1.9-2 2-2s2 .9 2 2c0 2-3 1.75-3 5h2c0-2.25 3-2.5 3-5 0-2.21-1.79-4-4-4z"/></svg>
|
||||
|
After Width: | Height: | Size: 402 B |
@@ -0,0 +1,14 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
width="24px" height="24px" viewBox="0 0 24 24" enable-background="new 0 0 24 24" xml:space="preserve">
|
||||
<path d="M18.993,8.993L18.99,5c0-1.1-0.891-2-1.99-2h-4h-3H6C4.9,3,4.01,3.9,4.01,5L4.007,8.993L4,9v10c0,1.1,0.891,2,1.99,2H6h11
|
||||
h0.01c1.1,0,1.99-0.9,1.99-2V9L18.993,8.993z M17,10v9H6v-9V5h5h1h5V10z"/>
|
||||
<rect x="8" y="7" width="2" height="2"/>
|
||||
<rect x="12" y="7" width="3" height="2"/>
|
||||
<rect x="8" y="11" width="2" height="2"/>
|
||||
<rect x="12" y="11" width="3" height="2"/>
|
||||
<rect x="8" y="15" width="2" height="2"/>
|
||||
<rect x="12" y="15" width="3" height="2"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 939 B |
|
After Width: | Height: | Size: 870 KiB |
@@ -0,0 +1,42 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
width="218px" height="64px" viewBox="0 0 218 64" enable-background="new 0 0 218 64" xml:space="preserve">
|
||||
<rect x="-1" y="1" display="none" fill="#0032B1" width="219" height="64"/>
|
||||
<g>
|
||||
<path fill="#FFFFFF" d="M55.069,23.069c2.359,2.28,3.54,5.4,3.54,9.36c0,1.24-0.12,2.521-0.36,3.84
|
||||
c-0.6,3.4-1.791,6.351-3.57,8.851c-1.78,2.5-3.98,4.42-6.6,5.76c-2.62,1.341-5.47,2.01-8.55,2.01c-3.76,0-6.75-0.999-8.97-3
|
||||
c-2.22-2-3.471-4.62-3.75-7.86l-4.56,25.681h-1.14l8.4-47.64h1.14l-1.8,10.44c1.44-3.24,3.62-5.859,6.54-7.86
|
||||
c2.919-2,6.26-3,10.02-3C49.489,19.649,52.708,20.789,55.069,23.069z M51.049,47.639c3.12-2.779,5.12-6.57,6-11.37
|
||||
c0.24-1.319,0.36-2.56,0.36-3.72c0-3.72-1.08-6.62-3.24-8.7c-2.16-2.079-5.16-3.12-9-3.12c-2.8,0-5.421,0.651-7.86,1.95
|
||||
c-2.44,1.3-4.5,3.12-6.18,5.46c-1.68,2.34-2.781,5.051-3.3,8.13c-0.201,1.001-0.3,2.141-0.3,3.42c0,3.721,1.1,6.671,3.3,8.851
|
||||
c2.2,2.181,5.16,3.27,8.88,3.27C44.149,51.809,47.929,50.419,51.049,47.639z"/>
|
||||
<path fill="#FFFFFF" d="M70.788,20.069l-5.76,32.4h-1.2l5.76-32.4H70.788z M70.878,9.089c0.38-0.36,0.83-0.54,1.35-0.54
|
||||
c0.36,0,0.669,0.12,0.93,0.36c0.259,0.24,0.39,0.54,0.39,0.9c0,0.6-0.19,1.08-0.57,1.44c-0.381,0.36-0.831,0.54-1.35,0.54
|
||||
c-0.36,0-0.67-0.12-0.93-0.36c-0.261-0.24-0.39-0.54-0.39-0.9C70.308,9.929,70.498,9.449,70.878,9.089z"/>
|
||||
<path fill="#FFFFFF" d="M112.248,8.069l-7.86,44.4h-1.14l1.86-10.44c-1.44,3.24-3.621,5.86-6.54,7.86c-2.92,2.001-6.261,3-10.02,3
|
||||
c-4.08,0-7.3-1.14-9.66-3.42c-2.36-2.28-3.54-5.4-3.54-9.36c0-1.239,0.12-2.52,0.36-3.84c0.6-3.399,1.779-6.35,3.54-8.85
|
||||
c1.76-2.5,3.96-4.419,6.6-5.76c2.64-1.34,5.499-2.01,8.58-2.01c3.72,0,6.69,1,8.91,3c2.22,2,3.489,4.62,3.81,7.86l3.96-22.44
|
||||
H112.248z M82.848,24.899c-3.12,2.78-5.1,6.57-5.94,11.37c-0.24,1.32-0.36,2.561-0.36,3.72c0,3.721,1.07,6.621,3.21,8.7
|
||||
c2.14,2.081,5.13,3.12,8.97,3.12c2.799,0,5.43-0.649,7.89-1.95c2.46-1.299,4.52-3.12,6.18-5.46c1.66-2.34,2.77-5.049,3.33-8.13
|
||||
c0.2-1.319,0.3-2.438,0.3-3.36c0-3.72-1.11-6.68-3.33-8.88c-2.22-2.199-5.17-3.3-8.85-3.3
|
||||
C89.767,20.729,85.967,22.119,82.848,24.899z"/>
|
||||
<path fill="#FFFFFF" d="M152.507,8.069l-7.86,44.4h-1.14l1.86-10.44c-1.44,3.24-3.621,5.86-6.54,7.86c-2.92,2.001-6.261,3-10.02,3
|
||||
c-4.08,0-7.3-1.14-9.66-3.42c-2.36-2.28-3.54-5.4-3.54-9.36c0-1.239,0.12-2.52,0.36-3.84c0.6-3.399,1.779-6.35,3.54-8.85
|
||||
c1.76-2.5,3.96-4.419,6.6-5.76c2.64-1.34,5.499-2.01,8.58-2.01c3.72,0,6.69,1,8.91,3c2.22,2,3.489,4.62,3.81,7.86l3.96-22.44
|
||||
H152.507z M123.107,24.899c-3.12,2.78-5.1,6.57-5.94,11.37c-0.24,1.32-0.36,2.561-0.36,3.72c0,3.721,1.07,6.621,3.21,8.7
|
||||
c2.14,2.081,5.13,3.12,8.97,3.12c2.799,0,5.43-0.649,7.89-1.95c2.46-1.299,4.52-3.12,6.18-5.46c1.66-2.34,2.77-5.049,3.33-8.13
|
||||
c0.2-1.319,0.3-2.438,0.3-3.36c0-3.72-1.11-6.68-3.33-8.88c-2.22-2.199-5.17-3.3-8.85-3.3
|
||||
C130.026,20.729,126.227,22.119,123.107,24.899z"/>
|
||||
<path fill="#FFFFFF" d="M165.046,8.069l-7.8,44.4h-1.2l7.8-44.4H165.046z"/>
|
||||
<path fill="#FFFFFF" d="M194.176,21.239c1.898,1.06,3.318,2.451,4.26,4.17c0.939,1.72,1.41,3.621,1.41,5.7
|
||||
c0,0.52-0.08,1.34-0.24,2.459c-0.281,1.44-0.561,2.521-0.84,3.24h-29.04c-0.201,1.32-0.3,2.421-0.3,3.3
|
||||
c0,3.841,1.14,6.75,3.42,8.73c2.28,1.979,5.16,2.97,8.64,2.97c3.64,0,6.87-0.93,9.69-2.79c2.821-1.859,4.77-4.369,5.85-7.53h1.199
|
||||
c-1.16,3.4-3.238,6.15-6.239,8.25c-3,2.101-6.561,3.15-10.68,3.15c-3.96,0-7.12-1.13-9.48-3.39c-2.36-2.26-3.54-5.39-3.54-9.391
|
||||
c0-1.2,0.12-2.479,0.36-3.84c0.6-3.399,1.79-6.36,3.57-8.88c1.779-2.52,3.969-4.44,6.57-5.76c2.6-1.32,5.4-1.98,8.4-1.98
|
||||
C189.945,19.649,192.275,20.179,194.176,21.239z M198.705,31.529c0-3.56-1.119-6.25-3.359-8.07c-2.241-1.82-5.021-2.73-8.34-2.73
|
||||
c-2.56,0-5.021,0.551-7.38,1.65c-2.361,1.101-4.41,2.77-6.15,5.01c-1.74,2.241-2.91,5.021-3.51,8.34h28.201
|
||||
C198.525,34.169,198.705,32.769,198.705,31.529z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 4.1 KiB |
@@ -0,0 +1,14 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
width="24px" height="24px" viewBox="0 0 24 24" enable-background="new 0 0 24 24" xml:space="preserve">
|
||||
<path fill="none" d="M0,0h24v24H0V0z"/>
|
||||
<g>
|
||||
<path d="M12,2C6.48,2,2,6.48,2,12c0,5.52,4.48,10,10,10c5.52,0,10-4.48,10-10C22,6.48,17.52,2,12,2z M12,20c-4.41,0-8-3.59-8-8
|
||||
c0-3.537,2.311-6.541,5.5-7.594v1.815h5V4.406C17.689,5.459,20,8.463,20,12C20,16.41,16.41,20,12,20z"/>
|
||||
<circle cx="12" cy="16.242" r="1.5"/>
|
||||
<circle cx="7.757" cy="13.5" r="1.5"/>
|
||||
<circle cx="16.243" cy="13.5" r="1.5"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 882 B |
@@ -0,0 +1,14 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
width="24px" height="24px" viewBox="0 0 24 24" enable-background="new 0 0 24 24" xml:space="preserve">
|
||||
<path d="M17.493,10.993L17.49,7c0-1.1-0.891-2-1.99-2h-4h-3h-4C3.4,5,2.51,5.9,2.51,7l-0.003,3.993L2.5,11v10c0,1.1,0.891,2,1.99,2
|
||||
H4.5h11h0.01c1.1,0,1.99-0.9,1.99-2V11L17.493,10.993z M15.5,12v9h-11v-9V7h5h1h5V12z"/>
|
||||
<path d="M20.493,7.993L20.49,4c0-1.1-0.891-2-1.99-2h-4h-3h-4C6.4,2,5.51,2.9,5.51,4H7.5h5h1h5v5v9v2h0.01c1.1,0,1.99-0.9,1.99-2V8
|
||||
L20.493,7.993z"/>
|
||||
<line fill="none" stroke="#000000" stroke-width="2" stroke-linecap="round" stroke-miterlimit="10" x1="4" y1="14" x2="10" y2="14"/>
|
||||
<line fill="none" stroke="#000000" stroke-width="2" stroke-linecap="round" stroke-miterlimit="10" x1="13.259" y1="14" x2="16.064" y2="14"/>
|
||||
<path d="M10,10c-1.474,0-2.75,0.81-3.443,2H10c1.103,0,2,0.897,2,2s-0.897,2-2,2H6.556c0.694,1.189,1.97,2,3.444,2
|
||||
c2.206,0,4-1.794,4-4C14,11.794,12.206,10,10,10z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.3 KiB |
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
width="24px" height="24px" viewBox="0 0 24 24" enable-background="new 0 0 24 24" xml:space="preserve">
|
||||
<path d="M20.953,7.993L20.949,4c0-1.1-0.891-2-1.99-2h-4h-3h-3c-1.1,0-1.99,0.9-1.99,2h1.99h4h1h5h0.006v16h0.004
|
||||
c1.1,0,1.99-0.9,1.99-2V8L20.953,7.993z"/>
|
||||
<path d="M17.993,10.993L17.99,7c0-1.1-0.891-2-1.99-2h-4H9H6C4.9,5,4.01,5.9,4.01,7l-0.003,3.993L4,11v10c0,1.1,0.891,2,1.99,2H6h10
|
||||
h0.01c1.1,0,1.99-0.9,1.99-2V11L17.993,10.993z M16,12v9H6v-9V7h4h1h5V12z"/>
|
||||
<rect x="8" y="9" width="6" height="2"/>
|
||||
<circle cx="11" cy="15.859" r="2"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 919 B |
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
width="24px" height="24px" viewBox="0 0 24 24" enable-background="new 0 0 24 24" xml:space="preserve">
|
||||
<path fill="none" d="M0,0h24v24H0V0z"/>
|
||||
<g>
|
||||
<path d="M20,4h-4.5l-1-1h-5l-1,1H4v2h1v13c0,1.1,0.9,2,2,2h10c1.1,0,2-0.9,2-2V6h1V4z M17,19H7V6h10V19z"/>
|
||||
<rect x="9" y="8" width="2" height="8"/>
|
||||
<rect x="13" y="8" width="2" height="8"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 723 B |
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
width="24px" height="24px" fill="white" viewBox="0 0 24 24" enable-background="new 0 0 24 24" xml:space="preserve">
|
||||
<path fill="none" d="M0,0h24v24H0V0z"/>
|
||||
<g>
|
||||
<path d="M20,4h-4.5l-1-1h-5l-1,1H4v2h1v13c0,1.1,0.9,2,2,2h10c1.1,0,2-0.9,2-2V6h1V4z M17,19H7V6h10V19z"/>
|
||||
<rect x="9" y="8" width="2" height="8"/>
|
||||
<rect x="13" y="8" width="2" height="8"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 736 B |
@@ -0,0 +1,15 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
width="24px" height="24px" viewBox="0 0 24 24" enable-background="new 0 0 24 24" xml:space="preserve">
|
||||
<path d="M16,1H4C2.9,1,2,1.9,2,3v14h2V3h12V1z"/>
|
||||
<path d="M20.993,10.993L20.99,7c0-1.1-0.891-2-1.99-2h-4h-3H8C6.9,5,6.01,5.9,6.01,7l-0.003,3.993L6,11v10c0,1.1,0.891,2,1.99,2H8
|
||||
h11h0.01c1.1,0,1.99-0.9,1.99-2V11L20.993,10.993z M19,12v9H8v-9V7h5h1h5V12z"/>
|
||||
<rect x="10" y="9" width="2" height="2"/>
|
||||
<rect x="14" y="9" width="3" height="2"/>
|
||||
<rect x="10" y="13" width="2" height="2"/>
|
||||
<rect x="14" y="13" width="3" height="2"/>
|
||||
<rect x="10" y="17" width="2" height="2"/>
|
||||
<rect x="14" y="17" width="3" height="2"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 997 B |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24" width="24px" fill="#000000"><path d="M0 0h24v24H0V0z" fill="none"/><path d="M19.43 12.98c.04-.32.07-.64.07-.98 0-.34-.03-.66-.07-.98l2.11-1.65c.19-.15.24-.42.12-.64l-2-3.46c-.09-.16-.26-.25-.44-.25-.06 0-.12.01-.17.03l-2.49 1c-.52-.4-1.08-.73-1.69-.98l-.38-2.65C14.46 2.18 14.25 2 14 2h-4c-.25 0-.46.18-.49.42l-.38 2.65c-.61.25-1.17.59-1.69.98l-2.49-1c-.06-.02-.12-.03-.18-.03-.17 0-.34.09-.43.25l-2 3.46c-.13.22-.07.49.12.64l2.11 1.65c-.04.32-.07.65-.07.98 0 .33.03.66.07.98l-2.11 1.65c-.19.15-.24.42-.12.64l2 3.46c.09.16.26.25.44.25.06 0 .12-.01.17-.03l2.49-1c.52.4 1.08.73 1.69.98l.38 2.65c.03.24.24.42.49.42h4c.25 0 .46-.18.49-.42l.38-2.65c.61-.25 1.17-.59 1.69-.98l2.49 1c.06.02.12.03.18.03.17 0 .34-.09.43-.25l2-3.46c.12-.22.07-.49-.12-.64l-2.11-1.65zm-1.98-1.71c.04.31.05.52.05.73 0 .21-.02.43-.05.73l-.14 1.13.89.7 1.08.84-.7 1.21-1.27-.51-1.04-.42-.9.68c-.43.32-.84.56-1.25.73l-1.06.43-.16 1.13-.2 1.35h-1.4l-.19-1.35-.16-1.13-1.06-.43c-.43-.18-.83-.41-1.23-.71l-.91-.7-1.06.43-1.27.51-.7-1.21 1.08-.84.89-.7-.14-1.13c-.03-.31-.05-.54-.05-.74s.02-.43.05-.73l.14-1.13-.89-.7-1.08-.84.7-1.21 1.27.51 1.04.42.9-.68c.43-.32.84-.56 1.25-.73l1.06-.43.16-1.13.2-1.35h1.39l.19 1.35.16 1.13 1.06.43c.43.18.83.41 1.23.71l.91.7 1.06-.43 1.27-.51.7 1.21-1.07.85-.89.7.14 1.13zM12 8c-2.21 0-4 1.79-4 4s1.79 4 4 4 4-1.79 4-4-1.79-4-4-4zm0 6c-1.1 0-2-.9-2-2s.9-2 2-2 2 .9 2 2-.9 2-2 2z"/></svg>
|
||||
|
After Width: | Height: | Size: 1.4 KiB |
@@ -0,0 +1,44 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<meta name="theme-color" content="#000000" />
|
||||
<meta
|
||||
name="description"
|
||||
content="PiPedal Guitar Pedals"
|
||||
/>
|
||||
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700&display=swap" />
|
||||
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
|
||||
<!--
|
||||
manifest.json provides metadata used when your web app is installed on a
|
||||
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
|
||||
-->
|
||||
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
|
||||
<!--
|
||||
Notice the use of %PUBLIC_URL% in the tags above.
|
||||
It will be replaced with the URL of the `public` folder during the build.
|
||||
Only files inside the `public` folder can be referenced from the HTML.
|
||||
|
||||
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
|
||||
work correctly both with client-side routing and a non-root public URL.
|
||||
Learn how to configure a non-root public URL by running `npm run build`.
|
||||
-->
|
||||
<title>PiPedal</title>
|
||||
</head>
|
||||
<body>
|
||||
<noscript>You need to enable JavaScript to run this app.</noscript>
|
||||
<div id="root"></div>
|
||||
<!--
|
||||
This HTML file is a template.
|
||||
If you open it directly in the browser, you will see an empty page.
|
||||
|
||||
You can add webfonts, meta tags, or analytics to this file.
|
||||
The build step will place the bundled scripts into the <body> tag.
|
||||
|
||||
To begin the development, run `npm start` or `yarn start`.
|
||||
To create a production bundle, use `npm run build` or `yarn build`.
|
||||
-->
|
||||
</body>
|
||||
</html>
|
||||
|
After Width: | Height: | Size: 2.7 KiB |
|
After Width: | Height: | Size: 2.2 KiB |
|
After Width: | Height: | Size: 9.6 KiB |
@@ -0,0 +1,25 @@
|
||||
{
|
||||
"short_name": "PiPedal",
|
||||
"name": "PiPedal",
|
||||
"icons": [
|
||||
{
|
||||
"src": "favicon.ico",
|
||||
"sizes": "64x64 32x32 24x24 16x16",
|
||||
"type": "image/x-icon"
|
||||
},
|
||||
{
|
||||
"src": "logo192.png",
|
||||
"type": "image/png",
|
||||
"sizes": "192x192"
|
||||
},
|
||||
{
|
||||
"src": "logo512.png",
|
||||
"type": "image/png",
|
||||
"sizes": "512x512"
|
||||
}
|
||||
],
|
||||
"start_url": ".",
|
||||
"display": "standalone",
|
||||
"theme_color": "#000000",
|
||||
"background_color": "#ffffff"
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
# https://www.robotstxt.org/robotstxt.html
|
||||
User-agent: *
|
||||
Disallow:
|
||||
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"directoryListing": false
|
||||
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"socket_server_port": 80,
|
||||
"socket_server_address": "*",
|
||||
|
||||
"max_upload_size": 1048576,
|
||||
|
||||
"ui_plugins": [
|
||||
|
||||
]
|
||||
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
{
|
||||
"name": "Untitled",
|
||||
"items": [
|
||||
{
|
||||
"uri": "uri://two-play/piddle/pedalboard#Empty",
|
||||
"isEnabled": true,
|
||||
"controlValues": []
|
||||
|
||||
},
|
||||
{
|
||||
"uri": "uri://two-play/piddle/pedalboard#Empty",
|
||||
"controlValues": []
|
||||
},
|
||||
{
|
||||
"uri": "uri://two-play/piddle/pedalboard#Split",
|
||||
"isEnabled": true,
|
||||
"controlValues": [
|
||||
{ "key": "splitType", "value": 0},
|
||||
{ "key": "select", "value": 1},
|
||||
{ "key": "mix", "value": 0.3}
|
||||
],
|
||||
|
||||
"topChain": [
|
||||
{
|
||||
"uri": "uri://two-play/piddle/pedalboard#Empty",
|
||||
"isEnabled": true,
|
||||
"controlValues": []
|
||||
},
|
||||
{
|
||||
|
||||
"uri": "uri://two-play/piddle/pedalboard#Split",
|
||||
"isEnabled": true,
|
||||
"controlValues": [
|
||||
{ "key": "splitType", "value": 0},
|
||||
{ "key": "select", "value": 1},
|
||||
{ "key": "mix", "value": 0.3}
|
||||
],
|
||||
"topChain": [
|
||||
{
|
||||
"uri": "uri://two-play/piddle/pedalboard#Empty",
|
||||
"isEnabled": true,
|
||||
"controlValues": []
|
||||
}
|
||||
],
|
||||
"bottomChain": [
|
||||
{
|
||||
"uri": "uri://two-play/piddle/pedalboard#Empty",
|
||||
"isEnabled": true,
|
||||
"controlValues": []
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
],
|
||||
"bottomChain": [
|
||||
{
|
||||
"uri": "uri://two-play/piddle/pedalboard#Empty",
|
||||
"isEnabled": true,
|
||||
"controlValues": []
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"uri": "uri://two-play/piddle/pedalboard#Empty",
|
||||
"isEnabled": true,
|
||||
"controlValues": []
|
||||
},
|
||||
{
|
||||
"uri": "uri://two-play/piddle/pedalboard#Empty",
|
||||
"isEnabled": true,
|
||||
"controlValues": []
|
||||
}
|
||||
|
||||
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,276 @@
|
||||
{
|
||||
"uri": "http://lv2plug.in/ns/lv2core#Plugin",
|
||||
"display_name": "Plugin",
|
||||
"parent_uri": "http://lv2plug.in/ns/lv2core#PluginBase",
|
||||
"plugin_type": "Plugin",
|
||||
"children": [
|
||||
{
|
||||
"uri": "http://lv2plug.in/ns/lv2core#DelayPlugin",
|
||||
"display_name": "Delay",
|
||||
"parent_uri": "http://lv2plug.in/ns/lv2core#Plugin",
|
||||
"plugin_type": "DelayPlugin",
|
||||
"children": [
|
||||
{
|
||||
"uri": "http://lv2plug.in/ns/lv2core#ReverbPlugin",
|
||||
"display_name": "Reverb",
|
||||
"parent_uri": "http://lv2plug.in/ns/lv2core#DelayPlugin",
|
||||
"plugin_type": "ReverbPlugin",
|
||||
"children": []
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"uri": "http://lv2plug.in/ns/lv2core#DistortionPlugin",
|
||||
"display_name": "Distortion",
|
||||
"parent_uri": "http://lv2plug.in/ns/lv2core#Plugin",
|
||||
"plugin_type": "DistortionPlugin",
|
||||
"children": [
|
||||
{
|
||||
"uri": "http://lv2plug.in/ns/lv2core#WaveshaperPlugin",
|
||||
"display_name": "Waveshaper",
|
||||
"parent_uri": "http://lv2plug.in/ns/lv2core#DistortionPlugin",
|
||||
"plugin_type": "WaveshaperPlugin",
|
||||
"children": []
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"uri": "http://lv2plug.in/ns/lv2core#DynamicsPlugin",
|
||||
"display_name": "Dynamics",
|
||||
"parent_uri": "http://lv2plug.in/ns/lv2core#Plugin",
|
||||
"plugin_type": "DynamicsPlugin",
|
||||
"children": [
|
||||
{
|
||||
"uri": "http://lv2plug.in/ns/lv2core#AmplifierPlugin",
|
||||
"display_name": "Amplifier",
|
||||
"parent_uri": "http://lv2plug.in/ns/lv2core#DynamicsPlugin",
|
||||
"plugin_type": "AmplifierPlugin",
|
||||
"children": []
|
||||
},
|
||||
{
|
||||
"uri": "http://lv2plug.in/ns/lv2core#CompressorPlugin",
|
||||
"display_name": "Compressor",
|
||||
"parent_uri": "http://lv2plug.in/ns/lv2core#DynamicsPlugin",
|
||||
"plugin_type": "CompressorPlugin",
|
||||
"children": []
|
||||
},
|
||||
{
|
||||
"uri": "http://lv2plug.in/ns/lv2core#EnvelopePlugin",
|
||||
"display_name": "Envelope",
|
||||
"parent_uri": "http://lv2plug.in/ns/lv2core#DynamicsPlugin",
|
||||
"plugin_type": "EnvelopePlugin",
|
||||
"children": []
|
||||
},
|
||||
{
|
||||
"uri": "http://lv2plug.in/ns/lv2core#ExpanderPlugin",
|
||||
"display_name": "Expander",
|
||||
"parent_uri": "http://lv2plug.in/ns/lv2core#DynamicsPlugin",
|
||||
"plugin_type": "ExpanderPlugin",
|
||||
"children": []
|
||||
},
|
||||
{
|
||||
"uri": "http://lv2plug.in/ns/lv2core#GatePlugin",
|
||||
"display_name": "Gate",
|
||||
"parent_uri": "http://lv2plug.in/ns/lv2core#DynamicsPlugin",
|
||||
"plugin_type": "GatePlugin",
|
||||
"children": []
|
||||
},
|
||||
{
|
||||
"uri": "http://lv2plug.in/ns/lv2core#LimiterPlugin",
|
||||
"display_name": "Limiter",
|
||||
"parent_uri": "http://lv2plug.in/ns/lv2core#DynamicsPlugin",
|
||||
"plugin_type": "LimiterPlugin",
|
||||
"children": []
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"uri": "http://lv2plug.in/ns/lv2core#FilterPlugin",
|
||||
"display_name": "Filter",
|
||||
"parent_uri": "http://lv2plug.in/ns/lv2core#Plugin",
|
||||
"plugin_type": "FilterPlugin",
|
||||
"children": [
|
||||
{
|
||||
"uri": "http://lv2plug.in/ns/lv2core#AllpassPlugin",
|
||||
"display_name": "Allpass",
|
||||
"parent_uri": "http://lv2plug.in/ns/lv2core#FilterPlugin",
|
||||
"plugin_type": "AllpassPlugin",
|
||||
"children": []
|
||||
},
|
||||
{
|
||||
"uri": "http://lv2plug.in/ns/lv2core#BandpassPlugin",
|
||||
"display_name": "Bandpass",
|
||||
"parent_uri": "http://lv2plug.in/ns/lv2core#FilterPlugin",
|
||||
"plugin_type": "BandpassPlugin",
|
||||
"children": []
|
||||
},
|
||||
{
|
||||
"uri": "http://lv2plug.in/ns/lv2core#CombPlugin",
|
||||
"display_name": "Comb",
|
||||
"parent_uri": "http://lv2plug.in/ns/lv2core#FilterPlugin",
|
||||
"plugin_type": "CombPlugin",
|
||||
"children": []
|
||||
},
|
||||
{
|
||||
"uri": "http://lv2plug.in/ns/lv2core#EQPlugin",
|
||||
"display_name": "Equaliser",
|
||||
"parent_uri": "http://lv2plug.in/ns/lv2core#FilterPlugin",
|
||||
"plugin_type": "EQPlugin",
|
||||
"children": [
|
||||
{
|
||||
"uri": "http://lv2plug.in/ns/lv2core#MultiEQPlugin",
|
||||
"display_name": "Multiband",
|
||||
"parent_uri": "http://lv2plug.in/ns/lv2core#EQPlugin",
|
||||
"plugin_type": "MultiEQPlugin",
|
||||
"children": []
|
||||
},
|
||||
{
|
||||
"uri": "http://lv2plug.in/ns/lv2core#ParaEQPlugin",
|
||||
"display_name": "Parametric",
|
||||
"parent_uri": "http://lv2plug.in/ns/lv2core#EQPlugin",
|
||||
"plugin_type": "ParaEQPlugin",
|
||||
"children": []
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"uri": "http://lv2plug.in/ns/lv2core#HighpassPlugin",
|
||||
"display_name": "Highpass",
|
||||
"parent_uri": "http://lv2plug.in/ns/lv2core#FilterPlugin",
|
||||
"plugin_type": "HighpassPlugin",
|
||||
"children": []
|
||||
},
|
||||
{
|
||||
"uri": "http://lv2plug.in/ns/lv2core#LowpassPlugin",
|
||||
"display_name": "Lowpass",
|
||||
"parent_uri": "http://lv2plug.in/ns/lv2core#FilterPlugin",
|
||||
"plugin_type": "LowpassPlugin",
|
||||
"children": []
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"uri": "http://lv2plug.in/ns/lv2core#GeneratorPlugin",
|
||||
"display_name": "Generator",
|
||||
"parent_uri": "http://lv2plug.in/ns/lv2core#Plugin",
|
||||
"plugin_type": "GeneratorPlugin",
|
||||
"children": [
|
||||
{
|
||||
"uri": "http://lv2plug.in/ns/lv2core#ConstantPlugin",
|
||||
"display_name": "Constant",
|
||||
"parent_uri": "http://lv2plug.in/ns/lv2core#GeneratorPlugin",
|
||||
"plugin_type": "ConstantPlugin",
|
||||
"children": []
|
||||
},
|
||||
{
|
||||
"uri": "http://lv2plug.in/ns/lv2core#InstrumentPlugin",
|
||||
"display_name": "Instrument",
|
||||
"parent_uri": "http://lv2plug.in/ns/lv2core#GeneratorPlugin",
|
||||
"plugin_type": "InstrumentPlugin",
|
||||
"children": []
|
||||
},
|
||||
{
|
||||
"uri": "http://lv2plug.in/ns/lv2core#OscillatorPlugin",
|
||||
"display_name": "Oscillator",
|
||||
"parent_uri": "http://lv2plug.in/ns/lv2core#GeneratorPlugin",
|
||||
"plugin_type": "OscillatorPlugin",
|
||||
"children": []
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"uri": "http://lv2plug.in/ns/lv2core#ModulatorPlugin",
|
||||
"display_name": "Modulator",
|
||||
"parent_uri": "http://lv2plug.in/ns/lv2core#Plugin",
|
||||
"plugin_type": "ModulatorPlugin",
|
||||
"children": [
|
||||
{
|
||||
"uri": "http://lv2plug.in/ns/lv2core#ChorusPlugin",
|
||||
"display_name": "Chorus",
|
||||
"parent_uri": "http://lv2plug.in/ns/lv2core#ModulatorPlugin",
|
||||
"plugin_type": "ChorusPlugin",
|
||||
"children": []
|
||||
},
|
||||
{
|
||||
"uri": "http://lv2plug.in/ns/lv2core#FlangerPlugin",
|
||||
"display_name": "Flanger",
|
||||
"parent_uri": "http://lv2plug.in/ns/lv2core#ModulatorPlugin",
|
||||
"plugin_type": "FlangerPlugin",
|
||||
"children": []
|
||||
},
|
||||
{
|
||||
"uri": "http://lv2plug.in/ns/lv2core#PhaserPlugin",
|
||||
"display_name": "Phaser",
|
||||
"parent_uri": "http://lv2plug.in/ns/lv2core#ModulatorPlugin",
|
||||
"plugin_type": "PhaserPlugin",
|
||||
"children": []
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"uri": "http://lv2plug.in/ns/lv2core#SimulatorPlugin",
|
||||
"display_name": "Simulator",
|
||||
"parent_uri": "http://lv2plug.in/ns/lv2core#Plugin",
|
||||
"plugin_type": "SimulatorPlugin",
|
||||
"children": []
|
||||
},
|
||||
{
|
||||
"uri": "http://lv2plug.in/ns/lv2core#SpatialPlugin",
|
||||
"display_name": "Spatial",
|
||||
"parent_uri": "http://lv2plug.in/ns/lv2core#Plugin",
|
||||
"plugin_type": "SpatialPlugin",
|
||||
"children": []
|
||||
},
|
||||
{
|
||||
"uri": "http://lv2plug.in/ns/lv2core#SpectralPlugin",
|
||||
"display_name": "Spectral",
|
||||
"parent_uri": "http://lv2plug.in/ns/lv2core#Plugin",
|
||||
"plugin_type": "SpectralPlugin",
|
||||
"children": [
|
||||
{
|
||||
"uri": "http://lv2plug.in/ns/lv2core#PitchPlugin",
|
||||
"display_name": "Pitch Shifter",
|
||||
"parent_uri": "http://lv2plug.in/ns/lv2core#SpectralPlugin",
|
||||
"plugin_type": "PitchPlugin",
|
||||
"children": []
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"uri": "http://lv2plug.in/ns/lv2core#UtilityPlugin",
|
||||
"display_name": "Utility",
|
||||
"parent_uri": "http://lv2plug.in/ns/lv2core#Plugin",
|
||||
"plugin_type": "UtilityPlugin",
|
||||
"children": [
|
||||
{
|
||||
"uri": "http://lv2plug.in/ns/lv2core#AnalyserPlugin",
|
||||
"display_name": "Analyser",
|
||||
"parent_uri": "http://lv2plug.in/ns/lv2core#UtilityPlugin",
|
||||
"plugin_type": "AnalyserPlugin",
|
||||
"children": []
|
||||
},
|
||||
{
|
||||
"uri": "http://lv2plug.in/ns/lv2core#ConverterPlugin",
|
||||
"display_name": "Converter",
|
||||
"parent_uri": "http://lv2plug.in/ns/lv2core#UtilityPlugin",
|
||||
"plugin_type": "ConverterPlugin",
|
||||
"children": []
|
||||
},
|
||||
{
|
||||
"uri": "http://lv2plug.in/ns/lv2core#FunctionPlugin",
|
||||
"display_name": "Function",
|
||||
"parent_uri": "http://lv2plug.in/ns/lv2core#UtilityPlugin",
|
||||
"plugin_type": "FunctionPlugin",
|
||||
"children": []
|
||||
},
|
||||
{
|
||||
"uri": "http://lv2plug.in/ns/lv2core#MixerPlugin",
|
||||
"display_name": "Mixer",
|
||||
"parent_uri": "http://lv2plug.in/ns/lv2core#UtilityPlugin",
|
||||
"plugin_type": "MixerPlugin",
|
||||
"children": []
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
found 8 vulnerabilities (4 moderate, 4 high)
|
||||
run `npm audit fix` to fix them, or `npm audit` for details
|
||||
|
||||
Created git commit.
|
||||
|
||||
Success! Created my-app at /home/patch/src/piddle/react/my-app
|
||||
Inside that directory, you can run several commands:
|
||||
|
||||
npm start
|
||||
Starts the development server.
|
||||
|
||||
npm run build
|
||||
Bundles the app into static files for production.
|
||||
|
||||
npm test
|
||||
Starts the test runner.
|
||||
|
||||
npm run eject
|
||||
Removes this tool and copies build dependencies, configuration files
|
||||
and scripts into the app directory. If you do this, you canât go back!
|
||||
|
||||
We suggest that you begin by typing:
|
||||
|
||||
cd my-app
|
||||
npm start
|
||||
|
||||
Happy hacking!
|
||||
|
||||
@@ -0,0 +1,223 @@
|
||||
import React, { SyntheticEvent, Component } from 'react';
|
||||
import IconButton from '@material-ui/core/IconButton';
|
||||
import Typography from '@material-ui/core/Typography';
|
||||
import { PiPedalModel, PiPedalModelFactory, State } from './PiPedalModel';
|
||||
import { TransitionProps } from '@material-ui/core/transitions/transition';
|
||||
import Slide from '@material-ui/core/Slide';
|
||||
import Dialog from '@material-ui/core/Dialog';
|
||||
import AppBar from '@material-ui/core/AppBar';
|
||||
import Toolbar from '@material-ui/core/Toolbar';
|
||||
import { Theme, withStyles, WithStyles, createStyles } from '@material-ui/core/styles';
|
||||
import ArrowBackIcon from '@material-ui/icons/ArrowBack';
|
||||
import Divider from '@material-ui/core/Divider';
|
||||
import JackHostStatus from './JackHostStatus';
|
||||
|
||||
|
||||
interface AboutDialogProps extends WithStyles<typeof styles> {
|
||||
open: boolean;
|
||||
onClose: () => void;
|
||||
|
||||
|
||||
};
|
||||
|
||||
interface AboutDialogState {
|
||||
jackStatus?: JackHostStatus;
|
||||
};
|
||||
|
||||
|
||||
const styles = (theme: Theme) => createStyles({
|
||||
dialogAppBar: {
|
||||
position: 'relative',
|
||||
top: 0, left: 0
|
||||
},
|
||||
dialogTitle: {
|
||||
marginLeft: theme.spacing(2),
|
||||
flex: 1,
|
||||
},
|
||||
sectionHead: {
|
||||
marginLeft: 24,
|
||||
marginRight: 24,
|
||||
marginTop: 16,
|
||||
paddingBottom: 12
|
||||
|
||||
},
|
||||
textBlock: {
|
||||
marginLeft: 24,
|
||||
marginRight: 24,
|
||||
marginTop: 16,
|
||||
paddingBottom: 0,
|
||||
opacity: 0.95
|
||||
|
||||
},
|
||||
textBlockIndented: {
|
||||
marginLeft: 40,
|
||||
marginRight: 24,
|
||||
marginTop: 16,
|
||||
paddingBottom: 0,
|
||||
opacity: 0.95
|
||||
|
||||
},
|
||||
|
||||
setting: {
|
||||
minHeight: 64,
|
||||
width: "100%",
|
||||
textAlign: "left",
|
||||
paddingLeft: 22,
|
||||
paddingRight: 22
|
||||
},
|
||||
primaryItem: {
|
||||
|
||||
},
|
||||
secondaryItem: {
|
||||
|
||||
}
|
||||
|
||||
|
||||
});
|
||||
|
||||
|
||||
const Transition = React.forwardRef(function Transition(
|
||||
props: TransitionProps & { children?: React.ReactElement },
|
||||
ref: React.Ref<unknown>,
|
||||
) {
|
||||
return <Slide direction="up" ref={ref} {...props} />;
|
||||
});
|
||||
|
||||
|
||||
const AboutDialog = withStyles(styles, { withTheme: true })(
|
||||
|
||||
class extends Component<AboutDialogProps, AboutDialogState> {
|
||||
|
||||
model: PiPedalModel;
|
||||
|
||||
|
||||
|
||||
constructor(props: AboutDialogProps) {
|
||||
super(props);
|
||||
this.model = PiPedalModelFactory.getInstance();
|
||||
|
||||
this.handleDialogClose = this.handleDialogClose.bind(this);
|
||||
this.state = {
|
||||
jackStatus: undefined
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
mounted: boolean = false;
|
||||
subscribed: boolean = false;
|
||||
|
||||
tick() {
|
||||
if (this.model.state.get() === State.Ready) {
|
||||
this.model.getJackStatus()
|
||||
.then(jackStatus => {
|
||||
this.setState({jackStatus: jackStatus});
|
||||
})
|
||||
.catch(error => { /* ignore*/ });
|
||||
}
|
||||
}
|
||||
|
||||
timerHandle?: NodeJS.Timeout;
|
||||
|
||||
updateNotifications() {
|
||||
let subscribed = this.mounted && this.props.open;
|
||||
if (subscribed !== this.subscribed) {
|
||||
if (subscribed) {
|
||||
this.timerHandle = setInterval(() => this.tick(), 1000);
|
||||
} else {
|
||||
if (this.timerHandle) {
|
||||
clearInterval(this.timerHandle);
|
||||
this.timerHandle = undefined;
|
||||
}
|
||||
}
|
||||
this.subscribed = subscribed;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
componentDidMount() {
|
||||
this.mounted = true;
|
||||
this.updateNotifications();
|
||||
}
|
||||
componentWillUnmount() {
|
||||
this.mounted = false;
|
||||
this.updateNotifications();
|
||||
}
|
||||
componentDidUpdate() {
|
||||
this.updateNotifications();
|
||||
}
|
||||
|
||||
handleDialogClose(e: SyntheticEvent) {
|
||||
this.props.onClose();
|
||||
}
|
||||
|
||||
|
||||
render() {
|
||||
let classes = this.props.classes;
|
||||
|
||||
return (
|
||||
<Dialog fullScreen open={this.props.open}
|
||||
onClose={() => { this.props.onClose() }} TransitionComponent={Transition}>
|
||||
|
||||
<div style={{ display: "flex", flexDirection: "column", flexWrap: "nowrap", width: "100%", height: "100%", overflow: "hidden" }}>
|
||||
<div style={{ flex: "0 0 auto" }}>
|
||||
<AppBar className={classes.dialogAppBar} >
|
||||
<Toolbar>
|
||||
<IconButton edge="start" color="inherit" onClick={this.handleDialogClose} aria-label="back"
|
||||
>
|
||||
<ArrowBackIcon />
|
||||
</IconButton>
|
||||
<Typography variant="h6" className={classes.dialogTitle}>
|
||||
About
|
||||
</Typography>
|
||||
</Toolbar>
|
||||
</AppBar>
|
||||
</div>
|
||||
<div style={{
|
||||
flex: "1 1 auto", position: "relative", overflow: "hidden",
|
||||
overflowX: "hidden", overflowY: "auto", margin: 22
|
||||
}}
|
||||
>
|
||||
<div>
|
||||
<Typography display="block" variant="h6" color="textPrimary">
|
||||
PiPedal <span style={{fontSize: "0.7em"}}>
|
||||
{(this.model.serverVersion? this.model.serverVersion.serverVersion : "")
|
||||
+ (this.model.serverVersion?.debug ? " (Debug)": "")}
|
||||
</span>
|
||||
</Typography>
|
||||
<Typography display="block" variant="body2" style={{marginBottom: 12}} >
|
||||
Copyright © 2021 Robin Davies. All rights reserved.
|
||||
</Typography>
|
||||
<Divider/>
|
||||
<Typography display="block" variant="caption" >
|
||||
JACK AUDIO STATUS
|
||||
</Typography>
|
||||
<div style={{paddingTop: 8, paddingBottom: 24}}>
|
||||
{
|
||||
JackHostStatus.getDisplayView("",this.state.jackStatus)
|
||||
}
|
||||
</div>
|
||||
<Divider/>
|
||||
<Typography display="block" variant="caption" >
|
||||
LEGAL NOTICES
|
||||
</Typography>
|
||||
<Typography display="block" variant="caption" style={{paddingTop: 12, paddingBottom: 12}} >
|
||||
Pi Pedal uses the following open-source components.
|
||||
</Typography>
|
||||
<Typography display="block" variant="caption" style={{paddingTop: 12, paddingBottom: 12}} >
|
||||
TBD
|
||||
</Typography>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Dialog >
|
||||
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
);
|
||||
|
||||
export default AboutDialog;
|
||||
@@ -0,0 +1 @@
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
import React from 'react';
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import App from './App';
|
||||
|
||||
test('renders learn react link', () => {
|
||||
render(<App />);
|
||||
const linkElement = screen.getByText(/learn react/i);
|
||||
expect(linkElement).toBeInTheDocument();
|
||||
});
|
||||
@@ -0,0 +1,830 @@
|
||||
import { SyntheticEvent } from 'react';
|
||||
import { ThemeProvider } from '@material-ui/styles';
|
||||
import { createMuiTheme } from '@material-ui/core/styles';
|
||||
|
||||
import './App.css';
|
||||
import AppBar from '@material-ui/core/AppBar';
|
||||
import Toolbar from '@material-ui/core/Toolbar';
|
||||
import CssBaseline from '@material-ui/core/CssBaseline';
|
||||
import Typography from '@material-ui/core/Typography';
|
||||
import { createStyles, withStyles, WithStyles } from '@material-ui/core/styles';
|
||||
import IconButton from '@material-ui/core/IconButton';
|
||||
import MenuButton from '@material-ui/icons/Menu';
|
||||
import { TemporaryDrawer } from './TemporaryDrawer';
|
||||
import { Theme } from '@material-ui/core/styles/createMuiTheme';
|
||||
import FullscreenIcon from '@material-ui/icons/Fullscreen';
|
||||
import FullscreenExitIcon from '@material-ui/icons/FullscreenExit';
|
||||
import List from '@material-ui/core/List';
|
||||
import Divider from '@material-ui/core/Divider';
|
||||
import ListItem from '@material-ui/core/ListItem';
|
||||
import ListItemIcon from '@material-ui/core/ListItemIcon';
|
||||
import ListItemText from '@material-ui/core/ListItemText';
|
||||
import CircularProgress from '@material-ui/core/CircularProgress';
|
||||
import { OnChangedHandler } from './ObservableProperty';
|
||||
import ErrorOutlineIcon from '@material-ui/icons/Error';
|
||||
import ResizeResponsiveComponent from './ResizeResponsiveComponent';
|
||||
import Button from '@material-ui/core/Button';
|
||||
import PresetSelector from './PresetSelector';
|
||||
import SettingsDialog from './SettingsDialog';
|
||||
import AboutDialog from './AboutDialog';
|
||||
import BankDialog from './BankDialog';
|
||||
|
||||
import { PiPedalModelFactory, PiPedalModel, State } from './PiPedalModel';
|
||||
import MainPage from './MainPage';
|
||||
import DialogContent from '@material-ui/core/DialogContent';
|
||||
import DialogContentText from '@material-ui/core/DialogContentText';
|
||||
import Dialog from '@material-ui/core/Dialog';
|
||||
import DialogActions from '@material-ui/core/DialogActions';
|
||||
import ListSubheader from '@material-ui/core/ListSubheader';
|
||||
import { BankIndex, BankIndexEntry } from './Banks';
|
||||
import RenameDialog from './RenameDialog';
|
||||
import JackStatusView from './JackStatusView';
|
||||
|
||||
const theme = createMuiTheme({
|
||||
palette: {
|
||||
primary: {
|
||||
main: "#324c6c"
|
||||
}
|
||||
// secondary: {
|
||||
// main: "#00E676"
|
||||
// }
|
||||
}
|
||||
});
|
||||
|
||||
const appStyles = ({ palette, spacing, mixins }: Theme) => createStyles({
|
||||
loadingContent: {
|
||||
display: "block",
|
||||
position: "absolute",
|
||||
minHeight: "10em",
|
||||
left: "0px",
|
||||
top: "0px",
|
||||
width: "100%",
|
||||
height: "100%",
|
||||
background: "#DDD",
|
||||
opacity: "0.95",
|
||||
justifyContent: "center",
|
||||
textAlign: "center",
|
||||
zIndex: 2010
|
||||
},
|
||||
errorContent: {
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
justifyContent: "center",
|
||||
flexWrap: "nowrap",
|
||||
alignItems: "center",
|
||||
position: "fixed",
|
||||
minHeight: "10em",
|
||||
left: "0px",
|
||||
top: "0px",
|
||||
width: "100%",
|
||||
height: "100%",
|
||||
color: "#444",
|
||||
zIndex: 2000
|
||||
},
|
||||
errorContentMask: {
|
||||
position: "absolute", left: 0, top: 0,
|
||||
width: "100%", height: "100%",
|
||||
background: "#BBB",
|
||||
opacity: 0.95,
|
||||
zIndex: 1999
|
||||
},
|
||||
errorText: {
|
||||
marginTop: 0,
|
||||
padding: 0,
|
||||
marginBottom: 12,
|
||||
fontWeight: 500,
|
||||
fontSize: "13pt",
|
||||
maxWidth: 250,
|
||||
opacity: 1,
|
||||
zIndex: 2010,
|
||||
},
|
||||
progressText: {
|
||||
marginTop: 0,
|
||||
fontWeight: 500,
|
||||
fontSize: "13pt",
|
||||
maxWidth: 250,
|
||||
opacity: 1,
|
||||
zIndex: 2010,
|
||||
paddingTop: 12,
|
||||
},
|
||||
|
||||
errorMessageBox: {
|
||||
flex: "0 0 auto",
|
||||
width: 300,
|
||||
marginLeft: "auto",
|
||||
marginRight: "auto",
|
||||
zIndex: 2010
|
||||
|
||||
|
||||
},
|
||||
errorMessage: {
|
||||
color: '#000',
|
||||
textAlign: "left",
|
||||
zIndex: 2010
|
||||
|
||||
},
|
||||
loadingBox: {
|
||||
position: "relative",
|
||||
top: "20%",
|
||||
width: "120px",
|
||||
color: "#888",
|
||||
marginLeft: "auto",
|
||||
marginRight: "auto",
|
||||
// border: "3px solid #888",
|
||||
borderRadius: "12px",
|
||||
padding: "12px",
|
||||
justifyContent: "center",
|
||||
textAlign: "center",
|
||||
opacity: 0.8,
|
||||
zIndex: 2010
|
||||
|
||||
},
|
||||
loadingBoxItem: {
|
||||
justifyContent: "center",
|
||||
textAlign: "center",
|
||||
zIndex: 2010
|
||||
|
||||
},
|
||||
|
||||
|
||||
toolBarContent:
|
||||
{
|
||||
position: "absolute", top: 0, width: "100%"
|
||||
},
|
||||
|
||||
toolBarSpacer:
|
||||
{
|
||||
position: "relative", flex: "0 0 auto"
|
||||
},
|
||||
|
||||
|
||||
mainFrame: {
|
||||
overflow: "hidden",
|
||||
display: "flex",
|
||||
flexFlow: "column",
|
||||
flex: "1 1 100%"
|
||||
},
|
||||
|
||||
toolbarSizingPosition: {
|
||||
flexGrow: 0,
|
||||
flexShrink: 1,
|
||||
width: "100%",
|
||||
flexBasis: "auto"
|
||||
},
|
||||
mainSizingPosition: {
|
||||
overflow: "hidden",
|
||||
flex: "1 1 auto"
|
||||
},
|
||||
|
||||
heroContent: {
|
||||
backgroundColor: palette.background.paper,
|
||||
position: "relative",
|
||||
height: "100%",
|
||||
width: "100%"
|
||||
},
|
||||
|
||||
drawerItem: {
|
||||
width: 350,
|
||||
background: "#FF8080"
|
||||
},
|
||||
drawerItemFullWidth: {
|
||||
width: 'auto',
|
||||
},
|
||||
|
||||
icon: {
|
||||
marginRight: spacing(2),
|
||||
},
|
||||
|
||||
heroButtons: {
|
||||
marginTop: spacing(4),
|
||||
},
|
||||
cardGrid: {
|
||||
paddingTop: spacing(8),
|
||||
paddingBottom: spacing(8),
|
||||
},
|
||||
card: {
|
||||
height: '100%',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
},
|
||||
cardMedia: {
|
||||
paddingTop: '56.25%', // 16:9
|
||||
},
|
||||
cardContent: {
|
||||
flexGrow: 1,
|
||||
},
|
||||
footer: {
|
||||
backgroundColor: palette.background.paper,
|
||||
padding: spacing(6),
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
function supportsFullScreen(): boolean {
|
||||
let doc: any = window.document;
|
||||
let docEl: any = doc.documentElement;
|
||||
var requestFullScreen = docEl.requestFullscreen || docEl.mozRequestFullScreen || docEl.webkitRequestFullScreen || docEl.msRequestFullscreen;
|
||||
return (!!requestFullScreen);
|
||||
|
||||
}
|
||||
|
||||
function setFullScreen(value: boolean) {
|
||||
let doc: any = window.document;
|
||||
let docEl: any = doc.documentElement;
|
||||
|
||||
var requestFullScreen = docEl.requestFullscreen || docEl.mozRequestFullScreen || docEl.webkitRequestFullScreen || docEl.msRequestFullscreen;
|
||||
var cancelFullScreen = doc.exitFullscreen || doc.mozCancelFullScreen || doc.webkitExitFullscreen || doc.msExitFullscreen;
|
||||
|
||||
if (!doc.fullscreenElement && !doc.mozFullScreenElement && !doc.webkitFullscreenElement && !doc.msFullscreenElement) {
|
||||
requestFullScreen.call(docEl);
|
||||
}
|
||||
else {
|
||||
cancelFullScreen.call(doc);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function preventDefault(e: SyntheticEvent): void {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
}
|
||||
|
||||
type AppState = {
|
||||
isDrawerOpen: boolean;
|
||||
errorMessage: string;
|
||||
displayState: State;
|
||||
|
||||
canFullScreen: boolean;
|
||||
isFullScreen: boolean;
|
||||
tinyToolBar: boolean;
|
||||
alertDialogOpen: boolean;
|
||||
alertDialogMessage: string;
|
||||
isSettingsDialogOpen: boolean;
|
||||
isDebug: boolean;
|
||||
|
||||
renameBankDialogOpen: boolean;
|
||||
saveBankAsDialogOpen: boolean;
|
||||
|
||||
aboutDialogOpen: boolean;
|
||||
bankDialogOpen: boolean;
|
||||
editBankDialogOpen: boolean;
|
||||
|
||||
|
||||
presetName: string;
|
||||
presetChanged: boolean;
|
||||
banks: BankIndex;
|
||||
bankDisplayItems: number;
|
||||
};
|
||||
interface AppProps extends WithStyles<typeof appStyles> {
|
||||
}
|
||||
|
||||
const App = withStyles(appStyles)(class extends ResizeResponsiveComponent<AppProps, AppState> {
|
||||
// Before the component mounts, we initialise our state
|
||||
|
||||
model_: PiPedalModel;
|
||||
errorChangeHandler_: OnChangedHandler<string>;
|
||||
stateChangeHandler_: OnChangedHandler<State>;
|
||||
|
||||
constructor(props: AppProps) {
|
||||
super(props);
|
||||
this.model_ = PiPedalModelFactory.getInstance();
|
||||
|
||||
this.state = {
|
||||
isDrawerOpen: false,
|
||||
errorMessage: this.model_.errorMessage.get(),
|
||||
displayState: this.model_.state.get(),
|
||||
canFullScreen: supportsFullScreen(),
|
||||
isFullScreen: !!document.fullscreenElement,
|
||||
tinyToolBar: false,
|
||||
alertDialogOpen: false,
|
||||
alertDialogMessage: "",
|
||||
presetName: this.model_.presets.get().getSelectedText(),
|
||||
isSettingsDialogOpen: false,
|
||||
isDebug: true,
|
||||
presetChanged: this.model_.presets.get().presetChanged,
|
||||
banks: this.model_.banks.get(),
|
||||
renameBankDialogOpen: false,
|
||||
saveBankAsDialogOpen: false,
|
||||
aboutDialogOpen: false,
|
||||
bankDialogOpen: false,
|
||||
editBankDialogOpen: false,
|
||||
bankDisplayItems: 5
|
||||
|
||||
};
|
||||
|
||||
this.errorChangeHandler_ = this.setErrorMessage.bind(this);
|
||||
this.stateChangeHandler_ = this.setDisplayState.bind(this);
|
||||
this.presetChangedHandler = this.presetChangedHandler.bind(this);
|
||||
this.alertMessageChangedHandler = this.alertMessageChangedHandler.bind(this);
|
||||
this.handleCloseAlert = this.handleCloseAlert.bind(this);
|
||||
this.banksChangedHandler = this.banksChangedHandler.bind(this);
|
||||
|
||||
}
|
||||
|
||||
onOpenBank(bankId: number) {
|
||||
this.model_.openBank(bankId)
|
||||
.catch((error) => this.model_.showAlert(error));
|
||||
|
||||
}
|
||||
|
||||
handleSaveBankAsOk(newName: string) {
|
||||
let currentName = this.model_.banks.get().getSelectedEntryName();
|
||||
if (currentName === newName) {
|
||||
this.setState({
|
||||
renameBankDialogOpen: false,
|
||||
saveBankAsDialogOpen: false
|
||||
});
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
if (this.model_.banks.get().nameExists(newName)) {
|
||||
this.model_.showAlert("A bank by that name already exists.");
|
||||
return;
|
||||
}
|
||||
this.setState({
|
||||
renameBankDialogOpen: false,
|
||||
saveBankAsDialogOpen: false
|
||||
});
|
||||
this.model_.saveBankAs(this.model_.banks.get().selectedBank, newName)
|
||||
.catch((error) => {
|
||||
this.model_.showAlert(error);
|
||||
});
|
||||
|
||||
}
|
||||
handleBankRenameOk(newName: string) {
|
||||
let currentName = this.model_.banks.get().getSelectedEntryName();
|
||||
if (currentName === newName) {
|
||||
this.setState({
|
||||
renameBankDialogOpen: false,
|
||||
saveBankAsDialogOpen: false
|
||||
});
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
if (this.model_.banks.get().nameExists(newName)) {
|
||||
this.model_.showAlert("A bank by that name already exists.");
|
||||
return;
|
||||
}
|
||||
this.setState({
|
||||
renameBankDialogOpen: false
|
||||
});
|
||||
this.model_.renameBank(this.model_.banks.get().selectedBank, newName)
|
||||
.catch((error) => {
|
||||
this.model_.showAlert(error);
|
||||
});
|
||||
|
||||
}
|
||||
handleSettingsDialogClose() {
|
||||
this.setState({
|
||||
isSettingsDialogOpen: false
|
||||
});
|
||||
}
|
||||
handleDrawerSettingsClick() {
|
||||
this.setState({
|
||||
isDrawerOpen: false,
|
||||
isSettingsDialogOpen: true
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
handleDrawerManageBanks() {
|
||||
this.setState({
|
||||
isDrawerOpen: false,
|
||||
bankDialogOpen: true,
|
||||
editBankDialogOpen: true
|
||||
});
|
||||
|
||||
}
|
||||
handleDrawerSelectBank() {
|
||||
this.setState({
|
||||
isDrawerOpen: false,
|
||||
bankDialogOpen: true,
|
||||
editBankDialogOpen: false
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
handleDrawerAboutClick() {
|
||||
this.setState({
|
||||
aboutDialogOpen: true
|
||||
});
|
||||
|
||||
}
|
||||
handleDrawerRenameBank() {
|
||||
this.setState({
|
||||
isDrawerOpen: false,
|
||||
renameBankDialogOpen: true,
|
||||
saveBankAsDialogOpen: false
|
||||
});
|
||||
|
||||
}
|
||||
handleDrawerSaveBankAs() {
|
||||
this.setState({
|
||||
isDrawerOpen: false,
|
||||
renameBankDialogOpen: false,
|
||||
saveBankAsDialogOpen: true
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
handleCloseAlert(e?: any, reason?: any) {
|
||||
this.model_.alertMessage.set("");
|
||||
}
|
||||
banksChangedHandler() {
|
||||
this.setState({
|
||||
banks: this.model_.banks.get()
|
||||
});
|
||||
}
|
||||
presetChangedHandler() {
|
||||
let presets = this.model_.presets.get();
|
||||
|
||||
this.setState({
|
||||
presetName: presets.getSelectedText(),
|
||||
presetChanged: presets.presetChanged
|
||||
});
|
||||
}
|
||||
toggleFullScreen(): void {
|
||||
setFullScreen(this.state.isFullScreen);
|
||||
this.setState({ isFullScreen: !this.state.isFullScreen });
|
||||
}
|
||||
componentDidMount() {
|
||||
|
||||
super.componentDidMount();
|
||||
|
||||
window.addEventListener("beforeunload", (e) => {
|
||||
e.preventDefault();
|
||||
if (this.model_.state.get() === State.Ready)
|
||||
{
|
||||
e.returnValue = "Are you sure you want to leave this page?";
|
||||
return "Are you sure you want to leave this page?";
|
||||
}
|
||||
});
|
||||
|
||||
this.model_.errorMessage.addOnChangedHandler(this.errorChangeHandler_);
|
||||
this.model_.state.addOnChangedHandler(this.stateChangeHandler_);
|
||||
this.model_.pedalBoard.addOnChangedHandler(this.presetChangedHandler);
|
||||
this.model_.alertMessage.addOnChangedHandler(this.alertMessageChangedHandler);
|
||||
this.model_.banks.addOnChangedHandler(this.banksChangedHandler);
|
||||
this.alertMessageChangedHandler();
|
||||
}
|
||||
|
||||
|
||||
updateOverscroll(): void {
|
||||
if (this.model_.serverVersion) {
|
||||
// no pull-down refresh on android devices once we're ready (unless we're debug)
|
||||
let preventOverscroll =
|
||||
this.model_.state.get() === State.Ready
|
||||
&& !this.model_.serverVersion.debug;
|
||||
|
||||
let overscrollBehavior = preventOverscroll ? "none" : "auto";
|
||||
document.body.style.overscrollBehavior = overscrollBehavior;
|
||||
}
|
||||
}
|
||||
|
||||
componentDidUpdate() {
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
super.componentWillUnmount();
|
||||
this.model_.errorMessage.removeOnChangedHandler(this.errorChangeHandler_);
|
||||
this.model_.state.removeOnChangedHandler(this.stateChangeHandler_);
|
||||
this.model_.pedalBoard.removeOnChangedHandler(this.presetChangedHandler);
|
||||
this.model_.banks.addOnChangedHandler(this.banksChangedHandler);
|
||||
|
||||
}
|
||||
|
||||
alertMessageChangedHandler() {
|
||||
let message = this.model_.alertMessage.get();
|
||||
if (message === "") {
|
||||
this.setState({ alertDialogOpen: false });
|
||||
// leave the message intact so the dialog can fade.
|
||||
} else {
|
||||
this.setState({
|
||||
alertDialogOpen: true,
|
||||
alertDialogMessage: message
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
updateResponsive() {
|
||||
// functional, but disabled.
|
||||
// let tinyToolBar_ = this.windowSize.height < 600;
|
||||
// this.setState({ tinyToolBar: tinyToolBar_ });
|
||||
|
||||
let height = this.windowSize.height;
|
||||
|
||||
const ENTRY_HEIGHT = 48;
|
||||
// ENTRY_HEIGHT*6 +K = 727 from observation.
|
||||
const K = 450;
|
||||
|
||||
let bankEntries = Math.floor((height-K)/ENTRY_HEIGHT);
|
||||
if (bankEntries < 1) bankEntries = 1;
|
||||
if (bankEntries > 7) bankEntries = 7;
|
||||
this.setState({ bankDisplayItems: bankEntries });
|
||||
|
||||
}
|
||||
onWindowSizeChanged(width: number, height: number): void {
|
||||
super.onWindowSizeChanged(width, height);
|
||||
this.updateResponsive();
|
||||
}
|
||||
|
||||
|
||||
setErrorMessage(message: string): void {
|
||||
this.setState({ errorMessage: message });
|
||||
}
|
||||
setDisplayState(newState: State): void {
|
||||
this.updateOverscroll();
|
||||
|
||||
this.setState({ displayState: newState });
|
||||
}
|
||||
|
||||
showDrawer() {
|
||||
this.setState({ isDrawerOpen: true })
|
||||
}
|
||||
hideDrawer() {
|
||||
this.setState({ isDrawerOpen: false })
|
||||
}
|
||||
shortBankList(banks: BankIndex): BankIndexEntry[] {
|
||||
let n = this.state.bankDisplayItems;
|
||||
let entries = banks.entries;
|
||||
if (entries.length < n + 1) { // +1 for the .... entry.
|
||||
return entries;
|
||||
}
|
||||
let result: BankIndexEntry[] = [];
|
||||
let selectedIndex = -1;
|
||||
for (let i = 0; i < entries.length; ++i) {
|
||||
if (entries[i].instanceId === banks.selectedBank) {
|
||||
selectedIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (n > entries.length) n = entries.length;
|
||||
if (selectedIndex > n) {
|
||||
for (let i = 0; i < n - 1; ++i) {
|
||||
result.push(entries[i]);
|
||||
}
|
||||
result.push(entries[selectedIndex]);
|
||||
} else {
|
||||
for (let i = 0; i < n; ++i) {
|
||||
result.push(entries[i]);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
render() {
|
||||
|
||||
const { classes } = this.props;
|
||||
|
||||
let shortBankList = this.shortBankList(this.state.banks);
|
||||
let showBankSelectDialog = shortBankList.length !== this.state.banks.entries.length;
|
||||
|
||||
|
||||
return (
|
||||
<ThemeProvider theme={theme}>
|
||||
<div style={{
|
||||
minHeight: 345, minWidth: 390,
|
||||
position: "absolute", width: "100%", height: "100%", background: "#F88", userSelect: "none",
|
||||
display: "flex", flexDirection: "column", flexWrap: "nowrap",
|
||||
overscrollBehavior: this.state.isDebug ? "auto" : "none"
|
||||
}}
|
||||
onContextMenu={(e) => {
|
||||
if (!this.model_.serverVersion?.debug??false)
|
||||
{
|
||||
e.preventDefault(); e.stopPropagation();
|
||||
}
|
||||
}}
|
||||
>
|
||||
<CssBaseline />
|
||||
{(!this.state.tinyToolBar) ?
|
||||
(
|
||||
<AppBar position="absolute">
|
||||
<Toolbar variant="dense" >
|
||||
<IconButton edge="start"
|
||||
color="inherit" aria-label="menu" onClick={() => { this.showDrawer() }}
|
||||
>
|
||||
<MenuButton />
|
||||
</IconButton>
|
||||
<div style={{ flex: "1 1 1px" }} />
|
||||
<div style={{ flex: "0 1 400px", minWidth: 100 }}>
|
||||
<PresetSelector />
|
||||
</div>
|
||||
<div style={{ flex: "2 2 30px" }} />
|
||||
{this.state.canFullScreen &&
|
||||
<IconButton
|
||||
color="inherit" aria-label="menu" onClick={() => { this.toggleFullScreen(); }}>
|
||||
{this.state.isFullScreen ? (
|
||||
<FullscreenExitIcon />
|
||||
) : (
|
||||
<FullscreenIcon />
|
||||
|
||||
)}
|
||||
|
||||
</IconButton>
|
||||
}
|
||||
</Toolbar>
|
||||
</AppBar>
|
||||
) : (
|
||||
<div className={classes.toolBarContent} >
|
||||
<IconButton style={{ position: "absolute", left: 12, top: 8, zIndex: 2 }}
|
||||
color="inherit" aria-label="menu" onClick={() => { this.showDrawer() }}
|
||||
>
|
||||
<MenuButton />
|
||||
</IconButton>
|
||||
{this.state.canFullScreen && (
|
||||
<IconButton style={{ position: "absolute", right: 8, top: 8, zIndex: 2 }}
|
||||
color="inherit" aria-label="menu" onClick={() => { this.toggleFullScreen(); }}>
|
||||
{this.state.isFullScreen ? (
|
||||
<FullscreenExitIcon />
|
||||
) : (
|
||||
<FullscreenIcon />
|
||||
|
||||
)}
|
||||
|
||||
</IconButton>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
<TemporaryDrawer position='left' title="piddle"
|
||||
is_open={this.state.isDrawerOpen} onClose={() => { this.hideDrawer(); }} >
|
||||
<List subheader={
|
||||
<ListSubheader component="div" id="nested-list-subheader">Banks</ListSubheader>
|
||||
}>
|
||||
{
|
||||
shortBankList.map((bank) => {
|
||||
return (
|
||||
<ListItem button key={'bank' + bank.instanceId} selected={bank.instanceId === this.state.banks.selectedBank}
|
||||
onClick={() => this.onOpenBank(bank.instanceId)}
|
||||
>
|
||||
|
||||
<ListItemText primary={bank.name} />
|
||||
</ListItem>
|
||||
|
||||
);
|
||||
})
|
||||
}
|
||||
{
|
||||
showBankSelectDialog && (
|
||||
<ListItem button key={'bankDOTDOTDOT'} selected={false}
|
||||
onClick={() => this.handleDrawerSelectBank()}
|
||||
>
|
||||
|
||||
<ListItemText primary={"..."} />
|
||||
</ListItem>
|
||||
|
||||
|
||||
)
|
||||
}
|
||||
</List>
|
||||
<Divider />
|
||||
<List>
|
||||
<ListItem button key='RenameBank' onClick={() => { this.handleDrawerRenameBank() }}>
|
||||
<ListItemIcon><img src="img/drive_file_rename_outline_black_24dp.svg" alt="" style={{ opacity: 0.6 }} /></ListItemIcon>
|
||||
<ListItemText primary='Rename Bank' />
|
||||
</ListItem>
|
||||
<ListItem button key='SaveBank' onClick={() => { this.handleDrawerSaveBankAs() }} >
|
||||
<ListItemIcon>
|
||||
<img src="img/save_bank_as.svg" alt="" style={{ opacity: 0.6 }} />
|
||||
</ListItemIcon>
|
||||
<ListItemText primary='Save As New Bank' />
|
||||
</ListItem>
|
||||
<ListItem button key='CreateBank' onClick={() => { this.handleDrawerManageBanks(); }}>
|
||||
<ListItemIcon>
|
||||
<img src="img/edit_banks.svg" alt="" style={{ opacity: 0.6 }} />
|
||||
</ListItemIcon>
|
||||
<ListItemText primary='Manage Banks...' />
|
||||
</ListItem>
|
||||
</List>
|
||||
<Divider />
|
||||
<List>
|
||||
<ListItem button key='Settings' onClick={() => { this.handleDrawerSettingsClick() }}>
|
||||
<ListItemIcon>
|
||||
<img src="img/settings_black_24dp.svg" alt="" style={{ opacity: 0.6 }} />
|
||||
</ListItemIcon>
|
||||
<ListItemText primary='Settings' />
|
||||
</ListItem>
|
||||
<ListItem button key='Settings' onClick={() => { this.handleDrawerAboutClick() }}>
|
||||
<ListItemIcon>
|
||||
<img src="img/help_outline_black_24dp.svg" alt="" style={{ opacity: 0.6 }} />
|
||||
</ListItemIcon>
|
||||
<ListItemText primary='About' />
|
||||
</ListItem>
|
||||
</List>
|
||||
|
||||
</TemporaryDrawer>
|
||||
{!this.state.tinyToolBar && (
|
||||
<Toolbar className={classes.toolBarSpacer} variant="dense" />
|
||||
)}
|
||||
<main className={classes.mainFrame} >
|
||||
<div className={classes.mainSizingPosition}>
|
||||
<div className={classes.heroContent}>
|
||||
{(this.state.displayState !== State.Loading) && (
|
||||
<MainPage hasTinyToolBar={this.state.tinyToolBar} />
|
||||
)}
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</main>
|
||||
<BankDialog show={this.state.bankDialogOpen} isEditDialog={this.state.editBankDialogOpen} onDialogClose={() => this.setState({ bankDialogOpen: false })} />
|
||||
<AboutDialog open={this.state.aboutDialogOpen} onClose={() => this.setState({ aboutDialogOpen: false })} />
|
||||
<SettingsDialog open={this.state.isSettingsDialogOpen} onClose={() => this.handleSettingsDialogClose()} />
|
||||
<RenameDialog
|
||||
open={this.state.renameBankDialogOpen || this.state.saveBankAsDialogOpen}
|
||||
defaultName={this.model_.banks.get().getSelectedEntryName()}
|
||||
acceptActionName={"Rename"}
|
||||
onClose={() => {
|
||||
this.setState({
|
||||
renameBankDialogOpen: false,
|
||||
saveBankAsDialogOpen: false
|
||||
})
|
||||
}}
|
||||
onOk={(text: string) => {
|
||||
if (this.state.renameBankDialogOpen) {
|
||||
this.handleBankRenameOk(text);
|
||||
} else if (this.state.saveBankAsDialogOpen) {
|
||||
this.handleSaveBankAsOk(text);
|
||||
}
|
||||
}
|
||||
}
|
||||
/>
|
||||
|
||||
<Dialog
|
||||
open={this.state.alertDialogOpen}
|
||||
onClose={this.handleCloseAlert}
|
||||
aria-describedby="alert-dialog-description"
|
||||
>
|
||||
<DialogContent>
|
||||
<DialogContentText id="alert-dialog-description">
|
||||
{
|
||||
this.model_.alertMessage.get()
|
||||
}
|
||||
</DialogContentText>
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
<Button onClick={this.handleCloseAlert} color="primary" autoFocus>
|
||||
OK
|
||||
</Button>
|
||||
</DialogActions>
|
||||
</Dialog>
|
||||
<JackStatusView />
|
||||
<div className={classes.errorContent} style={{ display: this.state.displayState === State.Reconnecting ? "block" : "none" }}
|
||||
>
|
||||
<div className={classes.errorContentMask} />
|
||||
|
||||
<div className={classes.loadingBox}>
|
||||
<div className={classes.loadingBoxItem}>
|
||||
<CircularProgress color="inherit" className={classes.loadingBoxItem} />
|
||||
</div>
|
||||
<Typography variant="body2" className={classes.progressText}>
|
||||
Reconnecting...
|
||||
</Typography>
|
||||
</div>
|
||||
</div>
|
||||
<div className={classes.errorContent} style={{ display: this.state.displayState === State.Error ? "flex" : "none" }}
|
||||
onMouseDown={preventDefault} onKeyDown={preventDefault}
|
||||
>
|
||||
<div className={classes.errorContentMask} />
|
||||
<div style={{ flex: "2 2 3px", height: 20 }} > </div>
|
||||
<div className={classes.errorMessageBox} style={{ position: "relative" }} >
|
||||
<div style={{ fontSize: "30px", position: "absolute", left: 0, top: 0, color: "#A00" }}>
|
||||
<ErrorOutlineIcon color="inherit" fontSize="inherit" style={{ float: "left", marginRight: "12px" }} />
|
||||
</div>
|
||||
<div style={{ marginLeft: 40, marginTop: 3 }}>
|
||||
<p className={classes.errorText}>
|
||||
Error: {this.state.errorMessage}
|
||||
</p>
|
||||
</div>
|
||||
<div style={{ paddingTop: 50, paddingLeft: 36, textAlign: "left" }}>
|
||||
<Button variant='contained' color="primary" component='button'
|
||||
onClick={() => window.location.reload()} >
|
||||
Reload
|
||||
</Button>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div style={{ flex: "5 5 auto", height: 20 }} > </div>
|
||||
|
||||
</div>
|
||||
<div className={classes.errorContent} style={{ display: this.state.displayState === State.Loading ? "block" : "none" }}>
|
||||
<div className={classes.errorContentMask} />
|
||||
<div className={classes.loadingBox}>
|
||||
<div className={classes.loadingBoxItem}>
|
||||
<CircularProgress color="inherit" className={classes.loadingBoxItem} />
|
||||
</div>
|
||||
<Typography variant="body2" className={classes.loadingBoxItem}>
|
||||
Loading...
|
||||
</Typography>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div >
|
||||
</ThemeProvider>
|
||||
);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
export default App;
|
||||
@@ -0,0 +1,470 @@
|
||||
import React, { Component } from 'react';
|
||||
import IconButton from '@material-ui/core/IconButton';
|
||||
import Typography from '@material-ui/core/Typography';
|
||||
import { PiPedalModel, PiPedalModelFactory } from './PiPedalModel';
|
||||
import { BankIndexEntry, BankIndex } from './Banks';
|
||||
import Button from "@material-ui/core/Button";
|
||||
import ButtonBase from "@material-ui/core/ButtonBase";
|
||||
import { TransitionProps } from '@material-ui/core/transitions/transition';
|
||||
import Slide from '@material-ui/core/Slide';
|
||||
import Dialog from '@material-ui/core/Dialog';
|
||||
import AppBar from '@material-ui/core/AppBar';
|
||||
import Toolbar from '@material-ui/core/Toolbar';
|
||||
import { Theme, withStyles, WithStyles, createStyles } from '@material-ui/core/styles';
|
||||
import DraggableGrid, { ScrollDirection } from './DraggableGrid';
|
||||
|
||||
import SelectHoverBackground from './SelectHoverBackground';
|
||||
import CloseIcon from '@material-ui/icons/Close';
|
||||
import ArrowBackIcon from '@material-ui/icons/ArrowBack';
|
||||
import EditIcon from '@material-ui/icons/Edit';
|
||||
import RenameDialog from './RenameDialog';
|
||||
|
||||
interface BankDialogProps extends WithStyles<typeof styles> {
|
||||
show: boolean;
|
||||
isEditDialog: boolean;
|
||||
onDialogClose: () => void;
|
||||
|
||||
};
|
||||
|
||||
interface BankDialogState {
|
||||
banks: BankIndex;
|
||||
|
||||
showActionBar: boolean;
|
||||
|
||||
selectedItem: number;
|
||||
|
||||
filenameDialogOpen: boolean;
|
||||
filenameSaveAs: boolean;
|
||||
|
||||
};
|
||||
|
||||
|
||||
const styles = (theme: Theme) => createStyles({
|
||||
dialogAppBar: {
|
||||
position: 'relative',
|
||||
top: 0, left: 0
|
||||
},
|
||||
dialogActionBar: {
|
||||
position: 'relative',
|
||||
top: 0, left: 0,
|
||||
background: "black"
|
||||
},
|
||||
dialogTitle: {
|
||||
marginLeft: theme.spacing(2),
|
||||
flex: 1,
|
||||
},
|
||||
itemFrame: {
|
||||
display: "flex",
|
||||
flexDirection: "row",
|
||||
flexWrap: "nowrap",
|
||||
width: "100%",
|
||||
height: "56px",
|
||||
alignItems: "center",
|
||||
textAlign: "left",
|
||||
justifyContent: "center",
|
||||
paddingLeft: 8
|
||||
},
|
||||
iconFrame: {
|
||||
flex: "0 0 auto",
|
||||
|
||||
},
|
||||
itemIcon: {
|
||||
width: 24, height: 24, margin: 12, opacity: 0.6
|
||||
},
|
||||
itemLabel: {
|
||||
flex: "1 1 1px",
|
||||
marginLeft: 8
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
|
||||
const Transition = React.forwardRef(function Transition(
|
||||
props: TransitionProps & { children?: React.ReactElement },
|
||||
ref: React.Ref<unknown>,
|
||||
) {
|
||||
return <Slide direction="up" ref={ref} {...props} />;
|
||||
});
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
const ActionBarTransition = React.forwardRef(function Transition(
|
||||
props: TransitionProps & { children?: React.ReactElement },
|
||||
ref: React.Ref<unknown>,
|
||||
) {
|
||||
return <Slide direction="right" ref={ref} {...props} />;
|
||||
});
|
||||
|
||||
|
||||
const BankDialog = withStyles(styles, { withTheme: true })(
|
||||
|
||||
class extends Component<BankDialogProps, BankDialogState> {
|
||||
|
||||
model: PiPedalModel;
|
||||
|
||||
|
||||
|
||||
constructor(props: BankDialogProps) {
|
||||
super(props);
|
||||
this.model = PiPedalModelFactory.getInstance();
|
||||
|
||||
this.handleDialogClose = this.handleDialogClose.bind(this);
|
||||
let banks = this.model.banks.get();
|
||||
this.state = {
|
||||
banks: banks,
|
||||
showActionBar: false,
|
||||
selectedItem: banks.selectedBank,
|
||||
filenameDialogOpen: false,
|
||||
filenameSaveAs: false
|
||||
};
|
||||
this.handleBanksChanged = this.handleBanksChanged.bind(this);
|
||||
this.handlePopState = this.handlePopState.bind(this);
|
||||
|
||||
}
|
||||
|
||||
selectItemAtIndex(index: number)
|
||||
{
|
||||
let instanceId = this.state.banks.entries[index].instanceId;
|
||||
this.setState({selectedItem: instanceId});
|
||||
}
|
||||
isEditMode() {
|
||||
return this.state.showActionBar || this.props.isEditDialog;
|
||||
}
|
||||
|
||||
handleBanksChanged() {
|
||||
let banks = this.model.banks.get();
|
||||
|
||||
if (!banks.areEqual(this.state.banks,false)) // avoid a bunch of peculiar effects if we update while a drag is in progress
|
||||
{
|
||||
// if we don't have a valid selection, then use the current preset.
|
||||
if (this.state.banks.getEntry(this.state.selectedItem) == null)
|
||||
{
|
||||
this.setState({ banks: banks, selectedItem: banks.selectedBank});
|
||||
} else {
|
||||
this.setState({ banks: banks });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mounted: boolean = false;
|
||||
|
||||
hasHooks: boolean = false;
|
||||
|
||||
stateWasPopped: boolean = false;
|
||||
handlePopState(e: any): any {
|
||||
let state: any = e.state;
|
||||
if (!state || !state.bankDialog)
|
||||
{
|
||||
this.stateWasPopped = true;
|
||||
this.props.onDialogClose();
|
||||
}
|
||||
}
|
||||
|
||||
updateBackButtonHooks() : void {
|
||||
let wantHooks = this.mounted && this.props.show;
|
||||
if (wantHooks !== this.hasHooks)
|
||||
{
|
||||
this.hasHooks = wantHooks;
|
||||
|
||||
if (this.hasHooks)
|
||||
{
|
||||
this.stateWasPopped = false;
|
||||
window.addEventListener("popstate",this.handlePopState);
|
||||
// eslint-disable-next-line no-restricted-globals
|
||||
let newState: any = history.state;
|
||||
if (!newState)
|
||||
{
|
||||
newState = {};
|
||||
}
|
||||
newState.bankDialog = true;
|
||||
// eslint-disable-next-line no-restricted-globals
|
||||
history.pushState(
|
||||
newState,
|
||||
"PiPedal - Banks",
|
||||
"#Banks"
|
||||
);
|
||||
} else {
|
||||
window.removeEventListener("popstate",this.handlePopState);
|
||||
if (!this.stateWasPopped)
|
||||
{
|
||||
// eslint-disable-next-line no-restricted-globals
|
||||
history.back();
|
||||
}
|
||||
// eslint-disable-next-line no-restricted-globals
|
||||
history.replaceState({},"PiPedal","#");
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
componentDidUpdate()
|
||||
{
|
||||
this.updateBackButtonHooks();
|
||||
}
|
||||
componentDidMount() {
|
||||
this.model.banks.addOnChangedHandler(this.handleBanksChanged);
|
||||
this.handleBanksChanged();
|
||||
// scroll selected item into view.
|
||||
this.mounted = true;
|
||||
this.updateBackButtonHooks();
|
||||
}
|
||||
componentWillUnmount() {
|
||||
this.model.banks.removeOnChangedHandler(this.handleBanksChanged);
|
||||
this.mounted = false;
|
||||
this.updateBackButtonHooks();
|
||||
}
|
||||
|
||||
getSelectedIndex() {
|
||||
let instanceId = this.isEditMode() ? this.state.selectedItem: this.state.banks.selectedBank;
|
||||
let banks = this.state.banks;
|
||||
for (let i = 0; i < banks.entries.length; ++i) {
|
||||
if (banks.entries[i].instanceId === instanceId) return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
handleDeleteClick() {
|
||||
if (!this.state.selectedItem) return;
|
||||
let selectedItem = this.state.selectedItem;
|
||||
if (selectedItem !== -1) {
|
||||
this.model.deleteBankItem(selectedItem)
|
||||
.then((newSelection: number) => {
|
||||
this.setState({
|
||||
selectedItem: newSelection
|
||||
});
|
||||
})
|
||||
.catch((error) => {
|
||||
this.model.showAlert(error);
|
||||
});
|
||||
}
|
||||
}
|
||||
handleDialogClose()
|
||||
{
|
||||
this.props.onDialogClose();
|
||||
}
|
||||
|
||||
handleItemClick(instanceId: number): void {
|
||||
if (this.isEditMode()) {
|
||||
this.setState({ selectedItem: instanceId });
|
||||
} else {
|
||||
this.model.openBank(instanceId);
|
||||
this.props.onDialogClose();
|
||||
}
|
||||
}
|
||||
showActionBar(show: boolean): void {
|
||||
this.setState({ showActionBar: show });
|
||||
|
||||
}
|
||||
|
||||
|
||||
mapElement(el: any): React.ReactNode {
|
||||
let presetEntry = el as BankIndexEntry;
|
||||
let classes = this.props.classes;
|
||||
let selectedItem = this.isEditMode() ? this.state.selectedItem : this.state.banks.selectedBank;
|
||||
return (
|
||||
<div key={presetEntry.instanceId} style={{ background: "white" }} >
|
||||
|
||||
<ButtonBase style={{ width: "100%", height: 48 }}
|
||||
onClick={() => this.handleItemClick(presetEntry.instanceId)}
|
||||
>
|
||||
<SelectHoverBackground selected={presetEntry.instanceId === selectedItem} showHover={true} />
|
||||
<div className={classes.itemFrame}>
|
||||
<div className={classes.iconFrame}>
|
||||
<img src="img/ic_bank.svg" className={classes.itemIcon} alt="" />
|
||||
</div>
|
||||
<div className={classes.itemLabel}>
|
||||
<Typography>
|
||||
{presetEntry.name}
|
||||
</Typography>
|
||||
</div>
|
||||
</div>
|
||||
</ButtonBase>
|
||||
</div>
|
||||
|
||||
);
|
||||
}
|
||||
|
||||
moveElement(from: number, to: number): void {
|
||||
let newBanks = this.state.banks.clone();
|
||||
newBanks.moveBank(from, to);
|
||||
this.setState({
|
||||
banks: newBanks,
|
||||
selectedItem: newBanks.entries[to].instanceId
|
||||
});
|
||||
this.model.moveBank(from, to)
|
||||
.catch((error)=> {
|
||||
this.model.showAlert(error);
|
||||
});
|
||||
}
|
||||
|
||||
getSelectedName(): string {
|
||||
let item = this.state.banks.getEntry(this.state.selectedItem);
|
||||
if (item) return item.name;
|
||||
return "";
|
||||
}
|
||||
|
||||
handleRenameClick() {
|
||||
let item = this.state.banks.getEntry(this.state.selectedItem);
|
||||
if (item) {
|
||||
this.setState({ filenameDialogOpen: true, filenameSaveAs: false });
|
||||
}
|
||||
}
|
||||
handleRenameOk(text: string) {
|
||||
let item = this.state.banks.getEntry(this.state.selectedItem);
|
||||
if (!item) return;
|
||||
if (item.name !== text) {
|
||||
if (this.state.banks.hasName(text))
|
||||
{
|
||||
this.model.showAlert("A bank with that name already exists.");
|
||||
}
|
||||
this.model.renameBank(this.state.selectedItem, text)
|
||||
.catch((error) => {
|
||||
this.onError(error);
|
||||
});
|
||||
}
|
||||
|
||||
this.setState({ filenameDialogOpen: false });
|
||||
}
|
||||
handleSaveAsOk(text: string) {
|
||||
let item = this.state.banks.getEntry(this.state.selectedItem);
|
||||
if (item)
|
||||
{
|
||||
if (item.name !== text) {
|
||||
if (this.state.banks.hasName(text))
|
||||
{
|
||||
this.model.showAlert("A bank with that name already exists.");
|
||||
return;
|
||||
}
|
||||
this.model.saveBankAs(this.state.selectedItem, text)
|
||||
.then((newSelection) => {
|
||||
this.setState({selectedItem: newSelection});
|
||||
})
|
||||
.catch((error) => {
|
||||
this.onError(error);
|
||||
});
|
||||
}
|
||||
}
|
||||
this.setState({ filenameDialogOpen: false });
|
||||
}
|
||||
|
||||
handleCopy() {
|
||||
let item = this.state.banks.getEntry(this.state.selectedItem);
|
||||
if (item) {
|
||||
this.setState({ filenameDialogOpen: true, filenameSaveAs: true });
|
||||
}
|
||||
}
|
||||
|
||||
onError(error: string): void {
|
||||
this.model?.showAlert(error);
|
||||
}
|
||||
|
||||
|
||||
|
||||
render() {
|
||||
let classes = this.props.classes;
|
||||
|
||||
let actionBarClass = this.props.isEditDialog ? classes.dialogAppBar : classes.dialogActionBar;
|
||||
let defaultSelectedIndex = this.getSelectedIndex();
|
||||
|
||||
return (
|
||||
<Dialog fullScreen open={this.props.show}
|
||||
onClose={() => { this.handleDialogClose() }} TransitionComponent={Transition}>
|
||||
<div style={{ display: "flex", flexDirection: "column", flexWrap: "nowrap", width: "100%", height: "100%", overflow: "hidden" }}>
|
||||
<div style={{ flex: "0 0 auto" }}>
|
||||
<AppBar className={classes.dialogAppBar} style={{ display: this.isEditMode() ? "none" : "block" }} >
|
||||
<Toolbar>
|
||||
<IconButton edge="start" color="inherit" onClick={this.handleDialogClose} aria-label="back"
|
||||
disabled={this.isEditMode()}
|
||||
>
|
||||
<ArrowBackIcon />
|
||||
</IconButton>
|
||||
<Typography variant="h6" className={classes.dialogTitle}>
|
||||
Banks
|
||||
</Typography>
|
||||
<IconButton color="inherit" onClick={(e) => this.showActionBar(true)} >
|
||||
<EditIcon />
|
||||
</IconButton>
|
||||
</Toolbar>
|
||||
</AppBar>
|
||||
<AppBar className={actionBarClass} style={{ display: this.isEditMode() ? "block" : "none" }}
|
||||
onClick={(e) => { e.stopPropagation(); e.preventDefault(); }}
|
||||
>
|
||||
<Toolbar>
|
||||
{(!this.props.isEditDialog) ? (
|
||||
<IconButton edge="start" color="inherit" onClick={(e) => this.showActionBar(false)} aria-label="close">
|
||||
<CloseIcon />
|
||||
</IconButton>
|
||||
) : (
|
||||
<IconButton edge="start" color="inherit" onClick={this.handleDialogClose} aria-label="back"
|
||||
>
|
||||
<ArrowBackIcon />
|
||||
</IconButton>
|
||||
|
||||
)}
|
||||
<Typography variant="h6" className={classes.dialogTitle}>
|
||||
Banks
|
||||
</Typography>
|
||||
{(this.state.banks.getEntry(this.state.selectedItem) != null)
|
||||
&& (
|
||||
|
||||
<div style={{ flex: "0 0 auto" }}>
|
||||
<Button color="inherit" onClick={(e) => this.handleCopy()}>
|
||||
Copy
|
||||
</Button>
|
||||
<Button color="inherit" onClick={() => this.handleRenameClick()}>
|
||||
Rename
|
||||
</Button>
|
||||
<RenameDialog
|
||||
open={this.state.filenameDialogOpen}
|
||||
defaultName={this.getSelectedName()}
|
||||
acceptActionName={ this.state.filenameSaveAs? "SAVE AS": "RENAME"}
|
||||
onClose={() => { this.setState({ filenameDialogOpen: false }) }}
|
||||
onOk={(text: string) => {
|
||||
if (this.state.filenameSaveAs)
|
||||
{
|
||||
this.handleSaveAsOk(text);
|
||||
} else {
|
||||
this.handleRenameOk(text);
|
||||
}
|
||||
}
|
||||
}
|
||||
/>
|
||||
<IconButton color="inherit" onClick={(e) => this.handleDeleteClick()} >
|
||||
<img src="/img/old_delete_outline_white_24dp.svg" alt="Delete" style={{width: 24, height: 24, opacity: 0.6}} />
|
||||
</IconButton>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
</Toolbar>
|
||||
|
||||
</AppBar>
|
||||
</div>
|
||||
<div style={{ flex: "1 1 auto", position: "relative", overflow: "hidden" }} >
|
||||
<DraggableGrid
|
||||
onLongPress={(item) => this.showActionBar(true)}
|
||||
canDrag={this.isEditMode()}
|
||||
onDragStart={(index,x,y)=> {this.selectItemAtIndex(index) } }
|
||||
moveElement={(from, to) => { this.moveElement(from, to); }}
|
||||
scroll={ScrollDirection.Y}
|
||||
defaultSelectedIndex={defaultSelectedIndex}
|
||||
>
|
||||
{
|
||||
this.state.banks.entries.map((element) =>
|
||||
{
|
||||
return this.mapElement(element);
|
||||
})
|
||||
}
|
||||
</DraggableGrid>
|
||||
</div>
|
||||
</div>
|
||||
</Dialog >
|
||||
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
);
|
||||
|
||||
export default BankDialog;
|
||||
@@ -0,0 +1,95 @@
|
||||
|
||||
export class BankIndexEntry {
|
||||
deserialize(input: any) : BankIndexEntry {
|
||||
this.instanceId = input.instanceId;
|
||||
this.name = input.name;
|
||||
return this;
|
||||
|
||||
}
|
||||
|
||||
areEqual(other: BankIndexEntry) : boolean {
|
||||
return (this.instanceId === other.instanceId)
|
||||
&& (this.name === other.name);
|
||||
}
|
||||
static deserialize_array(input: any): BankIndexEntry[] {
|
||||
let result: BankIndexEntry[] = [];
|
||||
for (let i = 0; i < input.length; ++i)
|
||||
{
|
||||
result.push(new BankIndexEntry().deserialize(input[i]));
|
||||
}
|
||||
return result;
|
||||
|
||||
}
|
||||
instanceId: number = -1;
|
||||
name: string = "";
|
||||
}
|
||||
|
||||
export class BankIndex {
|
||||
deserialize(input: any) : BankIndex {
|
||||
this.selectedBank = input.selectedBank;
|
||||
this.entries = BankIndexEntry.deserialize_array(input.entries);
|
||||
return this;
|
||||
}
|
||||
selectedBank: number = -1;
|
||||
entries: BankIndexEntry[] = [];
|
||||
|
||||
areEqual(other: BankIndex, includeSelection: boolean): boolean {
|
||||
if (includeSelection && this.selectedBank !== other.selectedBank) return false;
|
||||
|
||||
if (this.entries.length !== other.entries.length) return false;
|
||||
|
||||
for (let i = 0; i < this.entries.length; ++i)
|
||||
{
|
||||
if (!this.entries[i].areEqual(other.entries[i])) return false;
|
||||
}
|
||||
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
moveBank(from: number, to: number)
|
||||
{
|
||||
let t = this.entries[from];
|
||||
this.entries.splice(from,1);
|
||||
this.entries.splice(to,0,t);
|
||||
|
||||
}
|
||||
|
||||
clone(): BankIndex {
|
||||
return new BankIndex().deserialize(this);
|
||||
}
|
||||
|
||||
hasName(name: string): boolean {
|
||||
for (let i = 0; i < this.entries.length; ++i)
|
||||
{
|
||||
let entry = this.entries[i];
|
||||
if (entry.name === name) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
getEntry(instanceId: number): BankIndexEntry | null {
|
||||
for (let i = 0; i < this.entries.length; ++i)
|
||||
{
|
||||
let entry = this.entries[i];
|
||||
if (entry.instanceId === instanceId) return entry;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
getSelectedEntryName(): string {
|
||||
let entry = this.getEntry(this.selectedBank);
|
||||
if (!entry) return "";
|
||||
return entry.name;
|
||||
}
|
||||
nameExists(name: string): boolean {
|
||||
for (let i = 0; i < this.entries.length; ++i)
|
||||
{
|
||||
let entry = this.entries[i];
|
||||
if (entry.name === name) return true;
|
||||
}
|
||||
return false;
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
import React from 'react';
|
||||
import { PiPedalModel, PiPedalModelFactory } from './PiPedalModel';
|
||||
|
||||
|
||||
import {PedalBoardItem, PedalBoardSplitItem} from './PedalBoard';
|
||||
import PluginControlView from './PluginControlView';
|
||||
import SplitControlView from './SplitControlView';
|
||||
import Typography from '@material-ui/core/Typography';
|
||||
import IControlViewFactory from './IControlViewFactory';
|
||||
import ToobInputStageViewFactory from './ToobInputStageView';
|
||||
import ToobToneStackViewFactory from './ToobToneStackView';
|
||||
import ToobCabSimViewFactory from './ToobCabSimView';
|
||||
|
||||
|
||||
let pluginFactories: IControlViewFactory[] = [
|
||||
new ToobInputStageViewFactory(),
|
||||
new ToobToneStackViewFactory(),
|
||||
new ToobCabSimViewFactory()
|
||||
];
|
||||
|
||||
|
||||
export function GetControlView(pedalBoardItem?: PedalBoardItem| null): React.ReactNode
|
||||
{
|
||||
let model: PiPedalModel = PiPedalModelFactory.getInstance();
|
||||
|
||||
if (!pedalBoardItem) {
|
||||
return (<div/>);
|
||||
}
|
||||
if (pedalBoardItem.isSplit())
|
||||
{
|
||||
return (
|
||||
<SplitControlView item={pedalBoardItem as PedalBoardSplitItem} instanceId={pedalBoardItem!.instanceId}
|
||||
/>
|
||||
);
|
||||
} else {
|
||||
for (let i = 0; i < pluginFactories.length; ++i)
|
||||
{
|
||||
let factory = pluginFactories[i];
|
||||
if (factory.uri === pedalBoardItem.uri)
|
||||
{
|
||||
return factory.Create(model,pedalBoardItem);
|
||||
}
|
||||
}
|
||||
let uiPlugin = model.getUiPlugin(pedalBoardItem.uri);
|
||||
if (!uiPlugin)
|
||||
{
|
||||
<div style={{paddingLeft: 40, paddingRight: 40}}>
|
||||
<Typography color="error" variant="h6" >Missing plugin.</Typography>
|
||||
<Typography>The plugin '{pedalBoardItem.pluginName}' ({pedalBoardItem.uri}) is not currently installed.</Typography>
|
||||
</div>
|
||||
} else {
|
||||
return (
|
||||
<PluginControlView instanceId={pedalBoardItem.instanceId} item={pedalBoardItem} />
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,479 @@
|
||||
import { MouseEvent, PointerEvent, ReactNode, Component } from 'react';
|
||||
import { createStyles, withStyles, WithStyles, Theme } from '@material-ui/core/styles';
|
||||
import { PiPedalStateError } from './PiPedalError';
|
||||
|
||||
|
||||
const SELECT_SCALE = 1.5;
|
||||
|
||||
const AUTOSCROLL_TICK_DELAY = 30;
|
||||
const AUTOSCROLL_THRESHOLD = 48;
|
||||
const AUTOSCROLL_SCROLL_PER_SECOND = 100;
|
||||
const AUTOSCROLL_SCROLL_RATE = AUTOSCROLL_SCROLL_PER_SECOND * AUTOSCROLL_TICK_DELAY / 1000;
|
||||
|
||||
const LONG_PRESS_TIME_MS = 250;
|
||||
|
||||
const styles = (theme: Theme) => createStyles({
|
||||
frame: {
|
||||
position: "relative",
|
||||
margin: "12px"
|
||||
}
|
||||
});
|
||||
|
||||
type Point = { x: number, y: number };
|
||||
|
||||
export interface DraggableProps extends WithStyles<typeof styles> {
|
||||
onDragStart?: (clientX: number, clientY: number) => void;
|
||||
onDragMove?: (clientX: number, clientY: number) => void;
|
||||
onDragEnd?: (clientX: number, clientY: number) => void;
|
||||
onDragCancel?: (clientX: number, clientY: number) => void;
|
||||
getScrollContainer: () => HTMLDivElement | null | undefined;
|
||||
|
||||
children?: ReactNode | ReactNode[];
|
||||
draggable?: boolean;
|
||||
|
||||
|
||||
};
|
||||
type DraggableState = {
|
||||
};
|
||||
|
||||
|
||||
const Draggable =
|
||||
withStyles(styles, { withTheme: true })(
|
||||
class extends Component<DraggableProps, DraggableState>
|
||||
{
|
||||
|
||||
constructor(props: DraggableProps) {
|
||||
super(props);
|
||||
this.onPointerDownCapture = this.onPointerDownCapture.bind(this);
|
||||
this.onPointerDown = this.onPointerDown.bind(this);
|
||||
this.onPointerCancel = this.onPointerCancel.bind(this);
|
||||
this.onPointerMove = this.onPointerMove.bind(this);
|
||||
this.onPointerUp = this.onPointerUp.bind(this);
|
||||
this.onClick = this.onClick.bind(this);
|
||||
this.autoScrollTick = this.autoScrollTick.bind(this);
|
||||
this.handleTouchMove = this.handleTouchMove.bind(this);
|
||||
this.handleTouchEnd = this.handleTouchEnd.bind(this);
|
||||
this.handleTouchStart = this.handleTouchStart.bind(this);
|
||||
|
||||
}
|
||||
handleTouchStart(e: any) {
|
||||
if (this.dragStarted) {
|
||||
//e.preventDefault();
|
||||
}
|
||||
}
|
||||
handleTouchMove(e: any) {
|
||||
if (this.dragStarted) {
|
||||
e.preventDefault();
|
||||
}
|
||||
}
|
||||
handleTouchEnd(e: any) {
|
||||
if (this.dragStarted) {
|
||||
e.preventDefault();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
isValidPointer(e: PointerEvent<HTMLDivElement>): boolean {
|
||||
if (e.pointerType === "mouse") {
|
||||
return e.button === 0;
|
||||
} else if (e.pointerType === "pen") {
|
||||
return true;
|
||||
} else if (e.pointerType === "touch") {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
mouseDown: boolean = false;
|
||||
pointerId: number = 0;
|
||||
pointerType: string = "";
|
||||
|
||||
lastOnDragTime: number = 0;
|
||||
|
||||
onClick(e: MouseEvent<HTMLDivElement>) {
|
||||
// if the click event immediately follows drag end, suppress it.
|
||||
let dt = new Date().getTime() - this.lastOnDragTime;
|
||||
if (dt < 150) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
componentDidMount()
|
||||
{
|
||||
}
|
||||
componentWillUnmount()
|
||||
{
|
||||
|
||||
}
|
||||
isCapturedPointer(e: PointerEvent<HTMLDivElement>): boolean {
|
||||
return this.mouseDown
|
||||
&& e.pointerId === this.pointerId
|
||||
&& e.pointerType === this.pointerType;
|
||||
|
||||
}
|
||||
|
||||
startX: number = 0;
|
||||
startY: number = 0;
|
||||
startClientX: number = 0;
|
||||
startClientY: number = 0;
|
||||
dragStarted: boolean = false;
|
||||
savedIndex: string = "";
|
||||
lastPointerDown: number = 0;
|
||||
originalBounds?: DOMRect;
|
||||
captureElement?: HTMLDivElement;
|
||||
pointerDownTime: number = 0;
|
||||
|
||||
onPointerDownCapture(e: any) {
|
||||
// a new pointer down of any type cancels capture
|
||||
this.cancelDrag();
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
}
|
||||
longPressTimer?: NodeJS.Timeout = undefined;
|
||||
|
||||
longPressTimerTick() {
|
||||
if (this.pointerType === "touch") {
|
||||
if (navigator.vibrate) {
|
||||
navigator.vibrate([10]);
|
||||
}
|
||||
if (!this.dragStarted) {
|
||||
this.dragTarget!.style.transform = "scale(" + SELECT_SCALE + ")";
|
||||
this.dragTarget!.style.zIndex = "3";
|
||||
}
|
||||
|
||||
this.startDrag();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
cancelDrag() {
|
||||
window.removeEventListener("touchmove", this.handleTouchMove);
|
||||
window.removeEventListener("touchend", this.handleTouchEnd);
|
||||
if (this.longPressTimer) {
|
||||
clearTimeout(this.longPressTimer);
|
||||
this.longPressTimer = undefined;
|
||||
}
|
||||
|
||||
this.stopAutoScroll();
|
||||
if (this.dragStarted && this.captureElement) {
|
||||
try {
|
||||
this.captureElement.releasePointerCapture(this.pointerId);
|
||||
} catch (error)
|
||||
{
|
||||
// throws if we've already lost it.
|
||||
}
|
||||
|
||||
}
|
||||
this.dragStarted = false;
|
||||
if (this.captureElement) {
|
||||
this.clearDragTransform(this.captureElement);
|
||||
this.captureElement = undefined;
|
||||
}
|
||||
this.mouseDown = false;
|
||||
|
||||
document.body.removeEventListener("pointerdown", this.onPointerDownCapture, true);
|
||||
this.dragTarget = undefined;
|
||||
}
|
||||
|
||||
|
||||
dragTarget?: HTMLDivElement;
|
||||
|
||||
onPointerDown(e: PointerEvent<HTMLDivElement>): void {
|
||||
|
||||
if (this.props.draggable !== undefined && !this.props.draggable) return;
|
||||
|
||||
// any new pointer down cancles a drag in progress.
|
||||
this.cancelDrag();
|
||||
|
||||
// avoid 2nd click of a double click (which is difficult to cancel)
|
||||
let timeMs = new Date().getTime();
|
||||
if (timeMs - this.lastPointerDown < 300) {
|
||||
this.lastPointerDown = timeMs;
|
||||
return;
|
||||
}
|
||||
this.lastPointerDown = timeMs;
|
||||
|
||||
|
||||
if (!this.mouseDown && this.isValidPointer(e)) {
|
||||
|
||||
this.longPressTimer = setTimeout(() => this.longPressTimerTick(), LONG_PRESS_TIME_MS);
|
||||
|
||||
this.dragTarget = e.currentTarget as HTMLDivElement;
|
||||
document.body.addEventListener("pointerdown", this.onPointerDownCapture, true);
|
||||
this.captureElement = e.currentTarget;
|
||||
// e.currentTarget.setPointerCapture(e.pointerId);
|
||||
|
||||
this.mouseDown = true;
|
||||
this.dragStarted = false;
|
||||
this.startX = e.clientX;
|
||||
this.startY = e.clientY;
|
||||
this.startClientX = e.clientX;
|
||||
this.startClientY = e.clientY;
|
||||
|
||||
this.pointerId = e.pointerId;
|
||||
this.pointerType = e.pointerType;
|
||||
this.savedIndex = e.currentTarget.style.zIndex;
|
||||
if (this.pointerType !== "touch") {
|
||||
this.dragTarget.style.transform = "scale(" + SELECT_SCALE + ")";
|
||||
this.dragTarget.style.zIndex = "3";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
clearDragTransform(element: HTMLDivElement): void {
|
||||
element.style.transform = "";
|
||||
element.style.zIndex = this.savedIndex;
|
||||
}
|
||||
|
||||
onPointerCancel(e: PointerEvent<HTMLDivElement>) {
|
||||
if (this.isCapturedPointer(e)) {
|
||||
this.cancelDrag();
|
||||
if (this.props.onDragCancel) {
|
||||
this.props.onDragCancel(e.clientX, e.clientY);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onPointerUp(e: PointerEvent<HTMLDivElement>) {
|
||||
if (this.isCapturedPointer(e)) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
if (this.dragStarted) {
|
||||
this.lastOnDragTime = new Date().getTime();
|
||||
if (this.props.onDragEnd) {
|
||||
this.props.onDragEnd(e.clientX, e.clientY);
|
||||
}
|
||||
}
|
||||
this.cancelDrag();
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
dragThresholdExceeded(e: PointerEvent<HTMLDivElement>) {
|
||||
let dx = e.clientX - this.startX;
|
||||
let dy = e.clientY - this.startY;
|
||||
let d2 = dx * dx + dy * dy;
|
||||
if (this.pointerType === "touch") {
|
||||
return false; // wait for long press.
|
||||
}
|
||||
|
||||
let DRAG_THRESHOLD = 5;
|
||||
if (this.pointerType === "touch") {
|
||||
DRAG_THRESHOLD = 15;
|
||||
}
|
||||
let result = (d2 > DRAG_THRESHOLD * DRAG_THRESHOLD);
|
||||
return result;
|
||||
}
|
||||
|
||||
lastX: number = 0;
|
||||
lastY: number = 0;
|
||||
|
||||
translateViewPortPoint(fromElement: HTMLElement, toElement: HTMLElement, pt: Point): Point {
|
||||
let rcFrom = fromElement.getBoundingClientRect();
|
||||
let rcTo = toElement.getBoundingClientRect();
|
||||
|
||||
return {
|
||||
x: pt.x + rcFrom.left + fromElement.scrollLeft - rcTo.left - toElement.scrollLeft,
|
||||
y: pt.y + rcFrom.top + fromElement.scrollTop - rcTo.top - toElement.scrollTop
|
||||
|
||||
};
|
||||
}
|
||||
translateViewPortRect(fromElement: HTMLElement, toElement: HTMLElement, rect: DOMRect): DOMRect {
|
||||
let rcFrom = fromElement.getBoundingClientRect();
|
||||
let rcTo = toElement.getBoundingClientRect();
|
||||
|
||||
return new DOMRect(
|
||||
rect.x + rcFrom.left - fromElement.scrollLeft - rcTo.left + toElement.scrollLeft,
|
||||
rect.y + rcFrom.top - fromElement.scrollTop - rcTo.top + toElement.scrollTop,
|
||||
rect.width,
|
||||
rect.height);
|
||||
}
|
||||
|
||||
|
||||
makeTransform(targetElement: HTMLElement, clientX: number, clientY: number): string {
|
||||
let scrollContainer = this.getScrollContainer();
|
||||
if (scrollContainer && this.originalBounds) {
|
||||
// the bounds of the translated element must COMPLETELY fit within the scroll range of the container.
|
||||
let newLocation = new DOMRect(
|
||||
this.originalBounds.x + clientX - this.startX,
|
||||
this.originalBounds.y + clientY - this.startY,
|
||||
this.originalBounds.width,
|
||||
this.originalBounds.height);
|
||||
|
||||
|
||||
if (newLocation.x < 0) {
|
||||
clientX -= newLocation.x;
|
||||
} else if (newLocation.right >= scrollContainer.scrollWidth) {
|
||||
clientX -= newLocation.right - scrollContainer.scrollWidth;
|
||||
}
|
||||
if (newLocation.top < 0) {
|
||||
clientY -= newLocation.top;
|
||||
} else if (newLocation.bottom > scrollContainer.scrollHeight) {
|
||||
clientY -= newLocation.bottom - scrollContainer.scrollHeight;
|
||||
}
|
||||
|
||||
}
|
||||
return "translate(" + (clientX - this.startX) + "px," + (clientY - this.startY) + "px) scale(" + SELECT_SCALE + ")";
|
||||
|
||||
}
|
||||
|
||||
createOriginalBounds() {
|
||||
// bounds of the target element, in ScrollContainer coordinates.
|
||||
let rcTarget = this.dragTarget!.getBoundingClientRect();
|
||||
rcTarget.x = 0;
|
||||
rcTarget.y = 0;
|
||||
|
||||
let scrollContainer = this.props.getScrollContainer();
|
||||
if (!scrollContainer) {
|
||||
throw new PiPedalStateError("ScrollContainer reference not valid.");
|
||||
}
|
||||
return this.translateViewPortRect(this.dragTarget!, scrollContainer, rcTarget);
|
||||
}
|
||||
|
||||
startDrag() {
|
||||
window.addEventListener("touchmove", this.handleTouchMove, { passive: false });
|
||||
window.addEventListener("touchend", this.handleTouchEnd, { passive: false });
|
||||
|
||||
|
||||
this.dragStarted = true;
|
||||
if (this.props.onDragStart) {
|
||||
this.props.onDragStart(this.startX, this.startY);
|
||||
}
|
||||
this.dragTarget!.setPointerCapture(this.pointerId);
|
||||
this.originalBounds = this.createOriginalBounds();
|
||||
|
||||
}
|
||||
|
||||
onPointerMove(e: PointerEvent<HTMLDivElement>): void {
|
||||
if (this.isCapturedPointer(e)) {
|
||||
if (!this.dragStarted && this.dragThresholdExceeded(e)) {
|
||||
this.startDrag();
|
||||
|
||||
|
||||
}
|
||||
if (this.dragStarted) {
|
||||
this.lastX = e.clientX;
|
||||
this.lastY = e.clientY;
|
||||
e.currentTarget.style.transform = this.makeTransform(e.currentTarget, e.clientX, e.clientY);
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
|
||||
if (this.props.onDragMove) {
|
||||
this.props.onDragMove(e.clientX, e.clientY);
|
||||
}
|
||||
|
||||
this.checkForAutoScroll(e.currentTarget);
|
||||
}
|
||||
}
|
||||
}
|
||||
autoScrollTimer?: number;
|
||||
|
||||
autoScrollTick(currentTarget: HTMLElement, dx: number, dy: number) {
|
||||
if (!this.mouseDown) return;
|
||||
let scrollContainer = this.getScrollContainer();
|
||||
if (scrollContainer) {
|
||||
let tx = scrollContainer.scrollLeft;
|
||||
let ty = scrollContainer.scrollTop;
|
||||
scrollContainer.scrollBy(dx, dy);
|
||||
this.startX -= (scrollContainer.scrollLeft - tx);
|
||||
this.startY -= (scrollContainer.scrollTop - ty);
|
||||
|
||||
currentTarget.style.transform = this.makeTransform(currentTarget, this.lastX, this.lastY);
|
||||
|
||||
this.checkForAutoScroll(currentTarget);
|
||||
}
|
||||
}
|
||||
stopAutoScroll() {
|
||||
if (this.autoScrollTimer) {
|
||||
cancelAnimationFrame(this.autoScrollTimer);
|
||||
this.autoScrollTimer = undefined;
|
||||
}
|
||||
}
|
||||
startAutoScroll(scrollContainer: HTMLElement, dx: number, dy: number) {
|
||||
this.stopAutoScroll();
|
||||
|
||||
this.autoScrollTimer = requestAnimationFrame(
|
||||
() => { this.autoScrollTick(scrollContainer, dx, dy) });
|
||||
}
|
||||
|
||||
checkForAutoScroll(currentTarget: HTMLElement) {
|
||||
let scrollContainer = this.getScrollContainer();
|
||||
if (!scrollContainer) {
|
||||
this.stopAutoScroll();
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
let scrollContainerRect = scrollContainer.getBoundingClientRect();
|
||||
|
||||
let dy: number = 0;
|
||||
let dx: number = 0;
|
||||
|
||||
if (this.lastX < scrollContainerRect.x + AUTOSCROLL_THRESHOLD) {
|
||||
dx = -AUTOSCROLL_SCROLL_RATE;
|
||||
if (dx < -scrollContainer.scrollLeft) {
|
||||
dx = -scrollContainer.scrollLeft;
|
||||
}
|
||||
} else if (this.lastX >= scrollContainerRect.right - AUTOSCROLL_THRESHOLD) {
|
||||
dx = AUTOSCROLL_SCROLL_RATE;
|
||||
let maxScroll = Math.max(scrollContainer.scrollWidth - scrollContainer.clientWidth, 0);
|
||||
if (dx > maxScroll) dx = maxScroll;
|
||||
}
|
||||
if (this.lastY < scrollContainerRect.top) {
|
||||
dy = -AUTOSCROLL_SCROLL_RATE;
|
||||
if (dy < -scrollContainer.scrollTop) dy = -scrollContainer.scrollTop;
|
||||
} else if (this.lastY >= scrollContainerRect.top - AUTOSCROLL_THRESHOLD) {
|
||||
dy = AUTOSCROLL_SCROLL_RATE;
|
||||
let maxScroll = Math.max(scrollContainer.scrollHeight - scrollContainer.clientHeight);
|
||||
if (dy > maxScroll) dy = maxScroll;
|
||||
}
|
||||
|
||||
if (dx === 0 && dy === 0) {
|
||||
this.stopAutoScroll();
|
||||
return;
|
||||
} else {
|
||||
this.startAutoScroll(currentTarget, dx, dy);
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
getScrollContainer(): HTMLDivElement | null {
|
||||
if (this.props.getScrollContainer) {
|
||||
let t = this.props.getScrollContainer();
|
||||
if (!t) return null;
|
||||
return t;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div style={{ transform: "" }}
|
||||
onPointerDown={this.onPointerDown}
|
||||
onPointerMove={this.onPointerMove}
|
||||
onPointerCancel={this.onPointerCancel}
|
||||
onPointerCancelCapture={this.onPointerCancel}
|
||||
onPointerUp={this.onPointerUp}
|
||||
onDragStart={(e) => { e.preventDefault(); e.stopPropagation(); }}
|
||||
onClick={this.onClick}
|
||||
>
|
||||
{
|
||||
this.props.children
|
||||
}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
);
|
||||
|
||||
export default Draggable;
|
||||
@@ -0,0 +1,922 @@
|
||||
import React, { MouseEvent, PointerEvent, Component } from 'react';
|
||||
import { createStyles, Theme } from '@material-ui/core/styles';
|
||||
import { Property } from "csstype";
|
||||
import { nullCast} from './Utility'
|
||||
|
||||
const SELECT_SCALE = 1.0;
|
||||
|
||||
const AUTOSCROLL_TICK_DELAY = 30;
|
||||
const AUTOSCROLL_THRESHOLD = 48;
|
||||
const AUTOSCROLL_SCROLL_PER_SECOND = 300;
|
||||
const AUTOSCROLL_SCROLL_RATE = AUTOSCROLL_SCROLL_PER_SECOND * AUTOSCROLL_TICK_DELAY / 1000;
|
||||
|
||||
const LONG_PRESS_TIME_MS = 250;
|
||||
|
||||
|
||||
const ANIMATION_DURATION_MS = 100;
|
||||
|
||||
|
||||
|
||||
class AnimationData {
|
||||
constructor(element: HTMLElement, originalPosition: DOMRect) {
|
||||
this.element = element;
|
||||
this.originalPosition = originalPosition;
|
||||
this.fromY = this.toY = this.currentY = this.originalPosition.y;
|
||||
this.key = (element as any)["key"];
|
||||
}
|
||||
key: any;
|
||||
element: HTMLElement;
|
||||
originalPosition: DOMRect;
|
||||
fromY: number;
|
||||
toY: number;
|
||||
currentY: number;
|
||||
|
||||
animating: boolean = false;
|
||||
|
||||
setPosition(y: number)
|
||||
{
|
||||
this.fromY = this.toY;
|
||||
this.toY = y;
|
||||
this.animating = true;
|
||||
}
|
||||
snapToPosition() {
|
||||
this.fromY = this.toY;
|
||||
this.currentY = this.toY;
|
||||
this.animating = true;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
const styles = (theme: Theme) => createStyles({
|
||||
frame: {
|
||||
position: "relative",
|
||||
margin: "12px",
|
||||
background: theme.palette.background.paper
|
||||
}
|
||||
});
|
||||
|
||||
type Point = { x: number, y: number };
|
||||
|
||||
export enum ScrollDirection {
|
||||
None, X, Y
|
||||
}
|
||||
export interface DraggableGridProps
|
||||
{
|
||||
fullWidth?: boolean; // defaults to true
|
||||
fullHeight?: boolean; // defaults to true.
|
||||
onDragStart?: (itemIndex: number, clientX: number, clientY: number) => void;
|
||||
onDragMove?: (currentItemIndex: number, clientX: number, clientY: number) => void;
|
||||
onDragEnd?: (fromIndex: number, toIndex: number, clientX: number, clientY: number) => void;
|
||||
onDragCancel?: (clientX: number, clientY: number) => void;
|
||||
|
||||
defaultSelectedIndex?: number;
|
||||
|
||||
onLongPress?: (itemIndex: number) => void;
|
||||
|
||||
canDrag?: boolean;
|
||||
|
||||
children: React.ReactNode[];
|
||||
|
||||
moveElement: (from: number, to: number) => void;
|
||||
scroll: ScrollDirection;
|
||||
|
||||
};
|
||||
|
||||
type DraggableGridState = {
|
||||
indexToSelect?: number;
|
||||
};
|
||||
|
||||
|
||||
|
||||
const DraggableGrid =
|
||||
(
|
||||
class extends Component<DraggableGridProps, DraggableGridState>
|
||||
{
|
||||
refScrollContainer: React.RefObject<HTMLDivElement>;
|
||||
refGrid: React.RefObject<HTMLDivElement>;
|
||||
|
||||
constructor(props: DraggableGridProps) {
|
||||
super(props);
|
||||
this.refScrollContainer = React.createRef();
|
||||
this.refGrid = React.createRef();
|
||||
|
||||
this.state = {
|
||||
indexToSelect: props.defaultSelectedIndex
|
||||
};
|
||||
|
||||
this.handlePointerDownCapture = this.handlePointerDownCapture.bind(this);
|
||||
this.handlePointerDown = this.handlePointerDown.bind(this);
|
||||
this.handlePointerCancel = this.handlePointerCancel.bind(this);
|
||||
this.handlePointerMove = this.handlePointerMove.bind(this);
|
||||
this.handlePointerUp = this.handlePointerUp.bind(this);
|
||||
this.onClick = this.onClick.bind(this);
|
||||
this.handleAutoScrollTick = this.handleAutoScrollTick.bind(this);
|
||||
this.animationTick = this.animationTick.bind(this);
|
||||
this.longPressTimerTick = this.longPressTimerTick.bind(this);
|
||||
this.handleTouchMove = this.handleTouchMove.bind(this);
|
||||
this.handleTouchEnd = this.handleTouchEnd.bind(this);
|
||||
}
|
||||
|
||||
animationData: AnimationData[] = [];
|
||||
|
||||
|
||||
handleTouchMove(e: any)
|
||||
{
|
||||
if (this.dragStarted)
|
||||
{
|
||||
e.preventDefault();
|
||||
}
|
||||
}
|
||||
handleTouchEnd(e: any)
|
||||
{
|
||||
if (this.dragStarted)
|
||||
{
|
||||
e.preventDefault();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
animationStartTime: number = 0;
|
||||
timerHandle: number| null = null;
|
||||
|
||||
animationTick() {
|
||||
let t = (new Date().getTime() - this.animationStartTime) / ANIMATION_DURATION_MS;
|
||||
if (t > 1) t = 1;
|
||||
|
||||
// easing function.
|
||||
//t = 1-(1-t)*(1-t);
|
||||
let didAnimate: boolean = false;
|
||||
for (let i = 0; i < this.animationData.length; ++i) {
|
||||
let animationData = this.animationData[i];
|
||||
if (animationData.animating)
|
||||
{
|
||||
if (i !== this.startIndex) {
|
||||
//if ((animationData.xFrom !== animationData.xTo) || (animationData.yFrom !== animationData.yTo))
|
||||
let y = t * (animationData.toY - animationData.fromY) + animationData.fromY;
|
||||
let style = animationData.element.style;
|
||||
style.left = "0px";
|
||||
style.top = y-animationData.originalPosition.y + "px";
|
||||
animationData.currentY = y;
|
||||
animationData.animating = animationData.toY !== animationData.fromY;
|
||||
if (animationData.animating) didAnimate = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (t < 1 && didAnimate) {
|
||||
this.timerHandle = requestAnimationFrame(this.animationTick)
|
||||
} else {
|
||||
this.timerHandle = null;
|
||||
}
|
||||
|
||||
}
|
||||
startAnimation() {
|
||||
this.animationStartTime = new Date().getTime();
|
||||
|
||||
if (this.timerHandle === null) {
|
||||
|
||||
this.timerHandle = requestAnimationFrame(this.animationTick);
|
||||
this.animationTick();
|
||||
}
|
||||
}
|
||||
stopAnimation() {
|
||||
if (this.timerHandle !== null) {
|
||||
cancelAnimationFrame(this.timerHandle);
|
||||
}
|
||||
this.timerHandle = null;
|
||||
}
|
||||
|
||||
isValidPointer(e: PointerEvent<HTMLDivElement>): boolean
|
||||
{
|
||||
if (e.pointerType === "mouse") {
|
||||
return e.button === 0;
|
||||
} else if (e.pointerType === "pen") {
|
||||
return true;
|
||||
} else if (e.pointerType === "touch") {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bringSelectedItemIntoView() {
|
||||
if (this.state.indexToSelect && this.state.indexToSelect >= 0)
|
||||
{
|
||||
let grid = this.refGrid.current;
|
||||
if (grid)
|
||||
{
|
||||
if (this.state.indexToSelect >= 0 && this.state.indexToSelect < grid.childNodes.length)
|
||||
{
|
||||
let element = grid.childNodes[this.state.indexToSelect] as HTMLElement;
|
||||
element.scrollIntoView({
|
||||
block: "nearest",
|
||||
inline: "nearest"
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
componentDidMount() {
|
||||
this.bringSelectedItemIntoView();
|
||||
// this.refGrid.current!.addEventListener("touchstart",(e)=> this.handleTouchStart(e),{passive: false});
|
||||
}
|
||||
componentWillUnmount()
|
||||
{
|
||||
//this.refGrid.current!.removeEventListener("touchstart",(e) => this.handleTouchStart(e));
|
||||
this.stopAnimation();
|
||||
|
||||
}
|
||||
|
||||
componentDidUpdate() {
|
||||
if (!this.mouseDown)
|
||||
{
|
||||
this.bringSelectedItemIntoView();
|
||||
}
|
||||
if (this.mouseDown)
|
||||
{
|
||||
this.reprepareChildren();
|
||||
this.reflowChildren();
|
||||
this.stopAnimation();
|
||||
this.animationTick();
|
||||
}
|
||||
}
|
||||
|
||||
mouseDown: boolean = false;
|
||||
pointerId: number = 0;
|
||||
pointerType: string = "";
|
||||
|
||||
lastOnDragTime: number = 0;
|
||||
|
||||
onClick(e: MouseEvent<HTMLDivElement>) {
|
||||
// if the click event immediately follows drag end, suppress it.
|
||||
let dt = new Date().getTime() - this.lastOnDragTime;
|
||||
if (dt < 150) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
}
|
||||
|
||||
}
|
||||
hasClass(element: HTMLElement, className: string) {
|
||||
let classes = element.classList;
|
||||
for (let i = 0; i < classes.length; ++i) {
|
||||
if (classes[i] === className) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
findGridItem(element: any | null): HTMLDivElement | null {
|
||||
let grid = this.refGrid.current;
|
||||
if(!grid) return null;
|
||||
while (element !== null && element !== grid) {
|
||||
if (element.parentElement === grid) {
|
||||
return element as HTMLDivElement;
|
||||
}
|
||||
element = element.parentElement;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
isCapturedPointer(e: PointerEvent<HTMLDivElement>): boolean {
|
||||
return this.mouseDown
|
||||
&& e.pointerId === this.pointerId
|
||||
&& e.pointerType === this.pointerType;
|
||||
|
||||
}
|
||||
|
||||
startX: number = 0;
|
||||
startY: number = 0;
|
||||
startClientX: number = 0;
|
||||
startClientY: number = 0;
|
||||
dragStarted: boolean = false;
|
||||
savedIndex: string = "";
|
||||
savedBackground: string = "";
|
||||
lastPointerDown: number = 0;
|
||||
|
||||
ptItemStart: DOMPoint = new DOMPoint(0, 0);
|
||||
captureElement?: HTMLDivElement;
|
||||
|
||||
handlePointerDownCapture(e: any) {
|
||||
// a new pointer down of any type cancels capture
|
||||
this.cancelDrag();
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
}
|
||||
|
||||
|
||||
|
||||
cancelDrag() {
|
||||
//window.removeEventListener("touchstart",this.handleTouchStart);
|
||||
window.removeEventListener("touchmove",this.handleTouchMove);
|
||||
window.removeEventListener("touchend",this.handleTouchEnd);
|
||||
|
||||
if (this.originalOverscrollBehaviourY)
|
||||
{
|
||||
this.getScrollContainer()!.style.setProperty("overscroll-behaviour-y",this.originalOverscrollBehaviourY);
|
||||
this.originalOverscrollBehaviourY = undefined;
|
||||
}
|
||||
|
||||
if (this.longPressTimer) {
|
||||
clearTimeout(this.longPressTimer);
|
||||
this.longPressTimer = undefined;
|
||||
}
|
||||
this.stopAnimation();
|
||||
this.stopAutoScroll();
|
||||
if (this.dragStarted && this.captureElement) {
|
||||
try {
|
||||
this.captureElement.releasePointerCapture(this.pointerId);
|
||||
} catch (error)
|
||||
{
|
||||
// throws if we've already lost it.
|
||||
}
|
||||
|
||||
}
|
||||
this.dragStarted = false;
|
||||
if (this.dragTarget) {
|
||||
this.clearDragTransform(this.dragTarget);
|
||||
this.dragTarget = null;
|
||||
}
|
||||
this.captureElement = undefined;
|
||||
this.mouseDown = false;
|
||||
this.unprepareChildren();
|
||||
|
||||
document.body.removeEventListener("pointerdown", this.handlePointerDownCapture, true);
|
||||
}
|
||||
|
||||
|
||||
prepareChildren() {
|
||||
if (this.refGrid.current === null) return;
|
||||
let grid = this.refGrid.current;
|
||||
let nodes = grid.childNodes;
|
||||
let childRects = [];
|
||||
|
||||
let gridClientRect = grid.getBoundingClientRect();
|
||||
for (let i = 0; i < nodes.length; ++i) {
|
||||
let child = nodes[i] as HTMLDivElement;
|
||||
let rect = child.getBoundingClientRect();
|
||||
rect.x -= gridClientRect.left;
|
||||
rect.y -= gridClientRect.top;
|
||||
childRects[i] = rect;
|
||||
}
|
||||
for (let i = 0; i < nodes.length; ++i) {
|
||||
let child = nodes[i] as HTMLDivElement;
|
||||
child.style.position = "relative";
|
||||
child.style.left = "0px";
|
||||
child.style.top = "0px";
|
||||
}
|
||||
this.animationData = [];
|
||||
for (let i = 0; i < nodes.length; ++i) {
|
||||
this.animationData[i] = new AnimationData(nodes[i] as HTMLElement, childRects[i]);
|
||||
}
|
||||
grid.style.height = this.gridBounds.height + "px";
|
||||
grid.style.width = this.gridBounds.width + "px";
|
||||
|
||||
}
|
||||
reprepareChildren()
|
||||
{
|
||||
if (this.refGrid.current === null) return;
|
||||
let grid = this.refGrid.current;
|
||||
let nodes = grid.childNodes;
|
||||
|
||||
for (let i = 0; i < nodes.length; ++i) {
|
||||
let child = nodes[i] as HTMLDivElement;
|
||||
child.style.position = "relative";
|
||||
child.style.left = "0px";
|
||||
child.style.top = "0px";
|
||||
}
|
||||
if (nodes.length !== this.animationData.length)
|
||||
{
|
||||
this.cancelDrag();
|
||||
return;
|
||||
}
|
||||
for (let i = 0; i < nodes.length; ++i) {
|
||||
let key: any = (nodes[i] as any).key;
|
||||
if (nodes[i] as HTMLElement !== this.animationData[i].element) {
|
||||
console.log("Animation failed: Element " + i + " changed.");
|
||||
}
|
||||
if (key !== this.animationData[i].key) {
|
||||
console.log("Animation failed: Element " + i + " key has changed.");
|
||||
}
|
||||
this.animationData[i].element = nodes[i] as HTMLElement;
|
||||
if (i !== this.startIndex)
|
||||
{
|
||||
this.animationData[i].animating = true;
|
||||
}
|
||||
}
|
||||
grid.style.height = this.gridBounds.height + "px";
|
||||
grid.style.width = this.gridBounds.width + "px";
|
||||
this.animationTick();
|
||||
|
||||
|
||||
}
|
||||
|
||||
getIndexFromPosition(clientX: number, clientY: number)
|
||||
{
|
||||
let grid = nullCast(this.refGrid.current);
|
||||
let gridBounds = grid.getBoundingClientRect();
|
||||
clientX -= gridBounds.left;
|
||||
clientY -= gridBounds.top;
|
||||
|
||||
if (clientY < 0) return -1;
|
||||
for (let i = 0; i < this.animationData.length; ++i)
|
||||
{
|
||||
let item = this.animationData[i];
|
||||
if (clientY >= item.originalPosition.x && clientY < item.originalPosition.bottom)
|
||||
{
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
|
||||
|
||||
}
|
||||
getDragIndexFromPosition(clientX: number, clientY: number): number {
|
||||
|
||||
let grid = nullCast(this.refGrid.current);
|
||||
|
||||
let originalBounds = this.animationData[this.startIndex].originalPosition;
|
||||
// Base test on the CENTER point of the currently dragged item.
|
||||
clientX += originalBounds.width / 2 - this.ptItemStart.x;
|
||||
clientY += originalBounds.height / 2 - this.ptItemStart.y;
|
||||
|
||||
let gridBounds = grid.getBoundingClientRect();
|
||||
|
||||
clientX -= gridBounds.x;
|
||||
clientY -= gridBounds.top;
|
||||
|
||||
// clientX/Y is now the position of the center of the currently-dragged item, in the coordinates of the contenst of the scroll view (scroll applied)
|
||||
|
||||
|
||||
if (clientY < 0) return 0;
|
||||
for (let i = 0; i < this.animationData.length; ++i) {
|
||||
let rect = this.animationData[i].originalPosition;
|
||||
|
||||
if (clientY >= rect.top && clientY < rect.bottom)
|
||||
{
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return this.animationData.length-1;
|
||||
}
|
||||
|
||||
|
||||
unprepareChildren() {
|
||||
if (this.refGrid.current === null) return;
|
||||
let grid = this.refGrid.current;
|
||||
let nodes = grid.childNodes;
|
||||
|
||||
for (let i = 0; i < nodes.length; ++i) {
|
||||
let child = nodes[i] as HTMLDivElement;
|
||||
child.style.position = "";
|
||||
child.style.top = ""
|
||||
child.style.left = "";
|
||||
child.style.width = "";
|
||||
child.style.height = "";
|
||||
}
|
||||
|
||||
grid.style.height = "";
|
||||
grid.style.width = "100%";
|
||||
|
||||
// releast refs and storage.
|
||||
this.dragTarget = null;
|
||||
this.animationData = [];
|
||||
|
||||
}
|
||||
|
||||
dragTarget: HTMLDivElement | null = null;
|
||||
|
||||
elementKeys: any[] = [];
|
||||
|
||||
|
||||
|
||||
clearDragTransform(element: HTMLDivElement): void {
|
||||
element.style.transform = "";
|
||||
element.style.zIndex = this.savedIndex;
|
||||
element.style.background = this.savedBackground;
|
||||
element.style.opacity = "1";
|
||||
|
||||
}
|
||||
|
||||
handlePointerCancel(e: PointerEvent<HTMLDivElement>) {
|
||||
if (this.isCapturedPointer(e)) {
|
||||
this.cancelDrag();
|
||||
if (this.props.onDragCancel) {
|
||||
this.props.onDragCancel(e.clientX, e.clientY);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
handlePointerUp(e: PointerEvent<HTMLDivElement>) {
|
||||
if (this.isCapturedPointer(e)) {
|
||||
|
||||
this.assertKeysNotChanged(this.elementKeys); // make sure that a late render hasn't swapped elements while we're dragging.
|
||||
|
||||
this.handlePointerMove(e);
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
if (this.dragStarted) {
|
||||
this.lastOnDragTime = new Date().getTime();
|
||||
if (this.props.onDragEnd) {
|
||||
this.props.onDragEnd(
|
||||
this.startIndex, this.currentIndex,
|
||||
e.clientX, e.clientY);
|
||||
}
|
||||
if (this.startIndex !== this.currentIndex) {
|
||||
this.props.moveElement(this.startIndex, this.currentIndex);
|
||||
}
|
||||
}
|
||||
this.cancelDrag();
|
||||
}
|
||||
}
|
||||
|
||||
dragThresholdExceeded(e: PointerEvent<HTMLDivElement>) {
|
||||
if (this.pointerType === "touch")
|
||||
{
|
||||
let timeMs = new Date().getTime();
|
||||
let dt = timeMs -this.lastPointerDown;
|
||||
if (dt < LONG_PRESS_TIME_MS) return false;
|
||||
}
|
||||
|
||||
let dx = e.clientX - this.startX;
|
||||
let dy = e.clientY - this.startY;
|
||||
let d2 = dx * dx + dy * dy;
|
||||
|
||||
let DRAG_THRESHOLD = 5;
|
||||
if (this.pointerType === "touch")
|
||||
{
|
||||
DRAG_THRESHOLD = 15;
|
||||
}
|
||||
return (d2 > DRAG_THRESHOLD * DRAG_THRESHOLD);
|
||||
}
|
||||
|
||||
lastX: number = 0;
|
||||
lastY: number = 0;
|
||||
|
||||
translateViewPortPoint(fromElement: HTMLElement, toElement: HTMLElement, pt: Point): Point {
|
||||
let rcFrom = fromElement.getBoundingClientRect();
|
||||
let rcTo = toElement.getBoundingClientRect();
|
||||
|
||||
return {
|
||||
x: pt.x + rcFrom.left + fromElement.scrollLeft - rcTo.left - toElement.scrollLeft,
|
||||
y: pt.y + rcFrom.top + fromElement.scrollTop - rcTo.top - toElement.scrollTop
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
makeTransform(targetElement: HTMLElement, clientX: number, clientY: number): string {
|
||||
|
||||
// the bounds of the translated element must COMPLETELY fit within the scroll range of the container.
|
||||
let originalBounds = this.animationData[this.startIndex].originalPosition;
|
||||
|
||||
let newLocation = new DOMRect(
|
||||
originalBounds.x + clientX - this.startX,
|
||||
originalBounds.y + clientY - this.startY,
|
||||
originalBounds.width,
|
||||
originalBounds.height);
|
||||
|
||||
|
||||
if (newLocation.x < 0) {
|
||||
newLocation.x = 0;
|
||||
} else if (newLocation.right >= this.gridBounds.width) {
|
||||
newLocation.x -= newLocation.right - this.gridBounds.width;
|
||||
}
|
||||
|
||||
// chrome: inconsistent scroll height calculations.
|
||||
let maxY = this.gridBounds.height;
|
||||
if (newLocation.top < 0) {
|
||||
newLocation.y = 0;
|
||||
} else if (newLocation.bottom > maxY) {
|
||||
newLocation.y -= newLocation.bottom - maxY;
|
||||
}
|
||||
|
||||
return "translate(" + (newLocation.x - originalBounds.x) + "px,"
|
||||
+ (newLocation.y - originalBounds.y) + "px) scale(" + SELECT_SCALE + ")";
|
||||
}
|
||||
|
||||
createOriginalBounds(): DOMRect {
|
||||
// bounds of the target element, in ScrollContainer coordinates.
|
||||
let rcTarget = nullCast(this.dragTarget).getBoundingClientRect();
|
||||
|
||||
let rcScroll = nullCast(this.getScrollContainer()).getBoundingClientRect();
|
||||
rcTarget.x -= rcScroll.x;
|
||||
rcTarget.y -= rcScroll.y;
|
||||
return rcTarget;
|
||||
}
|
||||
|
||||
startIndex: number = 0;
|
||||
currentIndex: number = 0;
|
||||
|
||||
|
||||
reflowChildren(): void {
|
||||
|
||||
let y = 0;
|
||||
for (let i = 0; i < this.animationData.length; ++i) {
|
||||
if (i === this.startIndex) {
|
||||
if (i === this.currentIndex) {
|
||||
y += this.animationData[this.startIndex].originalPosition.height;
|
||||
}
|
||||
|
||||
} else {
|
||||
if (i === this.currentIndex && this.currentIndex < this.startIndex)
|
||||
{
|
||||
y += this.animationData[this.startIndex].originalPosition.height;
|
||||
}
|
||||
let animationItem = this.animationData[i];
|
||||
animationItem.setPosition(y);
|
||||
y += animationItem.originalPosition.height;
|
||||
if (i === this.currentIndex && this.currentIndex > this.startIndex)
|
||||
{
|
||||
y += this.animationData[this.startIndex].originalPosition.height;
|
||||
}
|
||||
}
|
||||
}
|
||||
this.stopAnimation();
|
||||
this.animationStartTime = new Date().getTime();
|
||||
this.animationTick();
|
||||
}
|
||||
|
||||
itemOffsetX: number = 0;
|
||||
itemOffsetY: number = 0;
|
||||
longPressTimer?: NodeJS.Timeout = undefined;
|
||||
|
||||
|
||||
longPressTimerTick() {
|
||||
if (navigator.vibrate)
|
||||
{
|
||||
navigator.vibrate([5]);
|
||||
}
|
||||
if (!this.dragStarted)
|
||||
{
|
||||
this.startDrag();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
handlePointerDown(e: PointerEvent<HTMLDivElement>): void {
|
||||
|
||||
if (this.props.canDrag !== undefined && !this.props.canDrag) return;
|
||||
|
||||
// any new pointer down cancels a drag in progress.
|
||||
this.cancelDrag();
|
||||
|
||||
// avoid 2nd click of a double click (which is difficult to cancel)
|
||||
let timeMs = new Date().getTime();
|
||||
if (timeMs - this.lastPointerDown < 300) {
|
||||
this.lastPointerDown = timeMs;
|
||||
return;
|
||||
}
|
||||
this.lastPointerDown = timeMs;
|
||||
|
||||
|
||||
let gridElement = this.findGridItem(e.target);
|
||||
if (gridElement === null) return;
|
||||
|
||||
|
||||
|
||||
if (!this.mouseDown && this.isValidPointer(e)) {
|
||||
|
||||
this.dragTarget = gridElement;
|
||||
|
||||
|
||||
document.body.addEventListener("pointerdown", this.handlePointerDownCapture, true);
|
||||
this.captureElement = e.currentTarget;
|
||||
|
||||
this.elementKeys = this.getElementKeys();
|
||||
this.longPressTimer = setTimeout(this.longPressTimerTick,LONG_PRESS_TIME_MS);
|
||||
this.mouseDown = true;
|
||||
|
||||
///window.addEventListener("touchstart",this.handleTouchStart, {passive: false});
|
||||
|
||||
this.dragStarted = false;
|
||||
this.startX = e.clientX;
|
||||
this.startY = e.clientY;
|
||||
this.startClientX = e.clientX;
|
||||
this.startClientY = e.clientY;
|
||||
|
||||
this.pointerId = e.pointerId;
|
||||
this.pointerType = e.pointerType;
|
||||
|
||||
this.savedIndex = gridElement.style.zIndex;
|
||||
gridElement.style.zIndex = "3";
|
||||
this.savedBackground = gridElement.style.background;
|
||||
gridElement.style.background = "#EEE";
|
||||
gridElement.style.opacity = "0.8";
|
||||
}
|
||||
}
|
||||
|
||||
originalOverscrollBehaviourY?: string;
|
||||
|
||||
gridBounds: DOMRect = new DOMRect();
|
||||
|
||||
startDrag() {
|
||||
this.dragStarted = true;
|
||||
let grid = this.refGrid.current!;
|
||||
this.gridBounds = grid.getBoundingClientRect();
|
||||
|
||||
window.addEventListener("touchmove",this.handleTouchMove, {passive: false});
|
||||
window.addEventListener("touchend",this.handleTouchEnd, {passive: false});
|
||||
|
||||
this.prepareChildren();
|
||||
|
||||
this.startIndex = this.getIndexFromPosition(this.startX, this.startY);
|
||||
if (this.startIndex === -1)
|
||||
{
|
||||
this.cancelDrag();
|
||||
return;
|
||||
}
|
||||
let t = grid.childNodes[this.startIndex];
|
||||
if (t !== this.dragTarget) {
|
||||
console.count("dragTarget mismatch!");
|
||||
this.startIndex = this.getIndexFromPosition(this.startX,this.startY);
|
||||
this.dragTarget = grid.childNodes[this.startIndex] as HTMLDivElement;
|
||||
}
|
||||
this.currentIndex = this.startIndex;
|
||||
if (this.props.onDragStart) {
|
||||
this.props.onDragStart(this.startIndex, this.startX, this.startY);
|
||||
}
|
||||
this.dragTarget!.setPointerCapture(this.pointerId);
|
||||
|
||||
let rcItem = this.dragTarget!.getBoundingClientRect();
|
||||
this.ptItemStart = new DOMPoint(this.startX - rcItem.x, this.startY - rcItem.y);
|
||||
|
||||
this.originalOverscrollBehaviourY = this.getScrollContainer()!.style.getPropertyValue("overscroll-behaviour-y");
|
||||
this.getScrollContainer()!.style.setProperty("overscroll-behaviour-y","none");
|
||||
|
||||
if (this.pointerType === "touch")
|
||||
{
|
||||
this.startY -= 4; // causes the selected item to "pop"
|
||||
}
|
||||
}
|
||||
|
||||
handlePointerMove(e: PointerEvent<HTMLDivElement>): void {
|
||||
if (this.isCapturedPointer(e)) {
|
||||
if (!this.dragStarted && this.dragThresholdExceeded(e)) {
|
||||
this.startDrag();
|
||||
}
|
||||
if (this.dragStarted) {
|
||||
this.lastX = e.clientX;
|
||||
this.lastY = e.clientY;
|
||||
if (this.dragTarget !== null) {
|
||||
this.dragTarget.style.transform = this.makeTransform(this.dragTarget, e.clientX, e.clientY);
|
||||
|
||||
let index = this.getDragIndexFromPosition(e.clientX, e.clientY);
|
||||
if (index !== this.currentIndex) {
|
||||
this.currentIndex = index;
|
||||
this.reflowChildren();
|
||||
}
|
||||
}
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
|
||||
if (this.props.onDragMove) {
|
||||
this.props.onDragMove(this.currentIndex, e.clientX, e.clientY);
|
||||
}
|
||||
|
||||
this.checkForAutoScroll(e.currentTarget);
|
||||
}
|
||||
}
|
||||
}
|
||||
autoScrollTimer?: NodeJS.Timeout;
|
||||
|
||||
|
||||
getElementKeys(): any[] {
|
||||
let grid = nullCast(this.refGrid.current);
|
||||
let children = grid.childNodes;
|
||||
let result: any[] = [];
|
||||
for (let i = 0; i < children.length; ++i)
|
||||
{
|
||||
result[i] = (children[i] as any).key;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
assertKeysNotChanged(oldKeys: any[])
|
||||
{
|
||||
let newKeys = this.getElementKeys();
|
||||
for (let i = 0; i < newKeys.length; ++i)
|
||||
{
|
||||
if (newKeys[i] !== oldKeys[i])
|
||||
{
|
||||
throw new Error("Keys have changed.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
handleAutoScrollTick(currentTarget: HTMLElement, dx: number, dy: number) {
|
||||
if (!this.mouseDown) return;
|
||||
let scrollContainer = this.getScrollContainer();
|
||||
if (scrollContainer) {
|
||||
let tx = scrollContainer.scrollLeft;
|
||||
let ty = scrollContainer.scrollTop;
|
||||
|
||||
let x = scrollContainer.scrollLeft + dx;
|
||||
let y = scrollContainer.scrollTop + dy;
|
||||
x = Math.min(x,scrollContainer.scrollWidth-scrollContainer.clientWidth);
|
||||
y = Math.min(y,scrollContainer.scrollHeight-scrollContainer.clientHeight);
|
||||
if (x < 0) x = 0;
|
||||
if (y < 0) y = 0;
|
||||
|
||||
scrollContainer.scrollTo(x, y);
|
||||
this.startX -= (scrollContainer.scrollLeft - tx);
|
||||
this.startY -= (scrollContainer.scrollTop - ty);
|
||||
|
||||
if (this.dragTarget) {
|
||||
this.dragTarget.style.transform = this.makeTransform(this.dragTarget, this.lastX, this.lastY);
|
||||
}
|
||||
|
||||
this.checkForAutoScroll(currentTarget);
|
||||
}
|
||||
}
|
||||
stopAutoScroll() {
|
||||
if (this.autoScrollTimer !== undefined) {
|
||||
clearTimeout(this.autoScrollTimer);
|
||||
this.autoScrollTimer = undefined;
|
||||
}
|
||||
}
|
||||
startAutoScroll(scrollContainer: HTMLElement, dx: number, dy: number) {
|
||||
this.stopAutoScroll();
|
||||
|
||||
this.autoScrollTimer = setTimeout(
|
||||
() => { this.handleAutoScrollTick(scrollContainer, dx, dy) }, AUTOSCROLL_TICK_DELAY);
|
||||
}
|
||||
|
||||
checkForAutoScroll(currentTarget: HTMLElement) {
|
||||
let scrollContainer = this.getScrollContainer();
|
||||
if (!scrollContainer) {
|
||||
this.stopAutoScroll();
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
let scrollContainerRect = scrollContainer.getBoundingClientRect();
|
||||
|
||||
let dy: number = 0;
|
||||
let dx: number = 0;
|
||||
|
||||
if (this.lastX < scrollContainerRect.x + AUTOSCROLL_THRESHOLD) {
|
||||
dx = -AUTOSCROLL_SCROLL_RATE;
|
||||
if (dx < -scrollContainer.scrollLeft) {
|
||||
dx = -scrollContainer.scrollLeft;
|
||||
}
|
||||
} else if (this.lastX >= scrollContainerRect.right - AUTOSCROLL_THRESHOLD) {
|
||||
dx = AUTOSCROLL_SCROLL_RATE;
|
||||
let maxScroll = Math.max(scrollContainer.scrollWidth - scrollContainer.clientWidth, 0);
|
||||
if (dx > maxScroll) dx = maxScroll;
|
||||
}
|
||||
if (this.lastY < scrollContainerRect.top) {
|
||||
dy = -AUTOSCROLL_SCROLL_RATE;
|
||||
if (dy < -scrollContainer.scrollTop) dy = -scrollContainer.scrollTop;
|
||||
} else if (this.lastY >= scrollContainerRect.bottom - AUTOSCROLL_THRESHOLD) {
|
||||
dy = AUTOSCROLL_SCROLL_RATE;
|
||||
let maxScroll = Math.max(scrollContainer.scrollHeight - scrollContainer.clientHeight);
|
||||
if (dy > maxScroll) dy = maxScroll;
|
||||
}
|
||||
|
||||
if (dx === 0 && dy === 0) {
|
||||
this.stopAutoScroll();
|
||||
return;
|
||||
} else {
|
||||
this.startAutoScroll(currentTarget, dx, dy);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
getScrollContainer(): HTMLDivElement | null {
|
||||
return this.refScrollContainer.current;
|
||||
}
|
||||
|
||||
overflowX(): Property.OverflowX {
|
||||
return this.props.scroll === ScrollDirection.X ? "auto" : "hidden";
|
||||
}
|
||||
overflowY(): Property.OverflowX {
|
||||
return this.props.scroll === ScrollDirection.Y ? "auto" : "hidden";
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
transform: "",
|
||||
overflowX: this.overflowX(),
|
||||
overflowY: this.overflowY(),
|
||||
width: this.props.fullWidth??true? "100%": "auto",
|
||||
height: this.props.fullHeight??true? "100%": "auto",
|
||||
}}
|
||||
onPointerDown={this.handlePointerDown}
|
||||
onPointerMove={this.handlePointerMove}
|
||||
onPointerCancel={this.handlePointerCancel}
|
||||
onPointerCancelCapture={this.handlePointerCancel}
|
||||
onPointerUp={this.handlePointerUp}
|
||||
onDragStart={(e) => { e.preventDefault(); e.stopPropagation(); }}
|
||||
onClick={this.onClick}
|
||||
ref={this.refScrollContainer}
|
||||
>
|
||||
<div style={{ display: "flex", flexDirection: "column", flexWrap: "nowrap", position: "relative" }}
|
||||
ref={this.refGrid}
|
||||
>
|
||||
{this.props.children}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
);
|
||||
|
||||
export default DraggableGrid;
|
||||
@@ -0,0 +1,212 @@
|
||||
import React, { ReactNode } from 'react';
|
||||
import { createStyles, withStyles, WithStyles, Theme } from '@material-ui/core/styles';
|
||||
import { PiPedalModel, PiPedalModelFactory } from './PiPedalModel';
|
||||
import ResizeResponsiveComponent from './ResizeResponsiveComponent';
|
||||
import { UiControl } from './Lv2Plugin';
|
||||
import Dialog from '@material-ui/core/Dialog';
|
||||
import Input from '@material-ui/core/Input';
|
||||
import { nullCast } from './Utility';
|
||||
import Typography from '@material-ui/core/Typography';
|
||||
|
||||
|
||||
const styles = (theme: Theme) => createStyles({
|
||||
frame: {
|
||||
display: "block",
|
||||
position: "relative",
|
||||
flexDirection: "row",
|
||||
flexWrap: "nowrap",
|
||||
paddingTop: "8px",
|
||||
paddingBottom: "0px",
|
||||
height: "100%",
|
||||
overflowX: "auto",
|
||||
overflowY: "auto"
|
||||
},
|
||||
|
||||
});
|
||||
|
||||
interface FullScreenIMEProps extends WithStyles<typeof styles> {
|
||||
theme: Theme;
|
||||
uiControl?: UiControl;
|
||||
caption: string;
|
||||
value: number;
|
||||
onChange: (key: string, value: number) => void;
|
||||
onClose: () => void;
|
||||
initialHeight: number;
|
||||
};
|
||||
type FullScreenIMEState = {
|
||||
error: boolean;
|
||||
};
|
||||
|
||||
const FullScreenIME =
|
||||
withStyles(styles, { withTheme: true })(
|
||||
class extends ResizeResponsiveComponent<FullScreenIMEProps, FullScreenIMEState>
|
||||
{
|
||||
model: PiPedalModel;
|
||||
inputRef: React.RefObject<HTMLInputElement>;
|
||||
|
||||
constructor(props: FullScreenIMEProps) {
|
||||
super(props);
|
||||
this.model = PiPedalModelFactory.getInstance();
|
||||
this.inputRef = React.createRef();
|
||||
|
||||
this.state = {
|
||||
error: false
|
||||
|
||||
}
|
||||
this.onValueChanged = this.onValueChanged.bind(this);
|
||||
this.handleClose = this.handleClose.bind(this);
|
||||
this.onInputChange = this.onInputChange.bind(this);
|
||||
this.onInputKeyPress = this.onInputKeyPress.bind(this);
|
||||
this.onInputLostFocus = this.onInputLostFocus.bind(this);
|
||||
}
|
||||
|
||||
onValueChanged(key: string, value: number): void {
|
||||
this.props.onChange(key, value);
|
||||
}
|
||||
|
||||
|
||||
onWindowSizeChanged(width: number, height: number): void {
|
||||
|
||||
if (this.props.uiControl) {
|
||||
// detecting a keyboard IME is painfully difficult.
|
||||
// we do it by comparing the current window height against the window height
|
||||
// when the IME was requested.
|
||||
|
||||
// a width change indicates a screen flip. Cancel the fullscreen ime in that case
|
||||
// because we can't determine the initial window heigth anymore.
|
||||
if (this.currentWidth === undefined)
|
||||
{
|
||||
this.currentWidth = window.innerWidth;
|
||||
}
|
||||
if (this.currentWidth !== window.innerWidth)
|
||||
{
|
||||
this.validateInput(true);
|
||||
}
|
||||
// eslint-disable-next-line no-restricted-globals
|
||||
if (window.innerHeight >= this.props.initialHeight) {
|
||||
this.validateInput(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
handleClose(e: {}, reason: string): void {
|
||||
this.props.onClose();
|
||||
}
|
||||
onInputLostFocus() {
|
||||
this.validateInput(true);
|
||||
|
||||
}
|
||||
|
||||
inputChanged: boolean = false;
|
||||
onInputChange() {
|
||||
|
||||
this.inputChanged = true;
|
||||
this.validateInput(false);
|
||||
}
|
||||
|
||||
currentValue: number = 0;
|
||||
|
||||
validateInput(commitValue: boolean) {
|
||||
if (!this.inputRef.current) return;
|
||||
|
||||
let text = this.inputRef.current.value;
|
||||
let valid = false;
|
||||
let result: number = this.currentValue;
|
||||
try {
|
||||
if (text.length === 0) {
|
||||
valid = false;
|
||||
} else {
|
||||
let v = Number(text);
|
||||
if (isNaN(v)) {
|
||||
valid = false;
|
||||
} else {
|
||||
valid = true;
|
||||
result = v;
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
valid = false;
|
||||
}
|
||||
|
||||
if (commitValue) {
|
||||
this.setState({ error: false });
|
||||
if (!valid) {
|
||||
result = this.currentValue; // reset the value!
|
||||
}
|
||||
// clamp and quantize.
|
||||
let uiControl = nullCast(this.uiControl);
|
||||
|
||||
// quantize value.
|
||||
result = uiControl.clampValue(result);
|
||||
|
||||
this.props.onChange(this.props.uiControl!.symbol, result);
|
||||
}
|
||||
}
|
||||
|
||||
onInputKeyPress(e: any): void {
|
||||
if (e.charCode === 13) {
|
||||
if (this.inputChanged) {
|
||||
this.inputChanged = false;
|
||||
this.validateInput(true);
|
||||
}
|
||||
else {
|
||||
this.props.onClose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uiControl?: UiControl;
|
||||
|
||||
currentWidth?: number;
|
||||
|
||||
render(): ReactNode {
|
||||
//let classes = this.props.classes;
|
||||
let control = this.props.uiControl;
|
||||
if (!control) {
|
||||
this.currentWidth = undefined;
|
||||
return (<div/>);
|
||||
}
|
||||
this.currentWidth = window.innerWidth;
|
||||
|
||||
this.uiControl = control;
|
||||
|
||||
let value = this.props.value;
|
||||
|
||||
return (
|
||||
<Dialog fullScreen open={!!(this.props.uiControl)} onClose={(e, r) => this.handleClose(e, r)} >
|
||||
<div style={{
|
||||
width: "100%", height: "100%", position: "relative",
|
||||
display: "flex", flexDirection: "column", flexWrap: "nowrap",
|
||||
justifyContent: "center", alignItems: "center"
|
||||
}}
|
||||
>
|
||||
<Typography variant="body2">{this.props.caption}</Typography>
|
||||
<Input key={value}
|
||||
type="number"
|
||||
defaultValue={control.formatValue(value)}
|
||||
error={this.state.error}
|
||||
autoFocus
|
||||
inputProps={{
|
||||
'aria-label':
|
||||
control.symbol + " value",
|
||||
style: { textAlign: "center" },
|
||||
width: 80
|
||||
}}
|
||||
inputRef={this.inputRef} onChange={this.onInputChange}
|
||||
onBlur={this.onInputLostFocus}
|
||||
|
||||
onKeyPress={this.onInputKeyPress} />
|
||||
|
||||
</div>
|
||||
|
||||
</Dialog>
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
);
|
||||
|
||||
export default FullScreenIME;
|
||||
|
||||
@@ -0,0 +1,338 @@
|
||||
import React, {Component} from 'react';
|
||||
import { createStyles, Theme, withStyles, WithStyles } from '@material-ui/core/styles';
|
||||
import { MonitorPortHandle, PiPedalModel, PiPedalModelFactory } from "./PiPedalModel";
|
||||
import SvgPathBuilder from './SvgPathBuilder'
|
||||
|
||||
//const char* model[] = {"12-TET","19-TET","24-TET", "31-TET", "53-TET"};
|
||||
// set_adjustment(ui->widget[2]->adj,440.0, 440.0, 427.0, 453.0, 0.1, CL_CONTINUOS);
|
||||
|
||||
const FREQUENCY_PORT_NAME = "FREQ";
|
||||
|
||||
const DIAL_WIDTH= 220;
|
||||
const DIAL_HEIGHT = 100;
|
||||
const NEEDLE_CY = 320;
|
||||
const CENTS_PER_MARK = 5;
|
||||
const NEEDLE_OUTER_RADIUS = 0.98;
|
||||
const TICK_OUTER_RADIUS = 0.96;
|
||||
const TICK_INNER_RADIUS = 0.85;
|
||||
const TICK_INNER_ZERO_RADIUS = 0.82;
|
||||
const DIAL_ANGLE_RADIANS = 20*Math.PI/180;
|
||||
|
||||
|
||||
const styles = (theme: Theme) =>
|
||||
createStyles({
|
||||
icon: {
|
||||
width: "24px",
|
||||
height: "24px",
|
||||
margin: "0px",
|
||||
opacity: "0.6"
|
||||
}
|
||||
});
|
||||
|
||||
interface PitchInfo {
|
||||
valid: boolean;
|
||||
name: string;
|
||||
fractionText: string;
|
||||
fraction: number;
|
||||
|
||||
semitoneCents: number
|
||||
};
|
||||
|
||||
const FLAT = "\u{266d}";
|
||||
const SHARP = "#";
|
||||
const DOUBLE_FLAT = "\uD834\uDD2B";
|
||||
const DOUBLE_SHARP = "\uD834\uDD2A";
|
||||
const HALF_FLAT = "\uD834\uDD33";
|
||||
const HALF_SHARP = "\uD834\uDD32";
|
||||
|
||||
|
||||
const NOTES_12TET: string[] = [
|
||||
"C", "C" + SHARP, "D", "E"+FLAT,"E", "F", "F" + SHARP, "G", "A"+FLAT,"A","B"+FLAT,"B"
|
||||
];
|
||||
const NOTES_19TET: string[] = [
|
||||
"C", "C" + SHARP, "D"+FLAT, "D", "D" + SHARP,"E"+FLAT,"E", "E" + SHARP, "F", "F" + SHARP, "G"+FLAT,"G","G" + SHARP,"A"+FLAT,"A","A" + SHARP,"B"+FLAT,"B","C"+FLAT
|
||||
];
|
||||
const NOTES_24TET: string[] = [
|
||||
"C","C"+HALF_SHARP, "C" + SHARP,"D"+HALF_FLAT, "D", "D"+HALF_SHARP, "E"+FLAT, "E"+HALF_FLAT, "E","E"+HALF_SHARP,"F", "F"+HALF_SHARP, "F" + SHARP, "G"+HALF_FLAT,"G","G"+HALF_SHARP, "A"+FLAT,"A"+HALF_FLAT,"A","A"+HALF_SHARP,"B"+FLAT,"B"+HALF_FLAT,"B","C"+HALF_FLAT
|
||||
];
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
const NOTES_31TET: string[] = [
|
||||
"C", "D"+DOUBLE_FLAT, "C" + SHARP,"Db","C"+DOUBLE_SHARP,"D","E"+DOUBLE_FLAT,"D" + SHARP,"Eb","D"+DOUBLE_SHARP,"E",'Fb","E" + SHARP,"F","G"+DOUBLE_FLAT,"F" + SHARP,"Gb""Fx","G","A"+DOUBLE_FLAT,"G"+DOUBLE_SHARP,"A"'
|
||||
];
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
const NOTES_53TET = ["la","laa","lo","law","ta","teh","te","tu","tuh","ti","tih","to","taw","da","do","di","daw","ro","rih","ra","ru","ruh","reh","re ","ri","raw","ma","meh","me","mu","muh","mi","maa","mo","maw","fe","fa","fih","fu","fuh","fi","se","suh","su","sih","sol","si","saw","lo","leh","le","lu","luh"];
|
||||
|
||||
interface GxTunerControlProps extends WithStyles<typeof styles> {
|
||||
theme: Theme;
|
||||
instanceId: number;
|
||||
};
|
||||
type GxTunerControlState = {
|
||||
pitchInfo: PitchInfo
|
||||
tet: number;
|
||||
refFrequency: number;
|
||||
};
|
||||
|
||||
const GxTunerControl =
|
||||
withStyles(styles, { withTheme: true })(
|
||||
class extends Component<GxTunerControlProps, GxTunerControlState>
|
||||
{
|
||||
model: PiPedalModel;
|
||||
|
||||
|
||||
refRoot: React.RefObject<HTMLDivElement>;
|
||||
|
||||
constructor(props: GxTunerControlProps) {
|
||||
super(props);
|
||||
this.model = PiPedalModelFactory.getInstance();
|
||||
|
||||
this.refRoot = React.createRef();
|
||||
|
||||
this.state = {
|
||||
pitchInfo: {
|
||||
valid: false,
|
||||
name: "",
|
||||
fractionText: "",
|
||||
fraction: 0,
|
||||
semitoneCents: 100},
|
||||
tet: 12,
|
||||
refFrequency: 440
|
||||
|
||||
};
|
||||
this.onFrequencyUpdated = this.onFrequencyUpdated.bind(this);
|
||||
}
|
||||
|
||||
|
||||
noteToPitchInfo(hz: number) : PitchInfo
|
||||
{
|
||||
if (hz < 65) // ground hum. Ignore it.
|
||||
{
|
||||
hz = 0;
|
||||
}
|
||||
let tet = this.state.tet;
|
||||
let refFrequency = this.state.refFrequency;
|
||||
let aOffset: number;
|
||||
let names: string[];
|
||||
let semitoneCents = 100;
|
||||
let valid = false;
|
||||
|
||||
|
||||
switch (tet)
|
||||
{
|
||||
case 12:
|
||||
default:
|
||||
aOffset = 69;
|
||||
names = NOTES_12TET;
|
||||
semitoneCents = 100;
|
||||
break;
|
||||
case 19:
|
||||
aOffset = 4*19+13;
|
||||
names = NOTES_19TET;
|
||||
semitoneCents = 100*12.0/19;
|
||||
break;
|
||||
case 24:
|
||||
aOffset = 4*24+18;
|
||||
semitoneCents = 50;
|
||||
names = NOTES_24TET;
|
||||
break;
|
||||
}
|
||||
let name = "";
|
||||
let fractionText = "";
|
||||
let fraction = 0;
|
||||
if (hz !== 0)
|
||||
{
|
||||
|
||||
let note = Math.log2(hz/refFrequency)*tet + aOffset;
|
||||
let noteNumber = Math.round(note);
|
||||
|
||||
let octave = Math.floor((noteNumber)/tet);
|
||||
|
||||
let nameIndex = noteNumber -octave* tet;
|
||||
if (nameIndex < 0) nameIndex += tet;
|
||||
name = names[ nameIndex ] + (octave-2);
|
||||
valid = true;
|
||||
fraction = note-noteNumber;
|
||||
if (fraction >= 0) {
|
||||
fractionText = "+" + fraction.toFixed(2).substr(1);
|
||||
} else {
|
||||
fractionText = "\u2212" + fraction.toFixed(2).substr(2);
|
||||
|
||||
}
|
||||
}
|
||||
return {
|
||||
valid: valid,
|
||||
name: name,
|
||||
fraction: fraction,
|
||||
fractionText: fractionText,
|
||||
semitoneCents: semitoneCents
|
||||
};
|
||||
}
|
||||
|
||||
lastValue: number = -1;
|
||||
onFrequencyUpdated(value: number): void
|
||||
{
|
||||
// suppress repeated zeros.
|
||||
if (value === this.lastValue && value === 0 && this.animationIdle) return;
|
||||
|
||||
this.lastValue = value;
|
||||
|
||||
let pitchInfo = this.noteToPitchInfo(value);
|
||||
this.setState({
|
||||
pitchInfo: pitchInfo
|
||||
});
|
||||
}
|
||||
|
||||
subscribedInstanceId: number = -1;
|
||||
|
||||
monitorHandle?: MonitorPortHandle;
|
||||
|
||||
addSubscription() {
|
||||
this.subscribedInstanceId = this.props.instanceId;
|
||||
this.monitorHandle = this.model.monitorPort(this.props.instanceId,FREQUENCY_PORT_NAME,1.0/30,this.onFrequencyUpdated);
|
||||
}
|
||||
removeSubscription() {
|
||||
this.subscribedInstanceId = -1;
|
||||
if (this.monitorHandle) {
|
||||
this.model.unmonitorPort(this.monitorHandle);
|
||||
this.monitorHandle = undefined;
|
||||
}
|
||||
}
|
||||
updateSubscription() {
|
||||
if (this.subscribedInstanceId !== this.props.instanceId)
|
||||
{
|
||||
this.removeSubscription();
|
||||
this.addSubscription();
|
||||
|
||||
}
|
||||
}
|
||||
componentDidMount()
|
||||
{
|
||||
this.addSubscription();
|
||||
}
|
||||
componentDidUpdate() {
|
||||
this.updateSubscription();
|
||||
}
|
||||
componentWillUnmount()
|
||||
{
|
||||
this.removeSubscription();
|
||||
}
|
||||
|
||||
|
||||
|
||||
makeDialTick(pitchInfo: PitchInfo, cents: number): React.ReactNode {
|
||||
let isZeroTick = Math.abs(cents) < 0.001;
|
||||
let r0 = TICK_OUTER_RADIUS;
|
||||
let r1 = (isZeroTick? TICK_INNER_ZERO_RADIUS: TICK_INNER_RADIUS);
|
||||
let width = isZeroTick? 3: 2;
|
||||
return this.makeTick(pitchInfo,cents,r0,r1,"#666", width);
|
||||
}
|
||||
|
||||
makeTick(pitchInfo: PitchInfo, cents: number, r0: number, r1: number, stroke: string,width: number): React.ReactNode
|
||||
{
|
||||
let range = pitchInfo.semitoneCents;
|
||||
if (range > 50) {
|
||||
range = 50;
|
||||
}
|
||||
r0 *= NEEDLE_CY;
|
||||
r1 *= NEEDLE_CY;
|
||||
let angle = DIAL_ANGLE_RADIANS * (cents*2/(range));
|
||||
|
||||
let sin_ = Math.sin(angle);
|
||||
let cos_ = -Math.cos(angle);
|
||||
let cx = DIAL_WIDTH/2;
|
||||
let cy = NEEDLE_CY;
|
||||
let path = new SvgPathBuilder().moveTo(r0*sin_+cx,r0*cos_+cy).lineTo(r1*sin_+cx,r1*cos_+cy).toString();
|
||||
|
||||
return (<path d={path} stroke={stroke} strokeWidth={width+""} />);
|
||||
}
|
||||
|
||||
lastValidTime: number = 0;
|
||||
lastValidCents: number = -100;
|
||||
animationIdle: boolean = true;
|
||||
|
||||
makeNeedle(pitchInfo: PitchInfo)
|
||||
{
|
||||
let maxCents = pitchInfo.semitoneCents/2;
|
||||
if (maxCents > 25) {
|
||||
maxCents = 25;
|
||||
}
|
||||
let cents: number;
|
||||
if (!pitchInfo.valid)
|
||||
{
|
||||
const NEEDLE_DECAY_RATE_PER_MS = -25/100;
|
||||
|
||||
// animate to zero position.
|
||||
let time = new Date().getTime();
|
||||
let dt = time-this.lastValidTime;
|
||||
|
||||
cents = this.lastValidCents+(dt*NEEDLE_DECAY_RATE_PER_MS);
|
||||
if (cents <= -maxCents)
|
||||
{
|
||||
cents = -maxCents;
|
||||
this.animationIdle = true;
|
||||
} else {
|
||||
this.animationIdle = false;
|
||||
}
|
||||
} else {
|
||||
cents = pitchInfo.semitoneCents*pitchInfo.fraction;
|
||||
if (cents > maxCents) {
|
||||
cents = maxCents;
|
||||
}
|
||||
if (cents < -maxCents)
|
||||
{
|
||||
cents = -maxCents;
|
||||
}
|
||||
this.lastValidCents = cents;
|
||||
this.lastValidTime = new Date().getTime();
|
||||
}
|
||||
return this.makeTick(pitchInfo,cents,NEEDLE_OUTER_RADIUS,0.1,"#800",3);
|
||||
}
|
||||
|
||||
renderDial(pitchInfo: PitchInfo)
|
||||
{
|
||||
|
||||
let bounds = "0,0," + DIAL_WIDTH+ "," +DIAL_HEIGHT;
|
||||
|
||||
let content: React.ReactNode[] = [];
|
||||
|
||||
let maxCents = Math.floor(pitchInfo.semitoneCents/2 / CENTS_PER_MARK)*CENTS_PER_MARK;
|
||||
|
||||
for (let cents = -maxCents; cents <= maxCents+ 0.01; cents += CENTS_PER_MARK) {
|
||||
content.push(this.makeDialTick(pitchInfo,cents));
|
||||
}
|
||||
|
||||
content.push(
|
||||
this.makeNeedle(pitchInfo)
|
||||
);
|
||||
|
||||
|
||||
return (
|
||||
<svg viewBox={bounds} width={DIAL_WIDTH} height={DIAL_HEIGHT} style={{position: "absolute"}} >
|
||||
{ content }
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
render() {
|
||||
|
||||
return (<div ref={this.refRoot} style={{width: DIAL_WIDTH, height: DIAL_HEIGHT, fontSize: "2em", fontWeight: 700, position: "relative",
|
||||
boxShadow: "1px 5px 6px #888 inset",
|
||||
fontFamily: "arial,roboto,helvetica,sans"}}>
|
||||
<div style={{position: "absolute", left: 0, bottom: 5, width: "50%",textAlign: "right"}}>
|
||||
<span style={{ marginRight: 20, color: "#444", textAlign: "right" }}>{this.state.pitchInfo.name}</span>
|
||||
</div>
|
||||
<div style={{position: "absolute", right: 0, bottom: 5, width: "50%",textAlign: "left"}}>
|
||||
<span style={{ marginLeft: 20, color: "#444", textAlign: "left" }}>{this.state.pitchInfo.fractionText}</span>
|
||||
</div>
|
||||
|
||||
{ this.renderDial(this.state.pitchInfo) }
|
||||
</div>);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
|
||||
|
||||
export default GxTunerControl;
|
||||
@@ -0,0 +1,13 @@
|
||||
import React from 'react';
|
||||
import { PiPedalModel } from "./PiPedalModel";
|
||||
import { PedalBoardItem } from './PedalBoard';
|
||||
|
||||
|
||||
|
||||
interface IControlViewFactory {
|
||||
uri: string;
|
||||
Create(model: PiPedalModel,pedalBoardItem: PedalBoardItem): React.ReactNode;
|
||||
|
||||
}
|
||||
|
||||
export default IControlViewFactory;
|
||||
@@ -0,0 +1,100 @@
|
||||
import {PiPedalArgumentError} from './PiPedalError';
|
||||
|
||||
export class JackChannelSelection {
|
||||
deserialize(input: any): JackChannelSelection
|
||||
{
|
||||
this.inputAudioPorts = input.inputAudioPorts.slice();
|
||||
this.outputAudioPorts = input.outputAudioPorts.slice();
|
||||
this.inputMidiPorts = input.inputMidiPorts.slice();
|
||||
this.sampleRate = input.sampleRate;
|
||||
this.bufferSize = input.bufferSize;
|
||||
this.numberOfBuffers = input.numberOfBuffers;
|
||||
return this;
|
||||
}
|
||||
clone() : JackChannelSelection {
|
||||
return new JackChannelSelection().deserialize(this);
|
||||
}
|
||||
inputAudioPorts: string[] = [];
|
||||
outputAudioPorts: string[] = [];
|
||||
inputMidiPorts: string[] = [];
|
||||
|
||||
sampleRate: number = 48000;
|
||||
bufferSize: number = 64;
|
||||
numberOfBuffers: number = 3;
|
||||
|
||||
static makeDefault(jackConfiguration: JackConfiguration): JackChannelSelection {
|
||||
let result = new JackChannelSelection();
|
||||
result.inputAudioPorts = jackConfiguration.inputAudioPorts.slice(0,2);
|
||||
result.outputAudioPorts = jackConfiguration.inputAudioPorts.slice(0,2);
|
||||
return result;
|
||||
}
|
||||
getChannelDisplayValue(selectedChannels: string[], availableChannels: string[],isConfigValid: boolean): string
|
||||
{
|
||||
if (!isConfigValid)
|
||||
{
|
||||
if (selectedChannels.length === 2) {
|
||||
return "Stereo";
|
||||
}
|
||||
if (selectedChannels.length === 1) return "Mono";
|
||||
return "Invalid selection";
|
||||
|
||||
}
|
||||
if (selectedChannels.length === 0) return "Invalid selection";
|
||||
if (availableChannels.length === 1) return "Mono";
|
||||
if (availableChannels.length === 2) {
|
||||
if (selectedChannels.length === 2) return "Stereo";
|
||||
if (selectedChannels[0] === availableChannels[0]) {
|
||||
return "Mono (left channel only)";
|
||||
} else if (selectedChannels[0] === availableChannels[1]) {
|
||||
return "Mono (right channel only)";
|
||||
} else {
|
||||
throw new PiPedalArgumentError("Invalid channel selection."); // should be subset of jackConfiguration.
|
||||
}
|
||||
} else {
|
||||
if (selectedChannels.length === 2) return "Stereo (custom selection)";
|
||||
if (selectedChannels.length === 1) return "Mono (custom selection)";
|
||||
throw new PiPedalArgumentError("Invalid channel selection."); // should be subset of jackConfiguration.
|
||||
}
|
||||
}
|
||||
getAudioInputDisplayValue(jackConfiguration: JackConfiguration): string
|
||||
{
|
||||
return this.getChannelDisplayValue(this.inputAudioPorts,jackConfiguration.inputAudioPorts, jackConfiguration.isValid);
|
||||
}
|
||||
getAudioOutputDisplayValue(jackConfiguration: JackConfiguration): string
|
||||
{
|
||||
return this.getChannelDisplayValue(this.outputAudioPorts,jackConfiguration.outputAudioPorts, jackConfiguration.isValid);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
export class JackConfiguration {
|
||||
deserialize(input: any): JackConfiguration {
|
||||
this.isValid = input.isValid;
|
||||
this.isRestarting = input.isRestarting;
|
||||
this.errorState = input.errorState;
|
||||
this.sampleRate = input.sampleRate;
|
||||
this.blockLength = input.blockLength;
|
||||
this.midiBufferSize = input.midiBufferSize;
|
||||
this.maxAllowedMidiDelta = input.maxAllowedMidiDelta;
|
||||
this.inputAudioPorts = input.inputAudioPorts;
|
||||
this.outputAudioPorts = input.outputAudioPorts;
|
||||
this.inputMidiPorts = input.inputMidiPorts;
|
||||
this.outputMidiPorts = input.outputMidiPorts;
|
||||
return this;
|
||||
}
|
||||
isValid: boolean = false;
|
||||
isRestarting: boolean = false;
|
||||
errorState: string = "Not loaded.";
|
||||
sampleRate: number = 0;
|
||||
blockLength: number = 0;
|
||||
midiBufferSize: number = 0;
|
||||
maxAllowedMidiDelta: number = 0;
|
||||
|
||||
inputAudioPorts: string[] = [];
|
||||
outputAudioPorts: string[] = [];
|
||||
inputMidiPorts: string[] = [];
|
||||
outputMidiPorts: string[] = [];
|
||||
|
||||
};
|
||||
|
||||
export default JackConfiguration;
|
||||
@@ -0,0 +1,112 @@
|
||||
import React from 'react';
|
||||
import Typography from '@material-ui/core/Typography';
|
||||
|
||||
|
||||
const RED_COLOR = "#C00";
|
||||
const GREEN_COLOR = "#666";
|
||||
|
||||
|
||||
|
||||
function tempDisplay(mC: number): string
|
||||
{
|
||||
return (mC/1000).toFixed(1) + "\u00B0C"; // degrees C.
|
||||
}
|
||||
function cpuDisplay(cpu: number): string
|
||||
{
|
||||
return cpu.toFixed(1)+"%";
|
||||
}
|
||||
|
||||
export default class JackHostStatus {
|
||||
deserialize(input: any): JackHostStatus
|
||||
{
|
||||
this.active = input.active;
|
||||
this.restarting = input.restarting;
|
||||
this.underruns = input.underruns;
|
||||
this.cpuUsage = input.cpuUsage;
|
||||
this.msSinceLastUnderrun = input.msSinceLastUnderrun;
|
||||
this.temperaturemC = input.temperaturemC;
|
||||
return this;
|
||||
}
|
||||
hasTemperature() : boolean {
|
||||
return this.temperaturemC >= -100000;
|
||||
}
|
||||
active: boolean = false;
|
||||
restarting: boolean = false;
|
||||
underruns: number = 0;
|
||||
cpuUsage: number = 0;
|
||||
msSinceLastUnderrun: number = -5000*1000;
|
||||
temperaturemC: number = -1000000;
|
||||
|
||||
static getDisplayView(label: string,status?: JackHostStatus): React.ReactNode {
|
||||
if (!status) {
|
||||
return (<div style={{whiteSpace: "nowrap"}}>
|
||||
<Typography variant="caption" color="textSecondary">{label}</Typography>
|
||||
<Typography variant="caption"> </Typography>
|
||||
</div>);
|
||||
}
|
||||
if (status.restarting)
|
||||
{
|
||||
return (
|
||||
<div style={{whiteSpace: "nowrap"}}>
|
||||
<Typography variant="caption" color="textSecondary">{label}</Typography>
|
||||
<span style={{color: RED_COLOR}}>
|
||||
<Typography variant="caption" color="inherit">Restarting </Typography>
|
||||
</span>
|
||||
{
|
||||
status.temperaturemC > -100000 &&
|
||||
(
|
||||
<span style={{color: status.temperaturemC > 75000? RED_COLOR: GREEN_COLOR}}>
|
||||
<Typography variant="caption" color="inherit">{tempDisplay(status.temperaturemC)}</Typography>
|
||||
</span>
|
||||
)
|
||||
}
|
||||
|
||||
</div>
|
||||
);
|
||||
|
||||
} else if (!status.active) {
|
||||
return (
|
||||
<div style={{whiteSpace: "nowrap"}}>
|
||||
<Typography variant="caption" color="textSecondary">{label}</Typography>
|
||||
|
||||
<span style={{color: RED_COLOR}}>
|
||||
<Typography variant="caption" color="inherit">Stopped </Typography>
|
||||
</span>
|
||||
{
|
||||
status.temperaturemC > -100000 &&
|
||||
(
|
||||
<span style={{color: status.temperaturemC > 75000? RED_COLOR: GREEN_COLOR}}>
|
||||
<Typography variant="caption" color="inherit">{tempDisplay(status.temperaturemC)}</Typography>
|
||||
</span>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
);
|
||||
} else {
|
||||
let underrunError = status.msSinceLastUnderrun < 15*1000;
|
||||
return (
|
||||
<div style={{whiteSpace: "nowrap"}}>
|
||||
<Typography variant="caption" color="textSecondary">{label}</Typography>
|
||||
<span style={{color: underrunError? RED_COLOR: GREEN_COLOR}}>
|
||||
<Typography variant="caption" color="inherit">
|
||||
XRuns: {status.underruns+""}
|
||||
</Typography>
|
||||
</span>
|
||||
<span style={{color: underrunError? RED_COLOR: GREEN_COLOR}}>
|
||||
<Typography variant="caption" color="inherit">
|
||||
CPU: {cpuDisplay(status.cpuUsage)}
|
||||
</Typography>
|
||||
</span>
|
||||
|
||||
<span style={{color: GREEN_COLOR}}>
|
||||
<Typography variant="caption" color="inherit">{tempDisplay(status.temperaturemC)}</Typography>
|
||||
</span>
|
||||
|
||||
</div>
|
||||
);
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
@@ -0,0 +1,35 @@
|
||||
|
||||
|
||||
export default class JackServerSettings {
|
||||
deserialize(input: any) : JackServerSettings{
|
||||
this.valid = input.valid;
|
||||
this.rebootRequired = input.rebootRequired;
|
||||
this.sampleRate = input.sampleRate;
|
||||
this.bufferSize = input.bufferSize;
|
||||
this.numberOfBuffers = input.numberOfBuffers;
|
||||
return this;
|
||||
}
|
||||
constructor(sampleRate?: number, bufferSize?: number, numberOfBuffers?: number)
|
||||
{
|
||||
if (sampleRate) this.sampleRate = sampleRate;
|
||||
if (bufferSize) this.bufferSize = bufferSize;
|
||||
if (numberOfBuffers) this.numberOfBuffers = numberOfBuffers;
|
||||
if (numberOfBuffers) {
|
||||
this.valid = true;
|
||||
}
|
||||
}
|
||||
valid: boolean = false;
|
||||
rebootRequired = false;
|
||||
sampleRate = 48000;
|
||||
bufferSize = 64;
|
||||
numberOfBuffers = 3;
|
||||
|
||||
getSummaryText() {
|
||||
if (this.valid) {
|
||||
return "Sample Rate: " + this.sampleRate + " BufferSize: " + this.bufferSize + " Number of Buffers: " + this.numberOfBuffers;
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,169 @@
|
||||
|
||||
import React, { Component } from 'react';
|
||||
|
||||
import { createStyles, WithStyles, withStyles, Theme } from '@material-ui/core/styles';
|
||||
|
||||
import Button from '@material-ui/core/Button';
|
||||
import DialogActions from '@material-ui/core/DialogActions';
|
||||
import Dialog from '@material-ui/core/Dialog';
|
||||
import JackServerSettings from './JackServerSettings';
|
||||
|
||||
|
||||
import InputLabel from '@material-ui/core/InputLabel';
|
||||
import FormControl from '@material-ui/core/FormControl';
|
||||
import Select from '@material-ui/core/Select';
|
||||
import DialogContent from '@material-ui/core/DialogContent';
|
||||
import MenuItem from '@material-ui/core/MenuItem';
|
||||
import { nullCast } from './Utility';
|
||||
import Typography from '@material-ui/core/Typography';
|
||||
|
||||
interface JackServerSettingsDialogState {
|
||||
latencyText: string;
|
||||
};
|
||||
|
||||
const styles = (theme: Theme) =>
|
||||
createStyles({
|
||||
formControl: {
|
||||
margin: theme.spacing(1),
|
||||
minWidth: 120,
|
||||
},
|
||||
selectEmpty: {
|
||||
marginTop: theme.spacing(2),
|
||||
},
|
||||
});
|
||||
export interface JackServerSettingsDialogProps extends WithStyles<typeof styles> {
|
||||
open: boolean;
|
||||
jackServerSettings: JackServerSettings;
|
||||
onClose: () => void;
|
||||
onApply: (sampleRate: number, bufferSize: number, numberOfBuffers: number) => void;
|
||||
}
|
||||
|
||||
function getLatencyText(sampleRate: number, bufferSize: number, numberOfBuffers: number): string {
|
||||
let ms = bufferSize * numberOfBuffers / sampleRate * 1000;
|
||||
return ms.toFixed(1) + "ms";
|
||||
}
|
||||
|
||||
const JackServerSettingsDialog = withStyles(styles)(
|
||||
class extends Component<JackServerSettingsDialogProps, JackServerSettingsDialogState> {
|
||||
constructor(props: JackServerSettingsDialogProps) {
|
||||
super(props);
|
||||
let jackServerSettings = props.jackServerSettings;
|
||||
|
||||
this.state = {
|
||||
latencyText:
|
||||
getLatencyText(
|
||||
jackServerSettings.sampleRate,
|
||||
jackServerSettings.bufferSize,
|
||||
jackServerSettings.numberOfBuffers)
|
||||
};
|
||||
}
|
||||
|
||||
updateLatency(e: any): void {
|
||||
setTimeout(()=> {
|
||||
let sampleRate: number = parseInt(nullCast<any>(document.getElementById('jsd_sampleRate')).value);
|
||||
let bufferSize: number = parseInt(nullCast<any>(document.getElementById('jsd_bufferSize')).value);
|
||||
let bufferCount: number = parseInt(nullCast<any>(document.getElementById('jsd_bufferCount')).value);
|
||||
this.setState({
|
||||
latencyText: getLatencyText(sampleRate, bufferSize, bufferCount)
|
||||
});
|
||||
},0);
|
||||
}
|
||||
handleApply() {
|
||||
let sampleRate = parseInt(nullCast<any>(document.getElementById('jsd_sampleRate')).value);
|
||||
let bufferSize = parseInt(nullCast<any>(document.getElementById('jsd_bufferSize')).value);
|
||||
let bufferCount = parseInt(nullCast<any>(document.getElementById('jsd_bufferCount')).value);
|
||||
|
||||
this.props.onApply(sampleRate,bufferSize,bufferCount);
|
||||
};
|
||||
|
||||
|
||||
render() {
|
||||
const classes = this.props.classes;
|
||||
|
||||
const { onClose, jackServerSettings, open } = this.props;
|
||||
|
||||
const handleClose = () => {
|
||||
onClose();
|
||||
};
|
||||
|
||||
return (
|
||||
<Dialog onClose={handleClose} aria-labelledby="select-channels-title" open={open}>
|
||||
<DialogContent>
|
||||
<FormControl className={classes.formControl}>
|
||||
<InputLabel htmlFor="sampleRate">Sample rate</InputLabel>
|
||||
<Select
|
||||
onChange={(e) => this.updateLatency(e)}
|
||||
defaultValue={jackServerSettings.sampleRate}
|
||||
inputProps={{
|
||||
name: 'Sample Rate',
|
||||
id: 'jsd_sampleRate',
|
||||
style: {
|
||||
textAlign: "right"
|
||||
}
|
||||
}}
|
||||
>
|
||||
<MenuItem value={44100}>44100</MenuItem>
|
||||
<MenuItem value={48000}>48000</MenuItem>
|
||||
<MenuItem value={88200}>88200</MenuItem>
|
||||
<MenuItem value={96000}>96000</MenuItem>
|
||||
<MenuItem value={176400}>176400</MenuItem>
|
||||
<MenuItem value={192000}>192000</MenuItem>
|
||||
</Select>
|
||||
</FormControl>
|
||||
<div style={{ display: "inline", whiteSpace: "nowrap" }}>
|
||||
<FormControl className={classes.formControl}>
|
||||
<InputLabel htmlFor="bufferSize">Buffer size</InputLabel>
|
||||
<Select
|
||||
onChange={(e) => this.updateLatency(e)}
|
||||
defaultValue={jackServerSettings.bufferSize}
|
||||
inputProps={{
|
||||
name: 'Buffer size',
|
||||
id: 'jsd_bufferSize',
|
||||
}}
|
||||
>
|
||||
<MenuItem value={16}>16</MenuItem>
|
||||
<MenuItem value={32}>32</MenuItem>
|
||||
<MenuItem value={64}>64</MenuItem>
|
||||
<MenuItem value={128}>128</MenuItem>
|
||||
<MenuItem value={256}>256</MenuItem>
|
||||
<MenuItem value={512}>512</MenuItem>
|
||||
<MenuItem value={1024}>1024</MenuItem>
|
||||
</Select>
|
||||
</FormControl>
|
||||
<FormControl className={classes.formControl}>
|
||||
<InputLabel htmlFor="numberofBuffers">Buffers</InputLabel>
|
||||
<Select
|
||||
onChange={(e) => this.updateLatency(e)}
|
||||
defaultValue={jackServerSettings.numberOfBuffers}
|
||||
inputProps={{
|
||||
name: 'Number of buffers',
|
||||
id: 'jsd_bufferCount',
|
||||
}}
|
||||
>
|
||||
<MenuItem value={2}>2</MenuItem>
|
||||
<MenuItem value={3}>3</MenuItem>
|
||||
<MenuItem value={4}>4</MenuItem>
|
||||
</Select>
|
||||
</FormControl>
|
||||
</div>
|
||||
<Typography display="block" variant="caption" style={{ textAlign: "left", marginTop: 8, marginLeft: 24 }}
|
||||
color="textSecondary">
|
||||
Latency: {this.state.latencyText}
|
||||
</Typography>
|
||||
</DialogContent>
|
||||
|
||||
<DialogActions>
|
||||
<Button onClick={handleClose} color="primary">
|
||||
Cancel
|
||||
</Button>
|
||||
<Button onClick={()=> this.handleApply()} color="secondary">
|
||||
OK
|
||||
</Button>
|
||||
</DialogActions>
|
||||
|
||||
</Dialog>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
export default JackServerSettingsDialog;
|
||||
@@ -0,0 +1,63 @@
|
||||
import React from 'react';
|
||||
import { PiPedalModel, PiPedalModelFactory,State } from './PiPedalModel';
|
||||
import JackHostStatus from './JackHostStatus';
|
||||
|
||||
|
||||
|
||||
|
||||
interface JackStatusViewProps {
|
||||
|
||||
};
|
||||
|
||||
interface JackStatusViewState {
|
||||
jackStatus?: JackHostStatus;
|
||||
}
|
||||
|
||||
|
||||
export default class JackStatusView extends React.Component<JackStatusViewProps, JackStatusViewState>
|
||||
{
|
||||
model: PiPedalModel;
|
||||
|
||||
constructor(props: JackStatusViewProps) {
|
||||
super(props);
|
||||
this.state = {
|
||||
jackStatus: undefined
|
||||
};
|
||||
this.model = PiPedalModelFactory.getInstance();
|
||||
this.tick = this.tick.bind(this);
|
||||
}
|
||||
|
||||
tick() {
|
||||
if (this.model.state.get() === State.Ready) {
|
||||
this.model.getJackStatus()
|
||||
.then(jackStatus => {
|
||||
this.setState({jackStatus: jackStatus});
|
||||
})
|
||||
.catch(error => { /* ignore*/ });
|
||||
}
|
||||
}
|
||||
|
||||
timerHandle?: NodeJS.Timeout;
|
||||
componentDidMount() {
|
||||
this.timerHandle = setInterval(this.tick, 1000);
|
||||
}
|
||||
componentWillUnmount() {
|
||||
if (this.timerHandle) {
|
||||
clearTimeout(this.timerHandle);
|
||||
this.timerHandle = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div style={{
|
||||
position: "absolute", right: 30, bottom: 0, height: 30, width: 200,
|
||||
paddingRight: 20, paddingBottom: 6,
|
||||
textAlign: "right", opacity: 0.7, whiteSpace: "nowrap", fontSize: 12, zIndex: 10, fontWeight: 900
|
||||
}}>
|
||||
{JackHostStatus.getDisplayView("",this.state.jackStatus) }
|
||||
</div>
|
||||
);
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,453 @@
|
||||
import React, { ReactNode, SyntheticEvent } from 'react';
|
||||
import Grid from '@material-ui/core/Grid';
|
||||
import { createStyles, withStyles, WithStyles, Theme } from '@material-ui/core/styles';
|
||||
import { PiPedalModel, PiPedalModelFactory } from './PiPedalModel';
|
||||
import { UiPlugin, PluginType } from './Lv2Plugin';
|
||||
import ButtonBase from '@material-ui/core/ButtonBase';
|
||||
import Button from '@material-ui/core/Button';
|
||||
import MenuItem from '@material-ui/core/MenuItem';
|
||||
import Typography from '@material-ui/core/Typography';
|
||||
import PluginInfoDialog from './PluginInfoDialog'
|
||||
import PluginIcon from './PluginIcon'
|
||||
import Dialog from '@material-ui/core/Dialog';
|
||||
import DialogActions from '@material-ui/core/DialogActions';
|
||||
import DialogContent from '@material-ui/core/DialogContent';
|
||||
import DialogTitle from '@material-ui/core/DialogTitle';
|
||||
import SelectHoverBackground from './SelectHoverBackground';
|
||||
import ArrowBackIcon from '@material-ui/icons/ArrowBack';
|
||||
import IconButton from '@material-ui/core/IconButton';
|
||||
import Select from '@material-ui/core/Select';
|
||||
import PluginClass from './PluginClass'
|
||||
import ClearIcon from '@material-ui/icons/Clear';
|
||||
import ResizeResponsiveComponent from './ResizeResponsiveComponent';
|
||||
import { TransitionProps } from '@material-ui/core/transitions/transition';
|
||||
import Slide from '@material-ui/core/Slide';
|
||||
|
||||
|
||||
export type CloseEventHandler = () => void;
|
||||
export type OkEventHandler = (pluginUri: string) => void;
|
||||
|
||||
const NARROW_DISPLAY_THRESHOLD = 600;
|
||||
const FILTER_STORAGE_KEY = "com.twoplay.piddle.load_dlg.filter";
|
||||
|
||||
|
||||
const pluginGridStyles = (theme: Theme) => createStyles({
|
||||
frame: {
|
||||
|
||||
position: "relative",
|
||||
width: "100%",
|
||||
height: "100%"
|
||||
},
|
||||
|
||||
top: {
|
||||
top: "0px",
|
||||
right: "0px",
|
||||
left: "0px",
|
||||
bottom: "64px",
|
||||
position: "relative",
|
||||
flexGrow: 1,
|
||||
overflowX: "hidden",
|
||||
overflowY: "visible"
|
||||
},
|
||||
bottom: {
|
||||
position: "relative",
|
||||
bottom: "0px",
|
||||
height: "64px",
|
||||
width: "100%",
|
||||
display: "flex",
|
||||
flexDirection: "row",
|
||||
flexWrap: "nowrap",
|
||||
justifyContent: "space-between",
|
||||
paddingLeft: "24px",
|
||||
paddingRight: "48px",
|
||||
background: theme.palette.background.paper,
|
||||
},
|
||||
paper: {
|
||||
position: "relative",
|
||||
overflow: "hidden",
|
||||
height: "56px",
|
||||
width: "100%",
|
||||
background: theme.palette.background.paper
|
||||
},
|
||||
buttonBase: {
|
||||
|
||||
width: "100%",
|
||||
height: "100%'"
|
||||
},
|
||||
content: {
|
||||
marginTop: "8px",
|
||||
marginBottom: "8px",
|
||||
marginLeft: "12px",
|
||||
marginRight: "12px",
|
||||
width: "100%",
|
||||
overflow: "hidden",
|
||||
textAlign: "left",
|
||||
display: "flex",
|
||||
flexDirection: "row",
|
||||
justifyContent: "flex-start",
|
||||
height: 48,
|
||||
alignItems: "start",
|
||||
flexWrap: "nowrap"
|
||||
},
|
||||
content2: {
|
||||
display: "flex", flexDirection: "column", flex: "1 1 auto", width: "100%",
|
||||
paddingLeft: 16, whiteSpace: "nowrap"
|
||||
|
||||
},
|
||||
table: {
|
||||
borderCollapse: "collapse",
|
||||
},
|
||||
icon: {
|
||||
width: "24px",
|
||||
height: "24px",
|
||||
margin: "0px",
|
||||
opacity: "0.6"
|
||||
},
|
||||
iconBorder: {
|
||||
flex: "0 0 auto"
|
||||
},
|
||||
label: {
|
||||
width: "100%"
|
||||
},
|
||||
label2: {
|
||||
marginTop: 8,
|
||||
color: theme.palette.text.secondary
|
||||
},
|
||||
control: {
|
||||
padding: theme.spacing(1),
|
||||
},
|
||||
tdText: {
|
||||
padding: "0px"
|
||||
|
||||
}
|
||||
})
|
||||
;
|
||||
|
||||
interface PluginGridProps extends WithStyles<typeof pluginGridStyles> {
|
||||
onOk: OkEventHandler;
|
||||
onCancel: CloseEventHandler;
|
||||
uri?: string;
|
||||
minimumItemWidth?: number;
|
||||
theme: Theme;
|
||||
open: boolean
|
||||
};
|
||||
|
||||
type PluginGridState = {
|
||||
selected_uri?: string,
|
||||
hover_uri?: string,
|
||||
filterType: PluginType,
|
||||
client_width: number,
|
||||
client_height: number,
|
||||
minimumItemWidth: number,
|
||||
|
||||
}
|
||||
const Transition = React.forwardRef(function Transition(
|
||||
props: TransitionProps & { children?: React.ReactElement<any, any> },
|
||||
ref: React.Ref<unknown>,
|
||||
) {
|
||||
return <Slide direction="up" ref={ref} {...props} />;
|
||||
});
|
||||
|
||||
|
||||
export const LoadPluginDialog =
|
||||
withStyles(pluginGridStyles, { withTheme: true })(
|
||||
class extends ResizeResponsiveComponent<PluginGridProps, PluginGridState>
|
||||
{
|
||||
model: PiPedalModel;
|
||||
|
||||
constructor(props: PluginGridProps) {
|
||||
super(props);
|
||||
this.model = PiPedalModelFactory.getInstance();
|
||||
|
||||
let filterType_ = PluginType.Plugin; // i.e. "Any".
|
||||
let persistedFilter = window.localStorage.getItem(FILTER_STORAGE_KEY);
|
||||
if (persistedFilter) {
|
||||
filterType_ = persistedFilter as PluginType;
|
||||
}
|
||||
|
||||
this.state = {
|
||||
selected_uri: this.props.uri,
|
||||
hover_uri: "",
|
||||
filterType: filterType_,
|
||||
client_width: window.innerWidth,
|
||||
client_height: window.innerHeight,
|
||||
minimumItemWidth: props.minimumItemWidth ? props.minimumItemWidth : 220
|
||||
};
|
||||
|
||||
this.updateWindowSize = this.updateWindowSize.bind(this);
|
||||
this.handleCancel = this.handleCancel.bind(this);
|
||||
this.handleOk = this.handleOk.bind(this);
|
||||
}
|
||||
|
||||
updateWindowSize() {
|
||||
this.setState({
|
||||
client_width: window.innerWidth,
|
||||
client_height: window.innerHeight
|
||||
});
|
||||
}
|
||||
componentDidMount() {
|
||||
super.componentDidMount();
|
||||
this.updateWindowSize();
|
||||
window.addEventListener('resize', this.updateWindowSize);
|
||||
}
|
||||
componentWillUnmount() {
|
||||
super.componentWillUnmount();
|
||||
window.removeEventListener('resize', this.updateWindowSize);
|
||||
}
|
||||
|
||||
onWindowSizeChanged(width: number, height: number): void {
|
||||
super.onWindowSizeChanged(width, height);
|
||||
|
||||
}
|
||||
|
||||
onFilterChange(e: any) {
|
||||
let value = e.target.value as PluginType;
|
||||
|
||||
window.localStorage.setItem(FILTER_STORAGE_KEY, value as string);
|
||||
|
||||
this.setState({ filterType: value });
|
||||
}
|
||||
onClearFilter(): void {
|
||||
let value = PluginType.Plugin;
|
||||
window.localStorage.setItem(FILTER_STORAGE_KEY, value as string);
|
||||
|
||||
this.setState({ filterType: value });
|
||||
}
|
||||
|
||||
selectItem(item: number): void {
|
||||
let uri: string = "";
|
||||
let plugins = this.model.ui_plugins.get();
|
||||
if (item >= 0 && item < plugins.length) {
|
||||
uri = plugins[item].uri;
|
||||
}
|
||||
if (uri !== this.state.selected_uri) {
|
||||
this.setState({ selected_uri: uri });
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
handleCancel(e: SyntheticEvent): void {
|
||||
e.preventDefault();
|
||||
this.cancel();
|
||||
}
|
||||
handleOk(e: SyntheticEvent): void {
|
||||
e.preventDefault();
|
||||
if (this.state.selected_uri) {
|
||||
this.props.onOk(this.state.selected_uri);
|
||||
}
|
||||
}
|
||||
|
||||
onDoubleClick(e: SyntheticEvent, uri: string): void {
|
||||
this.props.onOk(uri);
|
||||
}
|
||||
|
||||
|
||||
cancel(): void {
|
||||
this.props.onCancel();
|
||||
}
|
||||
|
||||
fireSelected(item?: UiPlugin) {
|
||||
if (item) {
|
||||
this.setState({ selected_uri: item.uri });
|
||||
}
|
||||
}
|
||||
setHoverUri(uri: string) {
|
||||
this.setState({ hover_uri: uri });
|
||||
}
|
||||
|
||||
onClick(e: SyntheticEvent, uri: string): void {
|
||||
this.setState({ selected_uri: uri });
|
||||
}
|
||||
handleMouseEnter(e: SyntheticEvent, uri: string): void {
|
||||
this.setHoverUri(uri);
|
||||
|
||||
}
|
||||
handleMouseLeave(e: SyntheticEvent, uri: string): void {
|
||||
this.setHoverUri("");
|
||||
|
||||
}
|
||||
onInfoClicked(): void {
|
||||
// let selectedUri = this.state.selected_uri;
|
||||
}
|
||||
|
||||
stereo_indicator(uiPlugin?: UiPlugin): string {
|
||||
if (!uiPlugin) return "";
|
||||
if (uiPlugin.audio_inputs === 2 || uiPlugin.audio_outputs === 2) {
|
||||
return " (Stereo)";
|
||||
}
|
||||
return "";
|
||||
}
|
||||
info_string(uiPlugin?: UiPlugin): string {
|
||||
if (uiPlugin === undefined) return "";
|
||||
let result = uiPlugin.name;
|
||||
if (uiPlugin.author_name !== "") {
|
||||
result += ", " + uiPlugin.author_name;
|
||||
}
|
||||
result += this.stereo_indicator(uiPlugin);
|
||||
return result;
|
||||
}
|
||||
createFilterChildren(result: ReactNode[], classNode: PluginClass, level: number): void {
|
||||
for (let i = 0; i < classNode.children.length; ++i) {
|
||||
let child = classNode.children[i];
|
||||
let name = "\u00A0".repeat(level * 3 + 1) + child.display_name;
|
||||
result.push((<MenuItem value={child.plugin_type}>{name}</MenuItem>));
|
||||
if (child.children.length !== 0) {
|
||||
this.createFilterChildren(result, child, level + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
createFilterOptions(): ReactNode[] {
|
||||
let classes = this.model.plugin_classes.get();
|
||||
let result: ReactNode[] = [];
|
||||
|
||||
result.push((<MenuItem value={PluginType.Plugin}> All</MenuItem>));
|
||||
this.createFilterChildren(result, classes, 1);
|
||||
return result;
|
||||
|
||||
}
|
||||
filterPlugins(plugins: UiPlugin[]): ReactNode[] {
|
||||
let result: ReactNode[] = [];
|
||||
let filterType = this.state.filterType;
|
||||
let classes = this.props.classes;
|
||||
let rootClass = this.model.plugin_classes.get();
|
||||
|
||||
for (let i = 0; i < plugins.length; ++i) {
|
||||
let value = plugins[i];
|
||||
if (filterType === PluginType.Plugin || rootClass.is_type_of(filterType, value.plugin_type)) {
|
||||
result.push(
|
||||
(
|
||||
<Grid key={value.uri} xs={12} sm={6} md={4} lg={3} xl={3} item
|
||||
onDoubleClick={(e) => { this.onDoubleClick(e, value.uri) }}
|
||||
onClick={(e) => { this.onClick(e, value.uri) }}
|
||||
onMouseEnter={(e) => { this.handleMouseEnter(e, value.uri) }}
|
||||
onMouseLeave={(e) => { this.handleMouseLeave(e, value.uri) }}
|
||||
>
|
||||
<ButtonBase className={classes.buttonBase} >
|
||||
<SelectHoverBackground selected={value.uri === this.state.selected_uri} showHover={true} />
|
||||
<div className={classes.content}>
|
||||
<div className={classes.iconBorder} >
|
||||
<PluginIcon pluginType={value.plugin_type} pluginUri={value.uri} size={24} />
|
||||
</div>
|
||||
<div className={classes.content2}>
|
||||
<Typography color="textPrimary" noWrap className={classes.label} >
|
||||
{value.name}
|
||||
</Typography>
|
||||
<Typography color="textSecondary" noWrap>
|
||||
{value.plugin_display_type} {this.stereo_indicator(value)}
|
||||
|
||||
</Typography>
|
||||
</div>
|
||||
</div>
|
||||
</ButtonBase>
|
||||
</Grid>
|
||||
)
|
||||
);
|
||||
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
render() {
|
||||
const { classes } = this.props;
|
||||
|
||||
|
||||
let model = this.model;
|
||||
let plugins = model.ui_plugins.get();
|
||||
|
||||
let selectedPlugin: UiPlugin | undefined = undefined;
|
||||
if (this.state.selected_uri) {
|
||||
let t = this.model.getUiPlugin(this.state.selected_uri);
|
||||
if (t) selectedPlugin = t;
|
||||
}
|
||||
|
||||
return (
|
||||
|
||||
<React.Fragment>
|
||||
<Dialog
|
||||
fullScreen={true}
|
||||
TransitionComponent={Transition}
|
||||
maxWidth={false}
|
||||
open={this.props.open}
|
||||
scroll="body"
|
||||
onClose={this.handleCancel}
|
||||
style={{ overflowX: "hidden", overflowY: "hidden", display: "flex", flexDirection: "column", flexWrap: "nowrap" }}
|
||||
aria-labelledby="select-plugin-dialog-title">
|
||||
<DialogTitle id="select-plugin-dialog-title" style={{ flex: "0 0 auto" }}>
|
||||
<div style={{ display: "flex", flexDirection: "row", flexWrap: "nowrap", width: "100%", alignItems: "center" }}>
|
||||
<IconButton onClick={() => { this.cancel(); }} style={{ flex: "0 0 auto" }} >
|
||||
<ArrowBackIcon />
|
||||
</IconButton>
|
||||
|
||||
<Typography display="block" variant="h6" style={{ flex: "0 1 auto" }}>
|
||||
{this.state.client_width > 520 ? "Select Plugin" : ""}
|
||||
</Typography>
|
||||
<div style={{ flex: "1 1 auto" }} />
|
||||
<Select defaultValue={this.state.filterType} key={this.state.filterType} onChange={(e) => { this.onFilterChange(e); }}
|
||||
style={{ flex: "0 0 160px" }}
|
||||
>
|
||||
{this.createFilterOptions()}
|
||||
</Select>
|
||||
<div style={{ flex: "0 0 auto", marginRight: 24, visibility: this.state.filterType === PluginType.Plugin ? "hidden" : "visible" }} >
|
||||
<IconButton onClick={() => { this.onClearFilter(); }}>
|
||||
<ClearIcon fontSize='small' style={{ opacity: 0.6 }} />
|
||||
</IconButton>
|
||||
</div>
|
||||
</div>
|
||||
</DialogTitle>
|
||||
<DialogContent dividers style={{
|
||||
height: this.state.client_height - (this.state.client_width < NARROW_DISPLAY_THRESHOLD ? 150 + 64 : 160),
|
||||
padding: 8,
|
||||
display: "1 1 flex"
|
||||
}} >
|
||||
<Grid container justify="flex-start" >
|
||||
{
|
||||
this.filterPlugins(plugins)
|
||||
}
|
||||
|
||||
|
||||
</Grid>
|
||||
</DialogContent>
|
||||
{(this.state.client_width >= NARROW_DISPLAY_THRESHOLD) ? (
|
||||
<DialogActions style={{ flex: "0 0 auto" }} >
|
||||
<div className={classes.bottom}>
|
||||
<div style={{ display: "flex", justifyContent: "start", alignItems: "center", flex: "1 1 auto", height: "100%", overflow: "hidden" }} >
|
||||
<PluginInfoDialog plugin_uri={this.state.selected_uri ?? ""} />
|
||||
<Typography display='block' variant='body2' color="textPrimary" noWrap >
|
||||
{this.info_string(selectedPlugin)}
|
||||
</Typography>
|
||||
</div>
|
||||
<div style={{ position: "relative", float: "right", flex: "0 1 200px", width: 200, display: "flex", alignItems: "center" }}>
|
||||
<Button onClick={this.handleCancel} style={{ width: 120 }} >Cancel</Button>
|
||||
<Button onClick={this.handleOk} color="secondary" disabled={selectedPlugin === null} style={{ width: 180 }} >SELECT</Button>
|
||||
</div>
|
||||
</div>
|
||||
</DialogActions>
|
||||
) : (
|
||||
<DialogActions style={{ flex: "0 0 auto", display: "block" }} >
|
||||
<div style={{ display: "flex", justifyContent: "start", alignItems: "center", flex: "1 1 auto", overflow: "hidden" }} >
|
||||
<PluginInfoDialog plugin_uri={this.state.selected_uri ?? ""} />
|
||||
<div style={{ width: 1, height: 48 }} />
|
||||
<Typography display='block' variant='body2' color="textPrimary" noWrap >
|
||||
{this.info_string(selectedPlugin)}
|
||||
</Typography>
|
||||
</div>
|
||||
<div className={classes.bottom}>
|
||||
<div style={{ flex: "1 1 1px" }} />
|
||||
<div style={{ position: "relative", flex: "0 1 auto", display: "flex", alignItems: "center" }}>
|
||||
<Button onClick={this.handleCancel} style={{ width: 120, height: 48 }} >Cancel</Button>
|
||||
<Button onClick={this.handleOk} color="secondary" disabled={selectedPlugin === null} style={{ width: 120, height: 48 }} >SELECT</Button>
|
||||
</div>
|
||||
</div>
|
||||
</DialogActions>
|
||||
|
||||
)}
|
||||
</Dialog>
|
||||
</React.Fragment >
|
||||
);
|
||||
}
|
||||
}
|
||||
);
|
||||
export default LoadPluginDialog;
|
||||
|
||||
@@ -0,0 +1,441 @@
|
||||
import React, { ReactNode, SyntheticEvent } from 'react';
|
||||
import Grid from '@material-ui/core/Grid';
|
||||
import { createStyles, withStyles, WithStyles, Theme } from '@material-ui/core/styles';
|
||||
import { PiPedalModel, PiPedalModelFactory } from './PiPedalModel';
|
||||
import { UiPlugin, PluginType } from './Lv2Plugin';
|
||||
import Paper from '@material-ui/core/Paper';
|
||||
import ButtonBase from '@material-ui/core/ButtonBase';
|
||||
import Button from '@material-ui/core/Button';
|
||||
import MenuItem from '@material-ui/core/MenuItem';
|
||||
import Typography from '@material-ui/core/Typography';
|
||||
import PluginInfoDialog from './PluginInfoDialog'
|
||||
import PluginIcon from './PluginIcon'
|
||||
import Dialog from '@material-ui/core/Dialog';
|
||||
import DialogActions from '@material-ui/core/DialogActions';
|
||||
import DialogContent from '@material-ui/core/DialogContent';
|
||||
import DialogTitle from '@material-ui/core/DialogTitle';
|
||||
import SelectHoverBackground from './SelectHoverBackground';
|
||||
import ArrowBackIcon from '@material-ui/icons/ArrowBack';
|
||||
import IconButton from '@material-ui/core/IconButton';
|
||||
import Select from '@material-ui/core/Select';
|
||||
import PluginClass from './PluginClass'
|
||||
import ClearIcon from '@material-ui/icons/Clear';
|
||||
import ResizeResponsiveComponent from './ResizeResponsiveComponent';
|
||||
import { TransitionProps } from '@material-ui/core/transitions/transition';
|
||||
import Slide from '@material-ui/core/Slide';
|
||||
|
||||
|
||||
export type CloseEventHandler = () => void;
|
||||
export type OkEventHandler = (pluginUri: string) => void;
|
||||
|
||||
const NARROW_DISPLAY_THRESHOLD = 600;
|
||||
const FILTER_STORAGE_KEY = "com.twoplay.piddle.load_dlg.filter";
|
||||
|
||||
const Transition = React.forwardRef(function Transition(
|
||||
props: TransitionProps & { children?: React.ReactElement },
|
||||
ref: React.Ref<unknown>,
|
||||
) {
|
||||
return <Slide direction="up" ref={ref} {...props} />;
|
||||
});
|
||||
|
||||
|
||||
|
||||
const pluginGridStyles = (theme: Theme) => createStyles({
|
||||
frame: {
|
||||
|
||||
position: "relative",
|
||||
width: "100%",
|
||||
height: "100%"
|
||||
},
|
||||
|
||||
top: {
|
||||
top: "0px",
|
||||
right: "0px",
|
||||
left: "0px",
|
||||
bottom: "64px",
|
||||
position: "relative",
|
||||
flexGrow: 1,
|
||||
background: "#eee",
|
||||
overflowX: "hidden",
|
||||
overflowY: "visible"
|
||||
},
|
||||
bottom: {
|
||||
position: "relative",
|
||||
bottom: "0px",
|
||||
height: "64px",
|
||||
width: "100%",
|
||||
display: "flex",
|
||||
flexDirection: "row",
|
||||
flexWrap: "nowrap",
|
||||
justifyContent: "space-between",
|
||||
paddingLeft: "24px",
|
||||
paddingRight: "48px",
|
||||
background: theme.palette.background.paper,
|
||||
},
|
||||
paper: {
|
||||
position: "relative",
|
||||
overflow: "hidden",
|
||||
height: "56px",
|
||||
width: "100%",
|
||||
background: theme.palette.background.paper
|
||||
},
|
||||
buttonBase: {
|
||||
|
||||
width: "100%",
|
||||
height: "100%'"
|
||||
},
|
||||
content: {
|
||||
marginTop: "8px",
|
||||
marginBottom: "8px",
|
||||
marginLeft: "12px",
|
||||
marginRight: "12px",
|
||||
width: "100%",
|
||||
overflow: "hidden"
|
||||
},
|
||||
table: {
|
||||
borderCollapse: "collapse",
|
||||
},
|
||||
icon: {
|
||||
width: "24px",
|
||||
height: "24px",
|
||||
margin: "0px",
|
||||
opacity: "0.6"
|
||||
},
|
||||
label: {
|
||||
marginLeft: "8px",
|
||||
|
||||
},
|
||||
control: {
|
||||
padding: theme.spacing(1),
|
||||
},
|
||||
tdText: {
|
||||
padding: "0px"
|
||||
|
||||
}
|
||||
})
|
||||
;
|
||||
|
||||
interface PluginGridProps extends WithStyles<typeof pluginGridStyles> {
|
||||
onOk: OkEventHandler;
|
||||
onCancel: CloseEventHandler;
|
||||
uri?: string;
|
||||
minimumItemWidth?: number;
|
||||
theme: Theme;
|
||||
open: boolean
|
||||
};
|
||||
|
||||
type PluginGridState = {
|
||||
selected_uri?: string,
|
||||
hover_uri?: string,
|
||||
filterType: PluginType,
|
||||
client_width: number,
|
||||
client_height: number,
|
||||
minimumItemWidth: number,
|
||||
|
||||
}
|
||||
|
||||
|
||||
export const LoadPluginDialog =
|
||||
withStyles(pluginGridStyles, { withTheme: true })(
|
||||
class extends ResizeResponsiveComponent<PluginGridProps, PluginGridState>
|
||||
{
|
||||
model: PiPedalModel;
|
||||
|
||||
constructor(props: PluginGridProps) {
|
||||
super(props);
|
||||
this.model = PiPedalModelFactory.getInstance();
|
||||
|
||||
let filterType_ = PluginType.Plugin; // i.e. "Any".
|
||||
let persistedFilter = window.localStorage.getItem(FILTER_STORAGE_KEY);
|
||||
if (persistedFilter) {
|
||||
filterType_ = persistedFilter as PluginType;
|
||||
}
|
||||
|
||||
this.state = {
|
||||
selected_uri: this.props.uri,
|
||||
hover_uri: "",
|
||||
filterType: filterType_,
|
||||
client_width: window.innerWidth,
|
||||
client_height: window.innerHeight,
|
||||
minimumItemWidth: props.minimumItemWidth ? props.minimumItemWidth : 220
|
||||
};
|
||||
|
||||
this.updateWindowSize = this.updateWindowSize.bind(this);
|
||||
this.handleCancel = this.handleCancel.bind(this);
|
||||
this.handleOk = this.handleOk.bind(this);
|
||||
}
|
||||
|
||||
updateWindowSize() {
|
||||
this.setState({
|
||||
client_width: window.innerWidth,
|
||||
client_height: window.innerHeight
|
||||
});
|
||||
}
|
||||
componentDidMount() {
|
||||
super.componentDidMount();
|
||||
this.updateWindowSize();
|
||||
window.addEventListener('resize', this.updateWindowSize);
|
||||
}
|
||||
componentWillUnmount() {
|
||||
super.componentWillUnmount();
|
||||
window.removeEventListener('resize', this.updateWindowSize);
|
||||
}
|
||||
|
||||
onWindowSizeChanged(width: number,height: number): void {
|
||||
super.onWindowSizeChanged(width,height);
|
||||
|
||||
}
|
||||
|
||||
onFilterChange(e: any) {
|
||||
let value = e.target.value as PluginType;
|
||||
|
||||
window.localStorage.setItem(FILTER_STORAGE_KEY, value as string);
|
||||
|
||||
this.setState({ filterType: value });
|
||||
}
|
||||
onClearFilter(): void {
|
||||
let value = PluginType.Plugin;
|
||||
window.localStorage.setItem(FILTER_STORAGE_KEY, value as string);
|
||||
|
||||
this.setState({ filterType: value });
|
||||
}
|
||||
|
||||
selectItem(item: number): void {
|
||||
let uri: string = "";
|
||||
let plugins = this.model.ui_plugins.get();
|
||||
if (item >= 0 && item < plugins.length) {
|
||||
uri = plugins[item].uri;
|
||||
}
|
||||
if (uri !== this.state.selected_uri) {
|
||||
this.setState({ selected_uri: uri });
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
handleCancel(e: SyntheticEvent): void {
|
||||
e.preventDefault();
|
||||
this.cancel();
|
||||
}
|
||||
handleOk(e: SyntheticEvent): void {
|
||||
e.preventDefault();
|
||||
if (this.state.selected_uri) {
|
||||
this.props.onOk(this.state.selected_uri);
|
||||
}
|
||||
}
|
||||
|
||||
onDoubleClick(e: SyntheticEvent, uri: string): void {
|
||||
this.props.onOk(uri);
|
||||
}
|
||||
|
||||
|
||||
cancel(): void {
|
||||
this.props.onCancel();
|
||||
}
|
||||
|
||||
fireSelected(item?: UiPlugin) {
|
||||
if (item) {
|
||||
this.setState({ selected_uri: item.uri });
|
||||
}
|
||||
}
|
||||
setHoverUri(uri: string) {
|
||||
this.setState({ hover_uri: uri });
|
||||
}
|
||||
|
||||
onClick(e: SyntheticEvent, uri: string): void {
|
||||
this.setState({ selected_uri: uri });
|
||||
}
|
||||
handleMouseEnter(e: SyntheticEvent, uri: string): void {
|
||||
this.setHoverUri(uri);
|
||||
|
||||
}
|
||||
handleMouseLeave(e: SyntheticEvent, uri: string): void {
|
||||
this.setHoverUri("");
|
||||
|
||||
}
|
||||
onInfoClicked(): void {
|
||||
// let selectedUri = this.state.selected_uri;
|
||||
}
|
||||
|
||||
stereo_indicator(uiPlugin?: UiPlugin): string {
|
||||
if (!uiPlugin) return "";
|
||||
if (uiPlugin.audio_inputs === 2 || uiPlugin.audio_outputs === 2) {
|
||||
return " (Stereo)";
|
||||
}
|
||||
return "";
|
||||
}
|
||||
info_string(uiPlugin?: UiPlugin): string {
|
||||
if (uiPlugin === undefined) return "";
|
||||
let result = uiPlugin.name;
|
||||
if (uiPlugin.author_name !== "") {
|
||||
result += ", " + uiPlugin.author_name;
|
||||
}
|
||||
result += this.stereo_indicator(uiPlugin);
|
||||
return result;
|
||||
}
|
||||
createFilterChildren(result: ReactNode[], classNode: PluginClass, level: number): void {
|
||||
for (let i = 0; i < classNode.children.length; ++i) {
|
||||
let child = classNode.children[i];
|
||||
let name = "\u00A0".repeat(level * 3+1) + child.display_name;
|
||||
result.push((<MenuItem value={child.plugin_type}>{name}</MenuItem>));
|
||||
if (child.children.length !== 0) {
|
||||
this.createFilterChildren(result, child, level + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
createFilterOptions(): ReactNode[] {
|
||||
let classes = this.model.plugin_classes.get();
|
||||
let result: ReactNode[] = [];
|
||||
|
||||
result.push((<MenuItem value={PluginType.Plugin}> All</MenuItem>));
|
||||
this.createFilterChildren(result, classes, 1);
|
||||
return result;
|
||||
|
||||
}
|
||||
filterPlugins(plugins: UiPlugin[]): ReactNode[] {
|
||||
let result: ReactNode[] = [];
|
||||
let filterType = this.state.filterType;
|
||||
let classes = this.props.classes;
|
||||
let rootClass = this.model.plugin_classes.get();
|
||||
|
||||
for (let i = 0; i < plugins.length; ++i) {
|
||||
let value = plugins[i];
|
||||
if (filterType === PluginType.Plugin || rootClass.is_type_of(filterType, value.plugin_type)) {
|
||||
result.push(
|
||||
(
|
||||
<Grid key={value.uri} xs={6} sm={6} md={4} lg={3} xl={3} item
|
||||
onDoubleClick={(e) => { this.onDoubleClick(e, value.uri) }}
|
||||
onClick={(e) => { this.onClick(e, value.uri) }}
|
||||
onMouseEnter={(e) => { this.handleMouseEnter(e, value.uri) }}
|
||||
onMouseLeave={(e) => { this.handleMouseLeave(e, value.uri) }}
|
||||
>
|
||||
<Paper className={classes.paper} >
|
||||
<ButtonBase className={classes.buttonBase} focusRipple>
|
||||
<SelectHoverBackground selected={value.uri === this.state.selected_uri} showHover={true} />
|
||||
<div className={classes.content}>
|
||||
<div style={{ display: "flex", flexDirection: "row", justifyContent: "flex-start", alignItems: "start", flexWrap: "nowrap" }} >
|
||||
<div style={{ flex: "0 1 auto" }}>
|
||||
<PluginIcon pluginType={value.plugin_type} pluginUri={value.uri} size={24} />
|
||||
</div>
|
||||
<div style={{ display: "flex", flexDirection: "column", flex: "1 1 auto", width: "100%" }}>
|
||||
<Typography className={classes.label}
|
||||
display='block' variant='body2' align="left" color="textPrimary" noWrap
|
||||
>{value.name}
|
||||
</Typography>
|
||||
<Typography className={classes.label}
|
||||
display='block' variant='caption' align="left" noWrap
|
||||
>
|
||||
{value.plugin_display_type} {this.stereo_indicator(value)}
|
||||
</Typography>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ButtonBase>
|
||||
</Paper>
|
||||
</Grid>
|
||||
)
|
||||
);
|
||||
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
render() {
|
||||
const { classes } = this.props;
|
||||
|
||||
|
||||
let model = this.model;
|
||||
let plugins = model.ui_plugins.get();
|
||||
|
||||
let selectedPlugin: UiPlugin | undefined = undefined;
|
||||
if (this.state.selected_uri) {
|
||||
let t = this.model.getUiPlugin(this.state.selected_uri);
|
||||
if (t) selectedPlugin = t;
|
||||
}
|
||||
|
||||
return (
|
||||
|
||||
<React.Fragment>
|
||||
<Dialog
|
||||
TransitionComponent={Transition}
|
||||
fullScreen={true}
|
||||
|
||||
maxWidth={false}
|
||||
open={this.props.open}
|
||||
scroll="body"
|
||||
onClose={this.handleCancel}
|
||||
style={{ overflowX: "hidden", overflowY: "hidden", display: "flex", flexDirection:"column",flexWrap:"nowrap" }}
|
||||
aria-labelledby="select-plugin-dialog-title">
|
||||
<DialogTitle id="select-plugin-dialog-title" style={{ flex: "0 0 auto"}}>
|
||||
<div style={{ display: "flex", flexDirection: "row", flexWrap: "nowrap", width: "100%", alignItems: "center" }}>
|
||||
<IconButton onClick={() => { this.cancel(); }} style={{ flex: "0 0 auto" }} >
|
||||
<ArrowBackIcon />
|
||||
</IconButton>
|
||||
|
||||
<Typography display="block" variant="h6" style={{ flex: "0 1 auto" }}>
|
||||
{ this.state.client_width > 520 ? "Select Plugin" : "" }
|
||||
</Typography>
|
||||
<div style={{ flex: "1 1 auto" }} />
|
||||
<Select defaultValue={this.state.filterType} key={this.state.filterType} onChange={(e) => { this.onFilterChange(e); }}
|
||||
style={{ flex: "0 0 160px" }}
|
||||
>
|
||||
{this.createFilterOptions()}
|
||||
</Select>
|
||||
<div style={{ flex: "0 0 auto", marginRight: 24, visibility: this.state.filterType === PluginType.Plugin? "hidden": "visible" }} >
|
||||
<IconButton onClick={() => { this.onClearFilter(); }}>
|
||||
<ClearIcon fontSize='small' style={{ opacity: 0.6 }} />
|
||||
</IconButton>
|
||||
</div>
|
||||
</div>
|
||||
</DialogTitle>
|
||||
<DialogContent dividers style={{ height: this.state.client_height - (this.state.client_width < NARROW_DISPLAY_THRESHOLD ? 288: 220),
|
||||
background: "#eee", display: "1 1 flex"}} >
|
||||
<Grid container justify="flex-start" spacing={1} style={{ background: "#EEE" }}>
|
||||
{
|
||||
this.filterPlugins(plugins)
|
||||
}
|
||||
|
||||
|
||||
</Grid>
|
||||
</DialogContent>
|
||||
{ (this.state.client_width >= NARROW_DISPLAY_THRESHOLD)? (
|
||||
<DialogActions style={{flex: "0 0 auto"}} >
|
||||
<div className={classes.bottom}>
|
||||
<div style={{ display: "flex", justifyContent: "start", alignItems: "center", flex: "1 1 auto", height: "100%", overflow: "hidden" }} >
|
||||
<PluginInfoDialog plugin_uri={this.state.selected_uri ?? ""} />
|
||||
<Typography display='block' variant='body2' color="textPrimary" noWrap >
|
||||
{this.info_string(selectedPlugin)}
|
||||
</Typography>
|
||||
</div>
|
||||
<div style={{ position: "relative", float: "right", flex: "0 1 200px", width: 200, display: "flex", alignItems: "center" }}>
|
||||
<Button onClick={this.handleCancel} style={{ width: 120 }} >Cancel</Button>
|
||||
<Button onClick={this.handleOk} color="secondary" disabled={selectedPlugin === null} style={{ width: 180 }} >SELECT</Button>
|
||||
</div>
|
||||
</div>
|
||||
</DialogActions>
|
||||
) :(
|
||||
<DialogActions style={{flex: "0 0 auto", display: "block"}} >
|
||||
<div style={{ display: "flex", justifyContent: "start", alignItems: "center", flex: "1 1 auto", overflow: "hidden" }} >
|
||||
<PluginInfoDialog plugin_uri={this.state.selected_uri ?? ""} />
|
||||
<div style={{width: 1, height: 48}} />
|
||||
<Typography display='block' variant='body2' color="textPrimary" noWrap >
|
||||
{this.info_string(selectedPlugin)}
|
||||
</Typography>
|
||||
</div>
|
||||
<div className={classes.bottom}>
|
||||
<div style={{flex: "1 1 1px"}}/>
|
||||
<div style={{ position: "relative", flex: "0 1 auto", display: "flex", alignItems: "center" }}>
|
||||
<Button onClick={this.handleCancel} style={{ width: 120, height: 48 }} >Cancel</Button>
|
||||
<Button onClick={this.handleOk} color="secondary" disabled={selectedPlugin === null} style={{ width: 120, height: 48 }} >SELECT</Button>
|
||||
</div>
|
||||
</div>
|
||||
</DialogActions>
|
||||
|
||||
)}
|
||||
</Dialog>
|
||||
</React.Fragment >
|
||||
);
|
||||
}
|
||||
}
|
||||
);
|
||||
export default LoadPluginDialog;
|
||||
|
||||
@@ -0,0 +1,371 @@
|
||||
// Generated by https://quicktype.io
|
||||
//
|
||||
// To change quicktype's target language, run command:
|
||||
//
|
||||
// "Set quicktype target language"
|
||||
|
||||
import {PiPedalArgumentError} from "./PiPedalError";
|
||||
import Units from './Units';
|
||||
|
||||
interface Deserializable<T> {
|
||||
deserialize(input: any): T;
|
||||
}
|
||||
|
||||
|
||||
export class Port implements Deserializable<Port> {
|
||||
deserialize(input: any): Port {
|
||||
this.port_index = input.port_index;
|
||||
this.symbol = input.symbol;
|
||||
this.name = input.name;
|
||||
this.min_value = input.min_value;
|
||||
this.max_value = input.max_value;
|
||||
this.default_value = input.default_value;
|
||||
this.scale_points = ScalePoint.deserialize_array(input.scale_points);
|
||||
this.is_input = input.is_input;
|
||||
this.is_output = input.is_output;
|
||||
this.is_control_port = input.is_control_port;
|
||||
this.is_audio_port = input.is_audio_port;
|
||||
this.is_atom_port = input.is_atom_port;
|
||||
this.is_valid = input.is_valid;
|
||||
this.supports_midi = input.supports_midi;
|
||||
this.port_group = input.port_group;
|
||||
return this;
|
||||
}
|
||||
|
||||
static EmptyPorts: Port[] = [];
|
||||
|
||||
static deserialize_array(input: any): Port[] {
|
||||
let result: Port[] = [];
|
||||
for (let i = 0; i < input.length; ++i)
|
||||
{
|
||||
result[i] = new Port().deserialize(input[i]);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
port_index: number = -1;
|
||||
symbol: string = "";
|
||||
name: string = "";
|
||||
min_value: number = 0;
|
||||
max_value: number = 1;
|
||||
default_value: number = 0.5;
|
||||
scale_points: ScalePoint[] = [];
|
||||
is_input: boolean = false;
|
||||
is_output: boolean = false
|
||||
is_control_port: boolean = false;
|
||||
is_audio_port: boolean = false;
|
||||
is_atom_port: boolean = false;
|
||||
is_valid: boolean = false;
|
||||
supports_midi: boolean = false;
|
||||
port_group: string = "";
|
||||
}
|
||||
|
||||
export class PortGroup {
|
||||
deserialize(input: any): PortGroup {
|
||||
this.symbol = input.symbol;
|
||||
this.name = input.name;
|
||||
return this;
|
||||
}
|
||||
static deserialize_array(input: any) : PortGroup[] {
|
||||
let result: PortGroup[] = [];
|
||||
for (let i = 0; i < input.length; ++i)
|
||||
{
|
||||
result.push(new PortGroup().deserialize(input[i]));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
symbol: string = "";
|
||||
name: string = "";
|
||||
};
|
||||
|
||||
export class Lv2Plugin implements Deserializable<Lv2Plugin> {
|
||||
deserialize(input: any): Lv2Plugin
|
||||
{
|
||||
this.uri = input.uri;
|
||||
this.name = input.name;
|
||||
this.plugin_class = input.plugin_class;
|
||||
this.supported_features = input.supported_features;
|
||||
this.required_features = input.required_features;
|
||||
this.optional_features = input.optional_features;
|
||||
this.author_name = input.author_name;
|
||||
this.author_homepage = input.author_homepage;
|
||||
this.comment = input.comment;
|
||||
this.ports= Port.deserialize_array(input.ports);
|
||||
this.port_groups = PortGroup.deserialize_array(input.port_groups);
|
||||
return this;
|
||||
}
|
||||
static EmptyFeatures: string[] = [];
|
||||
|
||||
uri: string = "";
|
||||
name: string = "";
|
||||
plugin_class: string = "";
|
||||
supported_features: string[] = Lv2Plugin.EmptyFeatures;
|
||||
required_features: string[] = Lv2Plugin.EmptyFeatures;
|
||||
optional_features: string[] = Lv2Plugin.EmptyFeatures;
|
||||
author_name: string = "";
|
||||
author_homepage: string = "";
|
||||
comment: string = "";
|
||||
ports: Port[] = Port.EmptyPorts;
|
||||
port_groups: PortGroup[] = [];
|
||||
}
|
||||
|
||||
|
||||
export class ScalePoint implements Deserializable<ScalePoint> {
|
||||
deserialize(input: any): ScalePoint {
|
||||
this.value = input.value;
|
||||
this.label = input.label;
|
||||
return this;
|
||||
}
|
||||
static deserialize_array(input: any): ScalePoint[]
|
||||
{
|
||||
let result: ScalePoint[] = [];
|
||||
for (let i = 0; i < input.length; ++i)
|
||||
{
|
||||
result[i] = new ScalePoint().deserialize(input[i]);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
value: number = 0;
|
||||
label: string = "";
|
||||
}
|
||||
|
||||
export enum PluginType {
|
||||
// Reserved types used in pedalboards.
|
||||
None="",
|
||||
InvalidPlugin= "InvalidPlugin",
|
||||
|
||||
Plugin = "Plugin",
|
||||
AllpassPlugin = "AllpassPlugin",
|
||||
AmplifierPlugin = "AmplifierPlugin",
|
||||
AnalyserPlugin = "AnalyserPlugin",
|
||||
BandpassPlugin = "BandpassPlugin",
|
||||
ChorusPlugin = "ChorusPlugin",
|
||||
CombPlugin = "CombPlugin",
|
||||
CompressorPlugin = "CompressorPlugin",
|
||||
ConstantPlugin = "ConstantPlugin",
|
||||
ConverterPlugin = "ConverterPlugin",
|
||||
DelayPlugin = "DelayPlugin",
|
||||
DistortionPlugin = "DistortionPlugin",
|
||||
DynamicsPlugin = "DynamicsPlugin",
|
||||
EQPlugin = "EQPlugin",
|
||||
EnvelopePlugin = "EnvelopePlugin",
|
||||
ExpanderPlugin = "ExpanderPlugin",
|
||||
FilterPlugin = "FilterPlugin",
|
||||
FlangerPlugin = "FlangerPlugin",
|
||||
FunctionPlugin = "FunctionPlugin",
|
||||
GatePlugin = "GatePlugin",
|
||||
GeneratorPlugin = "GeneratorPlugin",
|
||||
HighpassPlugin = "HighpassPlugin",
|
||||
InstrumentPlugin = "InstrumentPlugin",
|
||||
LimiterPlugin = "LimiterPlugin",
|
||||
LowpassPlugin = "LowpassPlugin",
|
||||
MixerPlugin = "MixerPlugin",
|
||||
ModulatorPlugin = "ModulatorPlugin",
|
||||
MultiEQPlugin = "MultiEQPlugin",
|
||||
OscillatorPlugin = "OscillatorPlugin",
|
||||
ParaEQPlugin = "ParaEQPlugin",
|
||||
PhaserPlugin = "PhaserPlugin",
|
||||
PitchPlugin = "PitchPlugin",
|
||||
ReverbPlugin = "ReverbPlugin",
|
||||
SimulatorPlugin = "SimulatorPlugin",
|
||||
SpatialPlugin = "SpatialPlugin",
|
||||
SpectralPlugin = "SpectralPlugin",
|
||||
UtilityPlugin = "UtilityPlugin",
|
||||
WaveshaperPlugin = "WaveshaperPlugin"
|
||||
}
|
||||
|
||||
|
||||
export class UiControl implements Deserializable<UiControl> {
|
||||
deserialize(input: any): UiControl
|
||||
{
|
||||
this.symbol = input.symbol;
|
||||
this.name = input.name;
|
||||
this.index = input.index;
|
||||
this.min_value = input.min_value;
|
||||
this.max_value = input.max_value;
|
||||
this.default_value = input.default_value;
|
||||
this.is_logarithmic = input.is_logarithmic;
|
||||
this.display_priority = input.display_priority;
|
||||
this.range_steps = input.range_steps;
|
||||
this.integer_property = input.integer_property;
|
||||
this.enumeration_property = input.enumeration_property;
|
||||
this.toggled_property = input.toggled_property;
|
||||
this.trigger = input.trigger;
|
||||
this.not_on_gui = input.not_on_gui;
|
||||
this.scale_points = ScalePoint.deserialize_array(input.scale_points);
|
||||
this.port_group = input.port_group;
|
||||
this.units = input.units as Units;
|
||||
return this;
|
||||
|
||||
}
|
||||
static deserialize_array(input: any): UiControl[] {
|
||||
let result: UiControl[] = [];
|
||||
for (let i = 0; i < input.length; ++i)
|
||||
{
|
||||
result[i] = new UiControl().deserialize(input[i]);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
symbol: string = "";
|
||||
name: string = "";
|
||||
index: number = -1;
|
||||
min_value: number = 0;
|
||||
max_value: number = 1;
|
||||
default_value:number = 0.5;
|
||||
is_logarithmic: boolean = false;
|
||||
display_priority: number = -1;
|
||||
range_steps: number = 0;
|
||||
integer_property:boolean = false;
|
||||
enumeration_property: boolean = false;
|
||||
trigger: boolean = false;
|
||||
not_on_gui: boolean = false;
|
||||
toggled_property: boolean = false;
|
||||
scale_points: ScalePoint[] = [];
|
||||
port_group: string = "";
|
||||
units: Units = Units.none;
|
||||
|
||||
isOnOffSwitch() : boolean {
|
||||
if (this.isAbToggle()) return false;
|
||||
if (this.min_value !== 0 || this.max_value !== 1) return false;
|
||||
return (this.toggled_property || this.enumeration_property || this.integer_property);
|
||||
;
|
||||
}
|
||||
|
||||
isAbToggle(): boolean {
|
||||
if (this.min_value !== 0 || this.max_value !== 1) return false;
|
||||
return this.enumeration_property && this.scale_points.length === 2;
|
||||
}
|
||||
isSelect() : boolean {
|
||||
return this.enumeration_property && !this.isOnOffSwitch() && !this.isAbToggle();
|
||||
}
|
||||
|
||||
valueToRange(value: number): number {
|
||||
if (this.toggled_property) return value === 0 ? 0: 1;
|
||||
|
||||
if (this.integer_property || this.enumeration_property) {
|
||||
value = Math.round(value);
|
||||
}
|
||||
let range = (value - this.min_value) / (this.max_value - this.min_value);
|
||||
if (range > 1) range = 1;
|
||||
if (range < 0) range = 0;
|
||||
|
||||
if (this.range_steps !== 0) {
|
||||
range = Math.round(range*this.range_steps)/this.range_steps;
|
||||
}
|
||||
|
||||
return range;
|
||||
}
|
||||
|
||||
rangeToValue(range: number) : number {
|
||||
if (range < 0) range = 0;
|
||||
if (range > 1) range = 1;
|
||||
|
||||
if (this.toggled_property) return range === 0? 0: 1;
|
||||
if (this.range_steps !== 0) {
|
||||
range = Math.round(range * this.range_steps) / this.range_steps;
|
||||
}
|
||||
let value = range * (this.max_value - this.min_value) + this.min_value;
|
||||
if (this.integer_property || this.enumeration_property) {
|
||||
value = Math.round(value);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
clampValue(value: number): number {
|
||||
return this.rangeToValue(this.valueToRange(value));
|
||||
}
|
||||
|
||||
formatValue(value: number): string {
|
||||
if (this.integer_property || this.enumeration_property) {
|
||||
value = Math.round(value);
|
||||
}
|
||||
if (this.enumeration_property) {
|
||||
for (let i = 0; i < this.scale_points.length; ++i) {
|
||||
let scale_point = this.scale_points[i];
|
||||
if (scale_point.value === value) {
|
||||
return scale_point.label;
|
||||
}
|
||||
}
|
||||
return "#invalid";
|
||||
} else if (this.integer_property) {
|
||||
return value.toFixed(0);
|
||||
} else {
|
||||
if (value >= 100 || value <= -100) {
|
||||
return value.toFixed(0);
|
||||
}
|
||||
if (value >= 10 || value <= -10) {
|
||||
return value.toFixed(1);
|
||||
}
|
||||
return value.toFixed(2);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
export class UiPlugin implements Deserializable<UiPlugin> {
|
||||
deserialize(input: any): UiPlugin
|
||||
{
|
||||
this.uri = input.uri;
|
||||
this.name = input.name;
|
||||
this.plugin_type = input.plugin_type as PluginType;
|
||||
this.plugin_display_type = input.plugin_display_type;
|
||||
this.author_name = input.author_name;
|
||||
this.author_homepage = input.author_homepage;
|
||||
this.audio_inputs = input.audio_inputs;
|
||||
this.audio_outputs = input.audio_outputs;
|
||||
this.has_midi_input = input.has_midi_input;
|
||||
this.has_midi_output = input.has_midi_output;
|
||||
this.description = input.description;
|
||||
this.controls = UiControl.deserialize_array(input.controls);
|
||||
this.port_groups = PortGroup.deserialize_array(input.port_groups);
|
||||
return this;
|
||||
|
||||
}
|
||||
static deserialize_array(input: any): UiPlugin[] {
|
||||
let result: UiPlugin[] = [];
|
||||
for (let i = 0; i < input.length; ++i)
|
||||
{
|
||||
result[i] = new UiPlugin().deserialize(input[i]);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
getControl(key: string): UiControl | undefined {
|
||||
for (let i = 0; i < this.controls.length; ++i)
|
||||
{
|
||||
let control = this.controls[i];
|
||||
if (control.symbol === key)
|
||||
{
|
||||
return control;
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
|
||||
}
|
||||
getPortGroup(symbol: string): PortGroup
|
||||
{
|
||||
for (let i = 0; i < this.port_groups.length; ++i)
|
||||
{
|
||||
let port_group = this.port_groups[i];
|
||||
if (port_group.symbol === symbol)
|
||||
{
|
||||
return port_group;
|
||||
}
|
||||
}
|
||||
throw new PiPedalArgumentError("Port group not found.");
|
||||
}
|
||||
|
||||
uri: string = "";
|
||||
name: string = "";
|
||||
plugin_type: PluginType = PluginType.InvalidPlugin;
|
||||
plugin_display_type: string = "";
|
||||
author_name: string = "";
|
||||
author_homepage: string = "";
|
||||
audio_inputs: number = 0;
|
||||
audio_outputs: number = 0;
|
||||
has_midi_input: number = 0;
|
||||
has_midi_output: number = 0;
|
||||
description: string = "";
|
||||
controls: UiControl[] = [];
|
||||
port_groups: PortGroup[] = [];
|
||||
}
|
||||
|
||||
@@ -0,0 +1,452 @@
|
||||
import { SyntheticEvent } from 'react';
|
||||
import { createStyles, withStyles, WithStyles, Theme } from '@material-ui/core/styles';
|
||||
import { PiPedalModel, PiPedalModelFactory } from './PiPedalModel';
|
||||
import {
|
||||
PedalBoard, PedalBoardItem, PedalBoardSplitItem, SplitType
|
||||
} from './PedalBoard';
|
||||
import Button from '@material-ui/core/Button';
|
||||
import InputIcon from '@material-ui/icons/Input';
|
||||
import LoadPluginDialog from './LoadPluginDialog';
|
||||
import Switch from '@material-ui/core/Switch';
|
||||
|
||||
import PedalBoardView from './PedalBoardView';
|
||||
import { PiPedalStateError } from './PiPedalError';
|
||||
import IconButton from '@material-ui/core/IconButton';
|
||||
import AddIcon from '@material-ui/icons/Add';
|
||||
import Menu from '@material-ui/core/Menu';
|
||||
import MenuItem from '@material-ui/core/MenuItem';
|
||||
import Fade from '@material-ui/core/Fade';
|
||||
import Divider from '@material-ui/core/Divider';
|
||||
import ResizeResponsiveComponent from './ResizeResponsiveComponent';
|
||||
import PluginInfoDialog from './PluginInfoDialog';
|
||||
import { GetControlView } from './ControlViewFactory';
|
||||
import MidiBindingsDialog from './MidiBindingsDialog';
|
||||
import PluginPresetSelector from './PluginPresetSelector';
|
||||
|
||||
|
||||
const SPLIT_CONTROLBAR_THRESHHOLD = 650;
|
||||
|
||||
const HORIZONTAL_CONTROL_SCROLL_HEIGHT_BREAK = 500;
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
const HORIZONTAL_LAYOUT_MQ = "@media (max-height: " + HORIZONTAL_CONTROL_SCROLL_HEIGHT_BREAK + "px)";
|
||||
|
||||
const styles = ({ palette }: Theme) => createStyles({
|
||||
frame: {
|
||||
position: "absolute", display: "flex", flexDirection: "column", flexWrap: "nowrap",
|
||||
justifyContent: "flex-start", left: "0px", top: "0px", bottom: "0px", right: "0px", overflow: "hidden"
|
||||
},
|
||||
pedalBoardScroll: {
|
||||
position: "relative", width: "100%",
|
||||
flex: "0 0 auto", overflow: "auto", maxHeight: 220
|
||||
},
|
||||
pedalBoardScrollSmall: {
|
||||
position: "relative", width: "100%",
|
||||
flex: "1 1 1px", overflow: "auto"
|
||||
},
|
||||
separator: {
|
||||
width: "100%", height: "1px", background: "#888", opacity: "0.5",
|
||||
flex: "0 0 1px"
|
||||
},
|
||||
|
||||
controlToolBar: {
|
||||
flex: "0 0 auto", width: "100%", height: 48
|
||||
},
|
||||
splitControlBar: {
|
||||
flex: "0 0 48px", width: "100%", paddingLeft: 24, paddingRight: 16, paddingBottom: 16
|
||||
},
|
||||
controlContent: {
|
||||
flex: "1 1 auto", width: "100%", overflowY: "auto", minHeight: 240
|
||||
},
|
||||
controlContentSmall: {
|
||||
flex: "0 0 162px", width: "100%", height: 162, overflowY: "hidden",
|
||||
},
|
||||
title: { fontSize: "1.3em", fontWeight: 700, marginRight: 8 },
|
||||
author: { fontWeight: 500, marginRight: 8 }
|
||||
});
|
||||
|
||||
|
||||
|
||||
interface MainProps extends WithStyles<typeof styles> {
|
||||
hasTinyToolBar: boolean;
|
||||
theme: Theme;
|
||||
};
|
||||
|
||||
interface MainState {
|
||||
selectedPedal: number;
|
||||
loadDialogOpen: boolean;
|
||||
pedalBoard: PedalBoard;
|
||||
addMenuAnchorEl: HTMLElement | null;
|
||||
splitControlBar: boolean;
|
||||
horizontalScrollLayout: boolean;
|
||||
showMidiBindingsDialog: boolean;
|
||||
screenHeight: number;
|
||||
};
|
||||
|
||||
|
||||
export const MainPage =
|
||||
withStyles(styles, { withTheme: true })(
|
||||
class extends ResizeResponsiveComponent<MainProps, MainState>
|
||||
{
|
||||
model: PiPedalModel;
|
||||
|
||||
constructor(props: MainProps) {
|
||||
super(props);
|
||||
this.model = PiPedalModelFactory.getInstance();
|
||||
|
||||
this.state = {
|
||||
selectedPedal: -1,
|
||||
loadDialogOpen: false,
|
||||
pedalBoard: this.model.pedalBoard.get(),
|
||||
addMenuAnchorEl: null,
|
||||
splitControlBar: this.windowSize.width < SPLIT_CONTROLBAR_THRESHHOLD,
|
||||
horizontalScrollLayout: this.windowSize.height < HORIZONTAL_CONTROL_SCROLL_HEIGHT_BREAK,
|
||||
showMidiBindingsDialog: false,
|
||||
screenHeight: this.windowSize.height,
|
||||
|
||||
};
|
||||
this.onSelectionChanged = this.onSelectionChanged.bind(this);
|
||||
this.onPedalDoubleClick = this.onPedalDoubleClick.bind(this);
|
||||
this.onLoadClick = this.onLoadClick.bind(this);
|
||||
this.onLoadOk = this.onLoadOk.bind(this);
|
||||
this.onLoadCancel = this.onLoadCancel.bind(this);
|
||||
this.onPedalBoardChanged = this.onPedalBoardChanged.bind(this);
|
||||
this.handleEnableCurrentItemChanged = this.handleEnableCurrentItemChanged.bind(this);
|
||||
}
|
||||
|
||||
onInsertPedal(instanceId: number) {
|
||||
this.setAddMenuAnchorEl(null);
|
||||
let newId = this.model.addPedalBoardItem(instanceId, false);
|
||||
this.setSelection(newId);
|
||||
}
|
||||
onAppendPedal(instanceId: number) {
|
||||
this.setAddMenuAnchorEl(null);
|
||||
let newId = this.model.addPedalBoardItem(instanceId, true);
|
||||
|
||||
this.setSelection(newId);
|
||||
|
||||
}
|
||||
onInsertSplit(instanceId: number) {
|
||||
this.setAddMenuAnchorEl(null);
|
||||
let newId = this.model.addPedalBoardSplitItem(instanceId, false);
|
||||
this.setSelection(newId);
|
||||
|
||||
}
|
||||
onAppendSplit(instanceId: number) {
|
||||
this.setAddMenuAnchorEl(null);
|
||||
let newId = this.model.addPedalBoardSplitItem(instanceId, true);
|
||||
this.setSelection(newId);
|
||||
|
||||
}
|
||||
setAddMenuAnchorEl(value: HTMLElement | null) {
|
||||
this.setState({ addMenuAnchorEl: value });
|
||||
}
|
||||
onAddClick(e: SyntheticEvent) {
|
||||
this.setAddMenuAnchorEl(e.currentTarget as HTMLElement);
|
||||
}
|
||||
|
||||
handleMidiBindingsDialogClose() {
|
||||
this.setState({ showMidiBindingsDialog: false })
|
||||
}
|
||||
handleAddClose(): void {
|
||||
this.setAddMenuAnchorEl(null);
|
||||
}
|
||||
|
||||
handleMidiConfiguration(instanceId: number): void {
|
||||
this.setState({ showMidiBindingsDialog: true });
|
||||
}
|
||||
handleEnableCurrentItemChanged(event: any): void {
|
||||
let newValue = event.target.checked;
|
||||
let item = this.getSelectedPedalBoardItem();
|
||||
if (item != null) {
|
||||
this.model.setPedalBoardItemEnabled(item.getInstanceId(), newValue);
|
||||
|
||||
}
|
||||
}
|
||||
handleSelectPluginPreset(instanceId: number, presetName: string) {
|
||||
this.model.loadPluginPreset(instanceId, presetName);
|
||||
}
|
||||
onPedalBoardChanged(value: PedalBoard) {
|
||||
this.setState({ pedalBoard: value });
|
||||
}
|
||||
onDeletePedal(instanceId: number): void {
|
||||
let result = this.model.deletePedalBoardPedal(instanceId);
|
||||
if (result != null) {
|
||||
this.setState({ selectedPedal: result });
|
||||
}
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
super.componentDidMount();
|
||||
this.model.pedalBoard.addOnChangedHandler(this.onPedalBoardChanged);
|
||||
}
|
||||
componentWillUnmount() {
|
||||
this.model.pedalBoard.removeOnChangedHandler(this.onPedalBoardChanged);
|
||||
super.componentWillUnmount();
|
||||
}
|
||||
updateResponsive() {
|
||||
this.setState({
|
||||
splitControlBar: this.windowSize.width < SPLIT_CONTROLBAR_THRESHHOLD,
|
||||
horizontalScrollLayout: this.windowSize.height < HORIZONTAL_CONTROL_SCROLL_HEIGHT_BREAK,
|
||||
screenHeight: this.windowSize.height
|
||||
});
|
||||
}
|
||||
onWindowSizeChanged(width: number, height: number): void {
|
||||
super.onWindowSizeChanged(width, height);
|
||||
this.updateResponsive();
|
||||
}
|
||||
|
||||
|
||||
setSelection(selectedId_: number): void {
|
||||
this.setState({ selectedPedal: selectedId_ });
|
||||
}
|
||||
|
||||
onSelectionChanged(selectedId: number): void {
|
||||
this.setSelection(selectedId);
|
||||
}
|
||||
onPedalDoubleClick(selectedId: number): void {
|
||||
this.setSelection(selectedId);
|
||||
let item = this.getPedalBoardItem(selectedId);
|
||||
if (item != null) {
|
||||
if (item.isSplit()) {
|
||||
let split = item as PedalBoardSplitItem;
|
||||
if (split.getSplitType() === SplitType.Ab) {
|
||||
let cv = split.getToggleAbControlValue();
|
||||
if (split.instanceId === undefined) throw new PiPedalStateError("Split without valid id.");
|
||||
this.model.setPedalBoardControlValue(split.instanceId, cv.key, cv.value);
|
||||
}
|
||||
} else {
|
||||
this.setState({ loadDialogOpen: true });
|
||||
}
|
||||
}
|
||||
}
|
||||
onLoadCancel(): void {
|
||||
this.setState({ loadDialogOpen: false });
|
||||
}
|
||||
onLoadOk(selectedUri: string): void {
|
||||
this.setState({ loadDialogOpen: false });
|
||||
let itemId = this.state.selectedPedal;
|
||||
let newSelectedItem = this.model.loadPedalBoardPlugin(itemId, selectedUri);
|
||||
this.setState({ selectedPedal: newSelectedItem });
|
||||
}
|
||||
|
||||
onLoadClick(e: SyntheticEvent) {
|
||||
this.setState({ loadDialogOpen: true });
|
||||
}
|
||||
|
||||
getPedalBoardItem(selectedId?: number): PedalBoardItem | null {
|
||||
if (selectedId === undefined) return null;
|
||||
|
||||
let pedalBoard = this.model.pedalBoard.get();
|
||||
if (!pedalBoard) return null;
|
||||
let it = pedalBoard.itemsGenerator();
|
||||
if (!selectedId) return null;
|
||||
while (true) {
|
||||
let v = it.next();
|
||||
if (v.done) break;
|
||||
let item = v.value;
|
||||
if (item.instanceId === selectedId) {
|
||||
return item;
|
||||
}
|
||||
|
||||
}
|
||||
return null;
|
||||
|
||||
}
|
||||
|
||||
onPedalboardPropertyChanged(instanceId: number, key: string, value: number) {
|
||||
this.model.setPedalBoardControlValue(instanceId, key, value);
|
||||
}
|
||||
getSelectedPedalBoardItem(): PedalBoardItem | null {
|
||||
return this.getPedalBoardItem(this.state.selectedPedal);
|
||||
}
|
||||
getSelectedUri(): string {
|
||||
let pedalBoardItem = this.getSelectedPedalBoardItem();
|
||||
if (pedalBoardItem == null) return "";
|
||||
return pedalBoardItem.uri;
|
||||
}
|
||||
titleBar(pedalBoardItem: PedalBoardItem | null): React.ReactNode {
|
||||
let title = "";
|
||||
let author = "";
|
||||
let pluginUri = "";
|
||||
let presetsUri = "";
|
||||
if (pedalBoardItem) {
|
||||
if (pedalBoardItem.isEmpty()) {
|
||||
title = "";
|
||||
} else if (pedalBoardItem.isSplit()) {
|
||||
title = "Split";
|
||||
} else {
|
||||
let uiPlugin = this.model.getUiPlugin(pedalBoardItem.uri);
|
||||
if (uiPlugin == null) {
|
||||
title = "Missing Plugin";
|
||||
} else {
|
||||
title = uiPlugin.name;
|
||||
author = uiPlugin.author_name;
|
||||
presetsUri = uiPlugin.uri;
|
||||
if (uiPlugin.description.length > 20) {
|
||||
pluginUri = uiPlugin.uri;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
let classes = this.props.classes;
|
||||
return (
|
||||
<div style={{
|
||||
flex: "1 0 auto", overflow: "hidden", marginRight: 8,
|
||||
display: "flex", flexDirection: "row", flexWrap: "nowrap",
|
||||
alignItems: "center"
|
||||
}}>
|
||||
<div style={{ flex: "0 1 auto" }}>
|
||||
<span style={{ whiteSpace: "nowrap" }}>
|
||||
<span className={classes.title}>{title}</span>
|
||||
</span>
|
||||
<span style={{ whiteSpace: "nowrap" }}>
|
||||
<span className={classes.author}>{author}</span>
|
||||
</span>
|
||||
</div>
|
||||
<div style={{ flex: "0 0 auto" }}>
|
||||
<PluginInfoDialog plugin_uri={pluginUri} />
|
||||
</div>
|
||||
<div style={{ flex: "0 0 auto" }}>
|
||||
<PluginPresetSelector pluginUri={presetsUri}
|
||||
onSelectPreset={(presetName) => this.handleSelectPluginPreset(pedalBoardItem!.instanceId, presetName)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
render() {
|
||||
let classes = this.props.classes;
|
||||
let pedalBoard = this.model.pedalBoard.get();
|
||||
let pedalBoardItem = this.getSelectedPedalBoardItem();
|
||||
let uiPlugin = null;
|
||||
let bypassVisible = false;
|
||||
let bypassChecked = false;
|
||||
let canDelete = false;
|
||||
let canAdd = false;
|
||||
let instanceId = -1;
|
||||
|
||||
if (pedalBoardItem) {
|
||||
canDelete = pedalBoard.canDeleteItem(pedalBoardItem.instanceId);
|
||||
instanceId = pedalBoardItem.instanceId;
|
||||
if (pedalBoardItem.isEmpty()) {
|
||||
canAdd = true;
|
||||
} else if (pedalBoardItem.isSplit()) {
|
||||
canAdd = true;
|
||||
} else {
|
||||
uiPlugin = this.model.getUiPlugin(pedalBoardItem.uri);
|
||||
canAdd = true;
|
||||
if (uiPlugin) {
|
||||
bypassVisible = true;
|
||||
bypassChecked = pedalBoardItem.isEnabled;
|
||||
}
|
||||
}
|
||||
}
|
||||
let horizontalScrollLayout = this.state.horizontalScrollLayout;
|
||||
|
||||
return (
|
||||
<div className={classes.frame}>
|
||||
<div id="pedalBoardScroll" className={horizontalScrollLayout ? classes.pedalBoardScrollSmall : classes.pedalBoardScroll}
|
||||
style={{ maxHeight: horizontalScrollLayout ? undefined : this.state.screenHeight / 2 }}>
|
||||
<PedalBoardView key={uiPlugin?.uri ?? "#error"} selectedId={this.state.selectedPedal}
|
||||
onSelectionChanged={this.onSelectionChanged}
|
||||
onDoubleClick={this.onPedalDoubleClick}
|
||||
hasTinyToolBar={this.props.hasTinyToolBar}
|
||||
/>
|
||||
</div>
|
||||
<div className={classes.separator} />
|
||||
<div className={classes.controlToolBar}>
|
||||
<div style={{
|
||||
display: "flex", flexFlow: "row nowrap", alignItems: "center", justifyContent: "center",
|
||||
width: "100%", height: 48, paddingLeft: 16, paddingRight: 16
|
||||
}} >
|
||||
<div style={{ flex: "0 0 auto", width: 80 }} >
|
||||
<div style={{ display: bypassVisible ? "block" : "none", width: 80 }} >
|
||||
<Switch checked={bypassChecked} onChange={this.handleEnableCurrentItemChanged} />
|
||||
</div>
|
||||
</div>
|
||||
{
|
||||
(!this.state.splitControlBar) && this.titleBar(pedalBoardItem)
|
||||
}
|
||||
<div style={{ flex: "1 1 1px" }}>
|
||||
|
||||
</div>
|
||||
<div style={{ flex: "0 0 auto", display: canAdd ? "block" : "none", paddingRight: 8 }}>
|
||||
<IconButton onClick={(e) => { this.onAddClick(e) }} >
|
||||
<AddIcon />
|
||||
</IconButton>
|
||||
<Menu
|
||||
id="add-menu"
|
||||
anchorEl={this.state.addMenuAnchorEl}
|
||||
keepMounted
|
||||
open={Boolean(this.state.addMenuAnchorEl)}
|
||||
onClose={() => this.handleAddClose()}
|
||||
TransitionComponent={Fade}
|
||||
>
|
||||
<MenuItem onClick={() => this.onInsertPedal(instanceId)}>Insert pedal</MenuItem>
|
||||
<MenuItem onClick={() => this.onAppendPedal(instanceId)}>Append pedal</MenuItem>
|
||||
<Divider />
|
||||
<MenuItem onClick={() => this.onInsertSplit(instanceId)}>Insert split</MenuItem>
|
||||
<MenuItem onClick={() => this.onAppendSplit(instanceId)}>Append split</MenuItem>
|
||||
</Menu>
|
||||
</div>
|
||||
<div style={{ flex: "0 0 auto", display: canDelete ? "block" : "none", paddingRight: 8 }}>
|
||||
<IconButton onClick={() => { this.onDeletePedal(pedalBoardItem?.instanceId ?? -1) }} >
|
||||
<img src="/img/old_delete_outline_black_24dp.svg" alt="Delete" style={{ width: 24, height: 24, opacity: 0.6 }} />
|
||||
</IconButton>
|
||||
</div>
|
||||
<div style={{ flex: "0 0 auto" }}>
|
||||
<Button
|
||||
variant="contained"
|
||||
color="primary"
|
||||
size="small"
|
||||
onClick={this.onLoadClick}
|
||||
disabled={this.state.selectedPedal === -1 || (this.getSelectedPedalBoardItem()?.isSplit() ?? true)}
|
||||
startIcon={<InputIcon />}
|
||||
>
|
||||
Load
|
||||
</Button>
|
||||
</div>
|
||||
<div style={{ flex: "0 0 auto" }}>
|
||||
<IconButton onClick={(e) => { this.handleMidiConfiguration(instanceId); }}>
|
||||
<img src="img/ic_midi.svg" style={{ width: 24, height: 24, opacity: 0.6 }} alt="Midi configuration" />
|
||||
</IconButton>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{
|
||||
this.state.splitControlBar && (
|
||||
<div className={classes.splitControlBar}>
|
||||
{
|
||||
this.titleBar(pedalBoardItem)
|
||||
}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
<div className={horizontalScrollLayout ? classes.controlContentSmall : classes.controlContent}>
|
||||
{
|
||||
GetControlView(pedalBoardItem)
|
||||
}
|
||||
|
||||
</div>
|
||||
<MidiBindingsDialog open={this.state.showMidiBindingsDialog}
|
||||
onClose={()=> this.setState({showMidiBindingsDialog: false} ) }
|
||||
/>
|
||||
{
|
||||
(this.state.loadDialogOpen) && (
|
||||
<LoadPluginDialog open={this.state.loadDialogOpen} uri={this.getSelectedUri()}
|
||||
onOk={this.onLoadOk} onCancel={this.onLoadCancel}
|
||||
/>
|
||||
|
||||
)
|
||||
}
|
||||
</div>
|
||||
|
||||
);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
export default MainPage
|
||||
@@ -0,0 +1,60 @@
|
||||
|
||||
|
||||
export default class MidiBinding {
|
||||
deserialize(input: any) : MidiBinding {
|
||||
this.symbol = input.symbol;
|
||||
this.bindingType = input.bindingType;
|
||||
this.note = input.note;
|
||||
this.control = input.control;
|
||||
this.minValue = input.minValue;
|
||||
this.maxValue = input.maxValue;
|
||||
this.linearControlType = input.linearControlType;
|
||||
this.switchControlType = input.switchControlType;
|
||||
return this;
|
||||
}
|
||||
static deserialize_array(input: any): MidiBinding[] {
|
||||
let result: MidiBinding[] = [];
|
||||
for (let i = 0; i < input.length; ++i)
|
||||
{
|
||||
result.push(new MidiBinding().deserialize(input[i]));
|
||||
}
|
||||
return result;
|
||||
|
||||
}
|
||||
clone(): MidiBinding { return new MidiBinding().deserialize(this);}
|
||||
equals(other: MidiBinding) : boolean
|
||||
{
|
||||
return (this.symbol === other.symbol)
|
||||
&& (this.bindingType === other.bindingType)
|
||||
&& (this.note === other.note)
|
||||
&& (this.control === other.control)
|
||||
&& (this.minValue === other.minValue)
|
||||
&& (this.maxValue === other.maxValue)
|
||||
&& (this.linearControlType === other.linearControlType)
|
||||
&& (this.switchControlType === other.switchControlType)
|
||||
}
|
||||
|
||||
static BINDING_TYPE_NONE: number = 0;
|
||||
static BINDING_TYPE_NOTE: number = 1;
|
||||
static BINDING_TYPE_CONTROL: number = 2;
|
||||
|
||||
symbol: string = "";
|
||||
|
||||
bindingType: number = MidiBinding.BINDING_TYPE_NONE;
|
||||
note: number = 12*4+24; // C4.
|
||||
control: number = 1;
|
||||
minValue: number = 0;
|
||||
maxValue: number = 1;
|
||||
rotaryScale: number = 1;
|
||||
|
||||
static LINEAR_CONTROL_TYPE:number = 0;
|
||||
static CIRCULAR_CONTROL_TYPE: number = 1;
|
||||
|
||||
linearControlType: number = MidiBinding.LINEAR_CONTROL_TYPE;
|
||||
|
||||
static LATCH_CONTROL_TYPE: number = 0;
|
||||
static MOMENTARY_CONTROL_TYPE: number = 1;
|
||||
|
||||
switchControlType: number = MidiBinding.LATCH_CONTROL_TYPE;
|
||||
|
||||
};
|
||||
@@ -0,0 +1,299 @@
|
||||
import { Component } from 'react';
|
||||
import { PiPedalModel, PiPedalModelFactory } from './PiPedalModel';
|
||||
import { Theme, withStyles, WithStyles, createStyles } from '@material-ui/core/styles';
|
||||
import MenuItem from '@material-ui/core/MenuItem';
|
||||
import Select from '@material-ui/core/Select';
|
||||
import MidiBinding from './MidiBinding';
|
||||
import Utility, { nullCast } from './Utility';
|
||||
import Typography from '@material-ui/core/Typography';
|
||||
import MicNoneOutlinedIcon from '@material-ui/icons/MicNoneOutlined';
|
||||
import MicOutlinedIcon from '@material-ui/icons/MicOutlined';
|
||||
import IconButton from '@material-ui/core/IconButton';
|
||||
import NumericInput from './NumericInput';
|
||||
|
||||
|
||||
|
||||
const styles = (theme: Theme) => createStyles({
|
||||
controlDiv: { flex: "0 0 auto", marginRight: 12,verticalAlign: "center", height: 48, paddingTop: 8, paddingBottom: 8 },
|
||||
controlDiv2: { flex: "0 0 auto", marginRight: 12,verticalAlign: "center",
|
||||
height: 48, paddingTop: 0, paddingBottom: 0, whiteSpace: "nowrap" }
|
||||
});
|
||||
|
||||
interface MidiBindingViewProps extends WithStyles<typeof styles> {
|
||||
instanceId: number;
|
||||
listen: boolean;
|
||||
midiBinding: MidiBinding;
|
||||
onChange: (instanceId: number, newBinding: MidiBinding) => void;
|
||||
onListen: (instanceId: number, key: string, listenForControl: boolean) => void;
|
||||
};
|
||||
|
||||
|
||||
interface MidiBindingViewState {
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
const MidiBindingView =
|
||||
withStyles(styles, { withTheme: true })(
|
||||
class extends Component<MidiBindingViewProps, MidiBindingViewState> {
|
||||
|
||||
model: PiPedalModel;
|
||||
|
||||
constructor(props: MidiBindingViewProps) {
|
||||
super(props);
|
||||
this.model = PiPedalModelFactory.getInstance();
|
||||
this.state = {
|
||||
};
|
||||
}
|
||||
|
||||
handleTypeChange(e: any, extra: any) {
|
||||
let newValue = parseInt(e.target.value);
|
||||
let newBinding = this.props.midiBinding.clone();
|
||||
newBinding.bindingType = newValue;
|
||||
this.props.onChange(this.props.instanceId, newBinding);
|
||||
}
|
||||
handleNoteChange(e: any, extra: any) {
|
||||
let newValue = parseInt(e.target.value);
|
||||
let newBinding = this.props.midiBinding.clone();
|
||||
newBinding.note = newValue;
|
||||
this.props.onChange(this.props.instanceId, newBinding);
|
||||
}
|
||||
handleControlChange(e: any, extra: any) {
|
||||
let newValue = parseInt(e.target.value);
|
||||
let newBinding = this.props.midiBinding.clone();
|
||||
newBinding.control = newValue;
|
||||
this.props.onChange(this.props.instanceId, newBinding);
|
||||
}
|
||||
handleLatchControlTypeChange(e: any, extra: any) {
|
||||
let newValue = parseInt(e.target.value);
|
||||
let newBinding = this.props.midiBinding.clone();
|
||||
newBinding.switchControlType = newValue;
|
||||
this.props.onChange(this.props.instanceId, newBinding);
|
||||
}
|
||||
handleLinearControlTypeChange(e: any, extra: any) {
|
||||
let newValue = parseInt(e.target.value);
|
||||
let newBinding = this.props.midiBinding.clone();
|
||||
newBinding.linearControlType = newValue;
|
||||
this.props.onChange(this.props.instanceId, newBinding);
|
||||
}
|
||||
handleMinChange(value: number): void {
|
||||
let newBinding = this.props.midiBinding.clone();
|
||||
newBinding.minValue = value;
|
||||
this.props.onChange(this.props.instanceId, newBinding);
|
||||
}
|
||||
handleMaxChange(value: number): void {
|
||||
let newBinding = this.props.midiBinding.clone();
|
||||
newBinding.maxValue = value;
|
||||
this.props.onChange(this.props.instanceId, newBinding);
|
||||
}
|
||||
handleScaleChange(value: number): void {
|
||||
let newBinding = this.props.midiBinding.clone();
|
||||
newBinding.rotaryScale = value;
|
||||
this.props.onChange(this.props.instanceId, newBinding);
|
||||
}
|
||||
|
||||
|
||||
generateMidiSelects(): React.ReactNode[] {
|
||||
let result: React.ReactNode[] = [];
|
||||
|
||||
for (let i = 0; i < 127; ++i) {
|
||||
result.push(
|
||||
<MenuItem value={i}>{Utility.midiNoteName(i)}</MenuItem>
|
||||
)
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
generateControlSelects(): React.ReactNode[] {
|
||||
|
||||
return Utility.validMidiControllers.map((control) => (
|
||||
<MenuItem value={control.value}>{control.displayName}</MenuItem>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
render() {
|
||||
let classes = this.props.classes;
|
||||
let midiBinding = this.props.midiBinding;
|
||||
let pedalBoardItem = this.model.pedalBoard.get().getItem(this.props.instanceId);
|
||||
let uiPlugin = this.model.getUiPlugin(pedalBoardItem.uri);
|
||||
if (!uiPlugin) {
|
||||
return (<div />);
|
||||
}
|
||||
|
||||
let canLatch: boolean = false;
|
||||
let canTrigger: boolean = false;
|
||||
let showLinearControlTypeSelect: boolean = false;
|
||||
let isBinaryControl: boolean = false;
|
||||
let showLinearRange: boolean = false;
|
||||
let canRotaryScale: boolean = false;
|
||||
if (this.props.midiBinding.symbol === "__bypass") {
|
||||
isBinaryControl = true;
|
||||
canLatch = midiBinding.bindingType !== MidiBinding.BINDING_TYPE_NONE;
|
||||
} else {
|
||||
let port = nullCast(uiPlugin!.getControl(this.props.midiBinding.symbol));
|
||||
isBinaryControl = (port.isAbToggle() || port.isOnOffSwitch());
|
||||
if (midiBinding.bindingType !== MidiBinding.BINDING_TYPE_NONE) {
|
||||
canLatch = isBinaryControl;
|
||||
canTrigger = port.trigger;
|
||||
showLinearControlTypeSelect = !(canLatch || canTrigger);
|
||||
showLinearRange = showLinearControlTypeSelect && midiBinding.linearControlType === MidiBinding.LINEAR_CONTROL_TYPE;
|
||||
canRotaryScale = showLinearControlTypeSelect && midiBinding.linearControlType === MidiBinding.CIRCULAR_CONTROL_TYPE;
|
||||
}
|
||||
}
|
||||
return (
|
||||
<div style={{ display: "flex", flexDirection: "row", flexWrap: "wrap", justifyContent: "left", alignItems: "center", minHeight: 48 }}>
|
||||
<div className={classes.controlDiv} >
|
||||
<Select
|
||||
style={{ width: 80, }}
|
||||
onChange={(e, extra) => this.handleTypeChange(e, extra)}
|
||||
value={midiBinding.bindingType}
|
||||
>
|
||||
<MenuItem value={0}>None</MenuItem>
|
||||
{(isBinaryControl) && (
|
||||
<MenuItem value={1}>Note</MenuItem>
|
||||
)}
|
||||
<MenuItem value={2}>Control</MenuItem>
|
||||
</Select>
|
||||
</div>
|
||||
{
|
||||
(midiBinding.bindingType === MidiBinding.BINDING_TYPE_NOTE) &&
|
||||
(
|
||||
<div className={classes.controlDiv2} >
|
||||
<Select
|
||||
style={{ width:80}}
|
||||
onChange={(e, extra) => this.handleNoteChange(e, extra)}
|
||||
value={midiBinding.note}
|
||||
|
||||
>
|
||||
{
|
||||
this.generateMidiSelects()
|
||||
}
|
||||
</Select>
|
||||
<IconButton onClick={()=> {
|
||||
if (this.props.listen)
|
||||
{
|
||||
this.props.onListen(-2, "", false)
|
||||
} else {
|
||||
this.props.onListen(this.props.instanceId, this.props.midiBinding.symbol, false)
|
||||
}
|
||||
}}>
|
||||
{ this.props.listen ? (
|
||||
<MicOutlinedIcon />
|
||||
) : (
|
||||
<MicNoneOutlinedIcon />
|
||||
)}
|
||||
</IconButton>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
{
|
||||
(midiBinding.bindingType === MidiBinding.BINDING_TYPE_CONTROL) &&
|
||||
(
|
||||
<div className={classes.controlDiv2} >
|
||||
|
||||
<Select
|
||||
style={{ width: 80 }}
|
||||
onChange={(e, extra) => this.handleControlChange(e, extra)}
|
||||
value={midiBinding.control}
|
||||
renderValue={(value)=> { return "CC-" + value}}
|
||||
>
|
||||
{
|
||||
this.generateControlSelects()
|
||||
}
|
||||
</Select>
|
||||
<IconButton onClick={()=> {
|
||||
if (this.props.listen)
|
||||
{
|
||||
this.props.onListen(-2, "", false)
|
||||
} else {
|
||||
this.props.onListen(this.props.instanceId, this.props.midiBinding.symbol, true)
|
||||
}
|
||||
}}>
|
||||
{ this.props.listen ? (
|
||||
<MicOutlinedIcon />
|
||||
) : (
|
||||
<MicNoneOutlinedIcon />
|
||||
)}
|
||||
</IconButton>
|
||||
|
||||
</div>
|
||||
)
|
||||
}
|
||||
{
|
||||
((canLatch) &&
|
||||
(
|
||||
<div className={classes.controlDiv} >
|
||||
<Select
|
||||
style={{ width: 120 }}
|
||||
onChange={(e, extra) => this.handleLatchControlTypeChange(e, extra)}
|
||||
value={midiBinding.switchControlType}
|
||||
>
|
||||
<MenuItem value={MidiBinding.LATCH_CONTROL_TYPE}>Latch</MenuItem>
|
||||
<MenuItem value={MidiBinding.MOMENTARY_CONTROL_TYPE}>Momentary</MenuItem>
|
||||
</Select>
|
||||
</div>
|
||||
))
|
||||
}
|
||||
{
|
||||
(canTrigger) &&
|
||||
(
|
||||
<div className={classes.controlDiv2} >
|
||||
<Typography style={{paddingTop: 12, paddingBottom: 12}}>(Trigger)</Typography>
|
||||
</div>
|
||||
)
|
||||
|
||||
}
|
||||
{
|
||||
(showLinearControlTypeSelect) &&
|
||||
(
|
||||
<div className={classes.controlDiv} >
|
||||
<Select
|
||||
style={{ width: 120 }}
|
||||
onChange={(e, extra) => this.handleLinearControlTypeChange(e, extra)}
|
||||
value={midiBinding.linearControlType}
|
||||
>
|
||||
<MenuItem value={MidiBinding.LATCH_CONTROL_TYPE}>Linear</MenuItem>
|
||||
<MenuItem value={MidiBinding.MOMENTARY_CONTROL_TYPE}>Rotary</MenuItem>
|
||||
</Select>
|
||||
</div>
|
||||
|
||||
)
|
||||
}
|
||||
{
|
||||
showLinearRange && (
|
||||
<div className={classes.controlDiv}>
|
||||
<Typography display="inline">Min: </Typography>
|
||||
<NumericInput defaultValue={midiBinding.minValue} ariaLabel='min'
|
||||
min={0} max={1} onChange={(value) => { this.handleMinChange(value); } }
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
{
|
||||
showLinearRange && (
|
||||
<div className={classes.controlDiv}>
|
||||
<Typography display="inline">Max: </Typography>
|
||||
<NumericInput defaultValue={midiBinding.maxValue} ariaLabel='max'
|
||||
min={0} max={1} onChange={(value) => { this.handleMaxChange(value); } } />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
{
|
||||
canRotaryScale && (
|
||||
<div className={classes.controlDiv}>
|
||||
<Typography display="inline">Scale: </Typography>
|
||||
<NumericInput defaultValue={midiBinding.maxValue} ariaLabel='scale'
|
||||
min={-100} max={100} onChange={(value) => { this.handleScaleChange(value); } } />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
)
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
export default MidiBindingView;
|
||||
@@ -0,0 +1,343 @@
|
||||
import React, { SyntheticEvent } from 'react';
|
||||
import Dialog from '@material-ui/core/Dialog';
|
||||
import ResizeResponsiveComponent from './ResizeResponsiveComponent';
|
||||
import { PiPedalModel, PiPedalModelFactory, ListenHandle } from './PiPedalModel';
|
||||
import Typography from '@material-ui/core/Typography';
|
||||
import { createStyles, withStyles, WithStyles, Theme } from '@material-ui/core/styles';
|
||||
import AppBar from '@material-ui/core/AppBar';
|
||||
import Toolbar from '@material-ui/core/Toolbar';
|
||||
import ArrowBackIcon from '@material-ui/icons/ArrowBack';
|
||||
import IconButton from '@material-ui/core/IconButton';
|
||||
import MidiBinding from './MidiBinding';
|
||||
import MidiBindingView from './MidiBindingView';
|
||||
import Snackbar from '@material-ui/core/Snackbar';
|
||||
|
||||
const styles = (theme: Theme) => createStyles({
|
||||
dialogAppBar: {
|
||||
position: 'relative',
|
||||
top: 0, left: 0
|
||||
},
|
||||
dialogTitle: {
|
||||
marginLeft: theme.spacing(2),
|
||||
flex: "1 1 auto",
|
||||
},
|
||||
pluginTable: {
|
||||
border: "collapse",
|
||||
width: "100%",
|
||||
|
||||
},
|
||||
pluginHead: {
|
||||
borderTop: "1pt #DDD solid", paddingLeft: 22, paddingRight: 22
|
||||
},
|
||||
bindingTd: {
|
||||
verticalAlign: "top",
|
||||
paddingLeft: 12, paddingBottom: 8
|
||||
},
|
||||
nameTd: {
|
||||
paddingLeft: 48,
|
||||
verticalAlign: "top",
|
||||
paddingTop: 12
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
export interface MidiBindingDialogProps extends WithStyles<typeof styles> {
|
||||
open: boolean,
|
||||
onClose: () => void
|
||||
};
|
||||
|
||||
export interface MidiBindingDialogState {
|
||||
listenInstanceId: number;
|
||||
listenSymbol: string;
|
||||
listenSnackbarOpen: boolean;
|
||||
|
||||
};
|
||||
|
||||
export const MidiBindingDialog =
|
||||
withStyles(styles, { withTheme: true })(
|
||||
class extends ResizeResponsiveComponent<MidiBindingDialogProps, MidiBindingDialogState> {
|
||||
|
||||
model: PiPedalModel;
|
||||
|
||||
constructor(props: MidiBindingDialogProps) {
|
||||
super(props);
|
||||
this.state = {
|
||||
listenInstanceId: -2,
|
||||
listenSymbol: "",
|
||||
listenSnackbarOpen: false
|
||||
};
|
||||
this.model = PiPedalModelFactory.getInstance();
|
||||
this.handlePopState = this.handlePopState.bind(this);
|
||||
this.handleClose = this.handleClose.bind(this);
|
||||
}
|
||||
mounted: boolean = false;
|
||||
|
||||
hasHooks: boolean = false;
|
||||
|
||||
stateWasPopped: boolean = false;
|
||||
handlePopState(e: any): any {
|
||||
this.stateWasPopped = true;
|
||||
let shouldClose = (!e.state);
|
||||
if (shouldClose) {
|
||||
this.props.onClose();
|
||||
}
|
||||
}
|
||||
handleClose() {
|
||||
this.props.onClose();
|
||||
}
|
||||
|
||||
listenTimeoutHandle?: NodeJS.Timeout;
|
||||
|
||||
listenHandle?: ListenHandle;
|
||||
|
||||
cancelListenForControl() {
|
||||
if (this.listenTimeoutHandle) {
|
||||
clearTimeout(this.listenTimeoutHandle);
|
||||
this.listenTimeoutHandle = undefined;
|
||||
}
|
||||
if (this.listenHandle)
|
||||
{
|
||||
this.model.cancelListenForMidiEvent(this.listenHandle)
|
||||
this.listenHandle = undefined;
|
||||
}
|
||||
|
||||
this.setState({ listenInstanceId: -2, listenSymbol: "" });
|
||||
|
||||
}
|
||||
|
||||
handleListenSucceeded(instanceId: number, symbol: string, isNote: boolean, noteOrControl: number)
|
||||
{
|
||||
this.listenHandle = undefined; // (one-shot event)
|
||||
this.cancelListenForControl();
|
||||
|
||||
let pedalBoard = this.model.pedalBoard.get();
|
||||
let item = pedalBoard.getItem(instanceId);
|
||||
if (!item) return;
|
||||
|
||||
let binding = item.getMidiBinding(symbol);
|
||||
let newBinding = binding.clone();
|
||||
if (isNote) {
|
||||
newBinding.bindingType = MidiBinding.BINDING_TYPE_NOTE;
|
||||
newBinding.note = noteOrControl;
|
||||
} else {
|
||||
newBinding.bindingType = MidiBinding.BINDING_TYPE_CONTROL;
|
||||
newBinding.control = noteOrControl;
|
||||
}
|
||||
|
||||
this.model.setMidiBinding(instanceId,newBinding);
|
||||
}
|
||||
|
||||
|
||||
handleListenForControl(instanceId: number, symbol: string, listenForControl: boolean): void {
|
||||
this.cancelListenForControl();
|
||||
this.setState({ listenInstanceId: instanceId, listenSymbol: symbol, listenSnackbarOpen: true });
|
||||
this.listenTimeoutHandle = setTimeout(() => {
|
||||
this.cancelListenForControl();
|
||||
}, 8000);
|
||||
|
||||
this.listenHandle = this.model.listenForMidiEvent(listenForControl,
|
||||
(isNote: boolean, noteOrControl: number) => {
|
||||
this.handleListenSucceeded(instanceId,symbol,isNote, noteOrControl);
|
||||
});
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
updateHooks(): void {
|
||||
let wantHooks = this.mounted && this.props.open;
|
||||
if (wantHooks !== this.hasHooks) {
|
||||
this.hasHooks = wantHooks;
|
||||
|
||||
if (this.hasHooks) {
|
||||
this.stateWasPopped = false;
|
||||
window.addEventListener("popstate", this.handlePopState);
|
||||
// eslint-disable-next-line no-restricted-globals
|
||||
let state = history.state;
|
||||
if (!state) {
|
||||
state = {};
|
||||
}
|
||||
state.midiBindingDialog = true;
|
||||
|
||||
// eslint-disable-next-line no-restricted-globals
|
||||
history.pushState(
|
||||
state,
|
||||
"Preset MIDI Bindings",
|
||||
"#MidiBindingDialog"
|
||||
);
|
||||
} else {
|
||||
window.removeEventListener("popstate", this.handlePopState);
|
||||
if (!this.stateWasPopped) {
|
||||
// eslint-disable-next-line no-restricted-globals
|
||||
history.back();
|
||||
}
|
||||
this.cancelListenForControl();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
onWindowSizeChanged(width: number, height: number): void {
|
||||
}
|
||||
|
||||
|
||||
componentDidMount() {
|
||||
super.componentDidMount();
|
||||
this.mounted = true;
|
||||
this.updateHooks();
|
||||
}
|
||||
componentWillUnmount() {
|
||||
super.componentDidMount();
|
||||
|
||||
this.mounted = false;
|
||||
this.updateHooks();
|
||||
|
||||
}
|
||||
|
||||
componentDidUpdate() {
|
||||
this.updateHooks();
|
||||
}
|
||||
handleItemChanged(instanceId: number, newBinding: MidiBinding) {
|
||||
this.model.setMidiBinding(instanceId, newBinding);
|
||||
}
|
||||
generateTable(): React.ReactNode[] {
|
||||
let classes = this.props.classes;
|
||||
let result: React.ReactNode[] = [];
|
||||
|
||||
let pedalboard = this.model.pedalBoard.get();
|
||||
let iter = pedalboard.itemsGenerator();
|
||||
while (true) {
|
||||
let v = iter.next();
|
||||
if (v.done) break;
|
||||
let item = v.value;
|
||||
|
||||
|
||||
let plugin = this.model.getUiPlugin(item.uri);
|
||||
if (plugin) {
|
||||
result.push(
|
||||
<tr>
|
||||
<td colSpan={2} className={classes.pluginHead}>
|
||||
<Typography variant="caption" color="textSecondary" noWrap>
|
||||
{plugin.name}
|
||||
</Typography>
|
||||
</td>
|
||||
</tr>
|
||||
);
|
||||
result.push(
|
||||
<tr>
|
||||
<td className={classes.nameTd}>
|
||||
<Typography noWrap style={{ verticalAlign: "center", height: 48 }}>
|
||||
Bypass
|
||||
</Typography>
|
||||
</td>
|
||||
<td className={classes.bindingTd}>
|
||||
<MidiBindingView instanceId={item.instanceId} midiBinding={item.getMidiBinding("__bypass")}
|
||||
onListen={(instanceId: number, symbol: string, listenForControl: boolean) => {
|
||||
if (instanceId === -2)
|
||||
{
|
||||
this.cancelListenForControl();
|
||||
} else {
|
||||
this.handleListenForControl(instanceId, symbol, listenForControl);
|
||||
}
|
||||
}}
|
||||
listen={item.instanceId === this.state.listenInstanceId && this.state.listenSymbol === "__bypass"}
|
||||
onChange={(instanceId: number, newItem: MidiBinding) => this.handleItemChanged(instanceId, newItem)}
|
||||
/>
|
||||
</td>
|
||||
</tr>
|
||||
);
|
||||
|
||||
for (let i = 0; i < plugin.controls.length; ++i) {
|
||||
let control = plugin.controls[i];
|
||||
let symbol = control.symbol;
|
||||
result.push(
|
||||
<tr>
|
||||
<td className={classes.nameTd}>
|
||||
<Typography noWrap style={{ verticalAlign: "middle", maxWidth: 120 }} >
|
||||
{control.name}
|
||||
</Typography>
|
||||
</td>
|
||||
<td className={classes.bindingTd}>
|
||||
<MidiBindingView instanceId={item.instanceId}
|
||||
midiBinding={item.getMidiBinding(symbol)}
|
||||
listen={item.instanceId === this.state.listenInstanceId && this.state.listenSymbol === symbol}
|
||||
onListen={(instanceId: number, symbol: string, listenForControl: boolean) => {
|
||||
this.handleListenForControl(instanceId, symbol, listenForControl);
|
||||
}}
|
||||
|
||||
onChange={(instanceId: number, newItem: MidiBinding) => this.handleItemChanged(instanceId, newItem)}
|
||||
/>
|
||||
</td>
|
||||
</tr>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
supressDefault(e: SyntheticEvent) {
|
||||
//e.preventDefault();
|
||||
//e.stopPropagation();
|
||||
}
|
||||
|
||||
render() {
|
||||
let props = this.props;
|
||||
let { open, classes } = props;
|
||||
if (!open) {
|
||||
return (<div />);
|
||||
}
|
||||
|
||||
return (
|
||||
<Dialog open={open} fullWidth onClose={this.handleClose} aria-labelledby="Rename-dialog-title"
|
||||
fullScreen={true}
|
||||
style={{userSelect: "none"}}
|
||||
>
|
||||
<div style={{ display: "flex", flexDirection: "column", flexWrap: "nowrap", width: "100%", height: "100%", overflow: "hidden" }}>
|
||||
<div style={{ flex: "0 0 auto" }}>
|
||||
<AppBar className={classes.dialogAppBar} >
|
||||
<Toolbar>
|
||||
<IconButton edge="start" color="inherit" onClick={this.handleClose} aria-label="back"
|
||||
>
|
||||
<ArrowBackIcon />
|
||||
</IconButton>
|
||||
<Typography variant="h6" className={classes.dialogTitle}>
|
||||
Preset MIDI Bindings
|
||||
</Typography>
|
||||
</Toolbar>
|
||||
</AppBar>
|
||||
</div>
|
||||
<div style={{ overflow: "auto", flex: "1 1 auto", width: "100%" }}>
|
||||
<table className={classes.pluginTable} >
|
||||
<colgroup>
|
||||
<col style={{ width: "auto" }} />
|
||||
<col style={{ width: "100%" }} />
|
||||
</colgroup>
|
||||
|
||||
<tbody>
|
||||
{this.generateTable()}
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<Snackbar
|
||||
anchorOrigin={{
|
||||
vertical: 'bottom',
|
||||
horizontal: 'left',
|
||||
}}
|
||||
open={this.state.listenSnackbarOpen}
|
||||
autoHideDuration={1500}
|
||||
onClose={() => this.setState({ listenSnackbarOpen: false })}
|
||||
message="Listening for MIDI input"
|
||||
/>
|
||||
</Dialog >
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
export default MidiBindingDialog;
|
||||
@@ -0,0 +1,131 @@
|
||||
import React, { Component } from 'react';
|
||||
import { createStyles, withStyles, WithStyles, Theme } from '@material-ui/core/styles';
|
||||
import Input from '@material-ui/core/Input';
|
||||
|
||||
|
||||
|
||||
const styles = ({ palette }: Theme) => createStyles({
|
||||
|
||||
});
|
||||
|
||||
|
||||
|
||||
interface NumericInputProps extends WithStyles<typeof styles> {
|
||||
ariaLabel: string;
|
||||
defaultValue: number;
|
||||
min: number;
|
||||
max: number;
|
||||
onChange: (value: number) => void;
|
||||
};
|
||||
|
||||
interface NumericInputState {
|
||||
error: boolean;
|
||||
};
|
||||
|
||||
export const NumericInput =
|
||||
withStyles(styles, { withTheme: true })(
|
||||
class extends Component<NumericInputProps, NumericInputState>
|
||||
{
|
||||
|
||||
|
||||
constructor(props: NumericInputProps) {
|
||||
super(props);
|
||||
this.state = {
|
||||
error: false
|
||||
};
|
||||
}
|
||||
|
||||
changed: boolean = false;
|
||||
|
||||
handleChange(event: any): void {
|
||||
this.changed = true;
|
||||
let strValue = event.target.value;
|
||||
try {
|
||||
let value = Number(strValue);
|
||||
if (isNaN(value))
|
||||
{
|
||||
this.setState({ error: true });
|
||||
} else if (value >= this.props.min && value <= this.props.max) {
|
||||
this.setState({ error: false });
|
||||
} else {
|
||||
this.setState({ error: true });
|
||||
}
|
||||
} catch (error) {
|
||||
this.setState({ error: true });
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
toDisplayValue(value: number): string {
|
||||
if (value <= -1000 || value >= 1000)
|
||||
{
|
||||
return value.toFixed(0);
|
||||
}
|
||||
if (value <= -100 || value >= 100)
|
||||
{
|
||||
return value.toFixed(1);
|
||||
}
|
||||
if (value <= -10 || value >= 10)
|
||||
{
|
||||
return value.toFixed(2);
|
||||
} else {
|
||||
return value.toFixed(3);
|
||||
}
|
||||
}
|
||||
|
||||
apply(input: HTMLInputElement) {
|
||||
if (this.changed) {
|
||||
let strValue = input.value;
|
||||
let value = Number(strValue);
|
||||
if (!isNaN(value))
|
||||
{
|
||||
if (value < this.props.min) value = this.props.min;
|
||||
if (value > this.props.max) value = this.props.max;
|
||||
input.value = this.toDisplayValue(value);
|
||||
this.props.onChange(value);
|
||||
} else {
|
||||
input.value = this.toDisplayValue(this.props.defaultValue);
|
||||
}
|
||||
this.changed = false;
|
||||
this.setState({ error: false });
|
||||
}
|
||||
}
|
||||
|
||||
handleLostFocus(e: any) {
|
||||
this.apply(e.currentTarget as HTMLInputElement);
|
||||
}
|
||||
handleKeyDown(e: React.KeyboardEvent<HTMLInputElement>) {
|
||||
if (e.key === "Enter")
|
||||
{
|
||||
this.apply(e.currentTarget as HTMLInputElement);
|
||||
} else if (e.key === "Escape")
|
||||
{
|
||||
e.currentTarget.value = this.toDisplayValue(this.props.defaultValue);
|
||||
this.changed = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
render() {
|
||||
//let classes = this.props.classes;
|
||||
return (<Input style={{ width: 50 }}
|
||||
inputProps={{
|
||||
'aria-label': this.props.ariaLabel,
|
||||
style: { textAlign: 'right' },
|
||||
"onBlur": (e: any) => this.handleLostFocus(e),
|
||||
"onKeyDown": (e: any) => this.handleKeyDown(e)
|
||||
}}
|
||||
onChange={(event) => this.handleChange(event)}
|
||||
defaultValue={this.toDisplayValue(this.props.defaultValue)}
|
||||
error={this.state.error}
|
||||
/>
|
||||
|
||||
);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
export default NumericInput;
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
|
||||
|
||||
export type OnChangedHandler<VALUE_TYPE> = (newValue: VALUE_TYPE) => void;
|
||||
|
||||
|
||||
export class ObservableProperty<VALUE_TYPE> {
|
||||
|
||||
_value_type: VALUE_TYPE;
|
||||
_on_changed_handlers: OnChangedHandler<VALUE_TYPE>[] = [];
|
||||
|
||||
constructor(default_value: VALUE_TYPE)
|
||||
{
|
||||
this._value_type = default_value;
|
||||
}
|
||||
|
||||
addOnChangedHandler(handler: OnChangedHandler<VALUE_TYPE> ) : void
|
||||
{
|
||||
this._on_changed_handlers.push(handler);
|
||||
handler(this._value_type);
|
||||
}
|
||||
removeOnChangedHandler(handler: OnChangedHandler<VALUE_TYPE> ) : void
|
||||
{
|
||||
let oldArray = this._on_changed_handlers;
|
||||
|
||||
let newArray: OnChangedHandler<VALUE_TYPE>[] = [];
|
||||
|
||||
let outIx = 0;
|
||||
for (let i = 0; i < oldArray.length; ++i)
|
||||
{
|
||||
if (oldArray[i] !== handler)
|
||||
{
|
||||
newArray[outIx++] = oldArray[i];
|
||||
}
|
||||
}
|
||||
this._on_changed_handlers = newArray;
|
||||
}
|
||||
|
||||
get() : VALUE_TYPE {
|
||||
return this._value_type;
|
||||
}
|
||||
|
||||
set(value: VALUE_TYPE) : void
|
||||
{
|
||||
this._value_type = value;
|
||||
|
||||
let t = this._on_changed_handlers; // take an copy in case removes happen while iteratiing.
|
||||
t.forEach(c => c(value));
|
||||
}
|
||||
|
||||
};
|
||||
@@ -0,0 +1,40 @@
|
||||
import React, { Component, ReactNode } from 'react';
|
||||
import { withStyles, createStyles, WithStyles } from '@material-ui/core';
|
||||
import { Theme } from '@material-ui/core/styles/createMuiTheme';
|
||||
import { Lv2Plugin } from './Lv2Plugin'
|
||||
|
||||
|
||||
|
||||
const pedalStyles = ({ palette }: Theme) => createStyles({
|
||||
});
|
||||
|
||||
interface PedalProps extends WithStyles<typeof pedalStyles> {
|
||||
plugin: Lv2Plugin;
|
||||
children?: React.ReactChild | React.ReactChild[];
|
||||
|
||||
};
|
||||
|
||||
type PedalState = {
|
||||
|
||||
};
|
||||
|
||||
export const TemporaryDrawer = withStyles(pedalStyles)(
|
||||
class extends Component<PedalProps, PedalState>
|
||||
{
|
||||
state: PedalState;
|
||||
|
||||
constructor(props: PedalProps) {
|
||||
super(props);
|
||||
this.state = {
|
||||
};
|
||||
}
|
||||
render() : ReactNode {
|
||||
return (
|
||||
<div>
|
||||
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
);
|
||||
@@ -0,0 +1,570 @@
|
||||
import { PiPedalArgumentError } from './PiPedalError';
|
||||
import MidiBinding from './MidiBinding';
|
||||
|
||||
|
||||
const SPLIT_PEDALBOARD_ITEM_URI = "uri://two-play/piddle/pedalboard#Split";
|
||||
const EMPTY_PEDALBOARD_ITEM_URI = "uri://two-play/piddle/pedalboard#Empty";
|
||||
|
||||
|
||||
interface Deserializable<T> {
|
||||
deserialize(input: any): T;
|
||||
}
|
||||
|
||||
export class ControlValue implements Deserializable<ControlValue> {
|
||||
constructor(key?: string, value?: number)
|
||||
{
|
||||
this.key = key??"";
|
||||
this.value = value?? 0;
|
||||
}
|
||||
deserialize(input: any): ControlValue {
|
||||
this.key = input.key;
|
||||
this.value = input.value;
|
||||
return this;
|
||||
}
|
||||
static EmptyArray: ControlValue[] = [];
|
||||
|
||||
static deserializeArray(input: any[]): ControlValue[] {
|
||||
let result: ControlValue[] = [];
|
||||
for (let i = 0; i < input.length; ++i) {
|
||||
result[i] = new ControlValue().deserialize(input[i]);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
setValue(value: number) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
key: string;
|
||||
value: number;
|
||||
|
||||
}
|
||||
|
||||
export class PedalBoardItem implements Deserializable<PedalBoardItem> {
|
||||
deserializePedalBoardItem(input: any): PedalBoardItem {
|
||||
this.instanceId = input.instanceId ?? -1;
|
||||
this.uri = input.uri;
|
||||
this.pluginName = input.pluginName;
|
||||
this.isEnabled = input.isEnabled;
|
||||
this.midiBindings = MidiBinding.deserialize_array(input.midiBindings);
|
||||
|
||||
this.controlValues = ControlValue.deserializeArray(input.controlValues);
|
||||
return this;
|
||||
}
|
||||
deserialize(input: any): PedalBoardItem {
|
||||
return this.deserializePedalBoardItem(input);
|
||||
}
|
||||
static deserializeArray(input: any): PedalBoardItem[] {
|
||||
let result: PedalBoardItem[] = [];
|
||||
for (let i = 0; i < input.length; ++i) {
|
||||
let inputItem: any = input[i];
|
||||
let uri: string = inputItem.uri as string;
|
||||
let outputItem: PedalBoardItem;
|
||||
|
||||
if (uri === SPLIT_PEDALBOARD_ITEM_URI) {
|
||||
outputItem = new PedalBoardSplitItem().deserialize(inputItem);
|
||||
} else {
|
||||
outputItem = new PedalBoardItem().deserialize(inputItem);
|
||||
}
|
||||
result[i] = outputItem;
|
||||
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
getInstanceId() : number {
|
||||
if (this.instanceId === undefined)
|
||||
{
|
||||
throw new PiPedalArgumentError("Item does not have an id.");
|
||||
}
|
||||
return this.instanceId;
|
||||
}
|
||||
isEmpty(): boolean {
|
||||
return this.uri === EMPTY_PEDALBOARD_ITEM_URI;
|
||||
}
|
||||
isSplit(): boolean {
|
||||
return this.uri === SPLIT_PEDALBOARD_ITEM_URI;
|
||||
}
|
||||
|
||||
getControl(key: string): ControlValue {
|
||||
for (let i = 0; i < this.controlValues.length; ++i) {
|
||||
let v = this.controlValues[i];
|
||||
if (v.key === key) {
|
||||
return v;
|
||||
}
|
||||
}
|
||||
throw new PiPedalArgumentError("Invalid key.");
|
||||
|
||||
}
|
||||
|
||||
getControlValue(key: string): number {
|
||||
for (let i = 0; i < this.controlValues.length; ++i) {
|
||||
let v = this.controlValues[i];
|
||||
if (v.key === key) {
|
||||
return v.value;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
setControlValue(key: string, value: number): boolean {
|
||||
for (let i = 0; i < this.controlValues.length; ++i) {
|
||||
let v = this.controlValues[i];
|
||||
if (v.key === key) {
|
||||
if (v.value === value) return false;
|
||||
v.setValue(value);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
throw new PiPedalArgumentError("Control not found.");
|
||||
}
|
||||
setMidiBinding(midiBinding: MidiBinding): boolean {
|
||||
if (this.midiBindings)
|
||||
{
|
||||
for (let i = 0; i < this.midiBindings.length; ++i)
|
||||
{
|
||||
let binding = this.midiBindings[i];
|
||||
if (midiBinding.symbol === binding.symbol)
|
||||
{
|
||||
if (binding.equals(midiBinding))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
this.midiBindings.splice(i,1,midiBinding);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
this.midiBindings.push(midiBinding);
|
||||
return true;
|
||||
}
|
||||
this.midiBindings = [ midiBinding];
|
||||
return true;
|
||||
}
|
||||
getMidiBinding(symbol: string): MidiBinding {
|
||||
if (this.midiBindings)
|
||||
{
|
||||
for (let i = 0; i < this.midiBindings.length; ++i)
|
||||
{
|
||||
let midiBinding = this.midiBindings[i];
|
||||
if (midiBinding.symbol === symbol)
|
||||
{
|
||||
return midiBinding;
|
||||
}
|
||||
}
|
||||
}
|
||||
let result = new MidiBinding();
|
||||
result.symbol = symbol;
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
static EmptyArray: PedalBoardItem[] = [];
|
||||
|
||||
instanceId: number = -1;
|
||||
isEnabled: boolean = false;
|
||||
uri: string = "";
|
||||
pluginName?: string;
|
||||
controlValues: ControlValue[] = ControlValue.EmptyArray;
|
||||
midiBindings: MidiBinding[] = [];
|
||||
};
|
||||
|
||||
|
||||
export enum SplitType {
|
||||
Ab = 0,
|
||||
Mix = 1,
|
||||
Lr = 2
|
||||
}
|
||||
|
||||
|
||||
|
||||
export class PedalBoardSplitItem extends PedalBoardItem {
|
||||
static PANL_KEY: string = "panL";
|
||||
static PANR_KEY: string = "panR";
|
||||
static VOLL_KEY: string = "volL";
|
||||
static VOLR_KEY: string = "volR";
|
||||
|
||||
static MIX_KEY: string = "mix";
|
||||
static TYPE_KEY: string = "splitType";
|
||||
static SELECT_KEY: string = "select";
|
||||
|
||||
|
||||
deserialize(input: any): PedalBoardSplitItem {
|
||||
this.deserializePedalBoardItem(input);
|
||||
this.topChain = PedalBoardItem.deserializeArray(input.topChain);
|
||||
this.bottomChain = PedalBoardItem.deserializeArray(input.bottomChain);
|
||||
|
||||
return this;
|
||||
}
|
||||
getSplitType(): SplitType {
|
||||
let rawValue = this.getControlValue(PedalBoardSplitItem.TYPE_KEY);
|
||||
|
||||
if (rawValue < 1) return SplitType.Ab;
|
||||
if (rawValue < 2) return SplitType.Mix;
|
||||
return SplitType.Lr;
|
||||
}
|
||||
getToggleAbControlValue(): ControlValue {
|
||||
let cv = new ControlValue();
|
||||
cv.key = "select";
|
||||
cv.value = this.isASelected() ? 1 : 0;
|
||||
return cv;
|
||||
|
||||
}
|
||||
getMixControl(): ControlValue {
|
||||
return this.getControl(PedalBoardSplitItem.MIX_KEY);
|
||||
}
|
||||
getMix(): number {
|
||||
return this.getControlValue(PedalBoardSplitItem.MIX_KEY);
|
||||
}
|
||||
isASelected(): boolean {
|
||||
return this.getSplitType() !== SplitType.Ab || this.getControlValue(PedalBoardSplitItem.SELECT_KEY) === 0;
|
||||
}
|
||||
isBSelected(): boolean {
|
||||
return this.getSplitType() !== SplitType.Ab || this.getControlValue(PedalBoardSplitItem.SELECT_KEY) !== 0;
|
||||
}
|
||||
|
||||
topChain: PedalBoardItem[] = PedalBoardItem.EmptyArray;
|
||||
bottomChain: PedalBoardItem[] = PedalBoardItem.EmptyArray;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
export class PedalBoard implements Deserializable<PedalBoard> {
|
||||
|
||||
deserialize(input: any): PedalBoard {
|
||||
this.name = input.name;
|
||||
this.items = PedalBoardItem.deserializeArray(input.items);
|
||||
this.nextInstanceId = input.nextInstanceId ?? -1;
|
||||
return this;
|
||||
}
|
||||
|
||||
clone(): PedalBoard {
|
||||
return new PedalBoard().deserialize(this);
|
||||
}
|
||||
name: string = "";
|
||||
items: PedalBoardItem[] = [];
|
||||
nextInstanceId: number = -1;
|
||||
|
||||
*itemsGenerator(): Generator<PedalBoardItem, void, undefined> {
|
||||
let it = itemGenerator_(this.items);
|
||||
while (true)
|
||||
{
|
||||
let v = it.next();
|
||||
if (v.done) break;
|
||||
yield v.value;
|
||||
}
|
||||
}
|
||||
maybeGetItem(instanceId: number): PedalBoardItem | null{
|
||||
let it = this.itemsGenerator();
|
||||
while (true)
|
||||
{
|
||||
let v = it.next();
|
||||
if (v.done) break;
|
||||
if (v.value.instanceId === instanceId)
|
||||
{
|
||||
return v.value;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
getItem(instanceId: number): PedalBoardItem {
|
||||
let it = this.itemsGenerator();
|
||||
while (true)
|
||||
{
|
||||
let v = it.next();
|
||||
if (v.done) break;
|
||||
if (v.value.instanceId === instanceId)
|
||||
{
|
||||
return v.value;
|
||||
}
|
||||
}
|
||||
throw new PiPedalArgumentError("Item not found.");
|
||||
}
|
||||
deleteItem_(instanceId: number,items: PedalBoardItem[]): number | null
|
||||
{
|
||||
for (let i = 0; i < items.length; ++i)
|
||||
{
|
||||
let item = items[i];
|
||||
if (item.instanceId === instanceId)
|
||||
{
|
||||
if (items.length > 1) {
|
||||
items.splice(i,1);
|
||||
let nextSelectedItem = i;
|
||||
if (i >= items.length) --nextSelectedItem;
|
||||
return items[nextSelectedItem].instanceId;
|
||||
} else {
|
||||
// replace with an empty item.
|
||||
let newItem = this.createEmptyItem();
|
||||
items[i] = newItem;
|
||||
return newItem.instanceId;
|
||||
|
||||
}
|
||||
} else {
|
||||
if (item.isSplit())
|
||||
{
|
||||
let splitItem = item as PedalBoardSplitItem;
|
||||
let t = this.deleteItem_(instanceId,splitItem.topChain);
|
||||
if (t != null) return t;
|
||||
|
||||
t = this.deleteItem_(instanceId,splitItem.bottomChain);
|
||||
if (t != null) return t;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
canDeleteItem_(instanceId: number,items: PedalBoardItem[]): boolean
|
||||
{
|
||||
for (let i = 0; i < items.length; ++i)
|
||||
{
|
||||
let item = items[i];
|
||||
if (item.instanceId === instanceId)
|
||||
{
|
||||
if (items.length > 1) return true;
|
||||
return !item.isEmpty(); // can delete if there's one non-empty item.
|
||||
}
|
||||
if (item.isSplit())
|
||||
{
|
||||
let splitItem = item as PedalBoardSplitItem;
|
||||
if (this.canDeleteItem_(instanceId,splitItem.topChain))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
if (this.canDeleteItem_(instanceId,splitItem.bottomChain))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
canDeleteItem(instanceId: number): boolean
|
||||
{
|
||||
return this.canDeleteItem_(instanceId,this.items);
|
||||
}
|
||||
// Returns the next selected instanceId, or null if no deletion occurred.
|
||||
deleteItem(instanceId: number): number | null {
|
||||
return this.deleteItem_(instanceId,this.items);
|
||||
}
|
||||
|
||||
setMidiBinding(instanceId: number, midiBinding: MidiBinding): boolean
|
||||
{
|
||||
let item = this.getItem(instanceId);
|
||||
if (!item) return false;
|
||||
return item.setMidiBinding(midiBinding);
|
||||
}
|
||||
addToStart(item: PedalBoardItem)
|
||||
{
|
||||
this.items.splice(0,0,item);
|
||||
}
|
||||
addToEnd(item: PedalBoardItem)
|
||||
{
|
||||
this.items.splice(this.items.length,0,item);
|
||||
}
|
||||
static _addRelative(items: PedalBoardItem[],newItem: PedalBoardItem, instanceId: number, addBefore: boolean): boolean
|
||||
{
|
||||
for (let i = 0; i < items.length; ++i)
|
||||
{
|
||||
let item = items[i];
|
||||
if (item.instanceId === instanceId)
|
||||
{
|
||||
if (addBefore)
|
||||
{
|
||||
items.splice(i,0,newItem);
|
||||
} else {
|
||||
items.splice(i+1,0,newItem);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
if (item.isSplit())
|
||||
{
|
||||
let split = item as PedalBoardSplitItem;
|
||||
if (this._addRelative(split.topChain,newItem,instanceId,addBefore))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
if (this._addRelative(split.bottomChain,newItem,instanceId,addBefore))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
addBefore(item: PedalBoardItem, instanceId: number)
|
||||
{
|
||||
if (item.instanceId === instanceId) return;
|
||||
let result = PedalBoard._addRelative(this.items,item, instanceId, true);
|
||||
if (!result) {
|
||||
throw new PiPedalArgumentError("instanceId not found.");
|
||||
}
|
||||
|
||||
}
|
||||
addAfter(item: PedalBoardItem, instanceId: number)
|
||||
{
|
||||
if (item.instanceId === instanceId) return;
|
||||
let result = PedalBoard._addRelative(this.items,item, instanceId, false);
|
||||
if (!result) {
|
||||
throw new PiPedalArgumentError("instanceId not found.");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
ensurePedalboardIds() {
|
||||
if (this.nextInstanceId === -1) {
|
||||
let minId = 1;
|
||||
let it = this.itemsGenerator();
|
||||
while (true) {
|
||||
let v = it.next();
|
||||
if (v.done) break;
|
||||
if (v.value.instanceId) {
|
||||
let t = v.value.instanceId;
|
||||
if (t+1 > minId) minId = t+1;
|
||||
}
|
||||
|
||||
}
|
||||
this.nextInstanceId = minId;
|
||||
}
|
||||
let it = this.itemsGenerator();
|
||||
while (true) {
|
||||
let v = it.next();
|
||||
if (v.done) break;
|
||||
if (v.value.instanceId === -1) {
|
||||
v.value.instanceId = ++this.nextInstanceId;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
createEmptySplit(): PedalBoardSplitItem {
|
||||
let result: PedalBoardSplitItem = new PedalBoardSplitItem();
|
||||
result.uri = SPLIT_PEDALBOARD_ITEM_URI;
|
||||
result.instanceId = ++this.nextInstanceId;
|
||||
result.pluginName = "";
|
||||
result.isEnabled = true;
|
||||
result.topChain = [ this.createEmptyItem()];
|
||||
result.bottomChain = [ this.createEmptyItem()];
|
||||
result.controlValues = [
|
||||
new ControlValue(PedalBoardSplitItem.TYPE_KEY, 0),
|
||||
new ControlValue(PedalBoardSplitItem.SELECT_KEY,0),
|
||||
new ControlValue(PedalBoardSplitItem.MIX_KEY,0),
|
||||
new ControlValue(PedalBoardSplitItem.PANL_KEY,0),
|
||||
new ControlValue(PedalBoardSplitItem.VOLL_KEY,-3),
|
||||
new ControlValue(PedalBoardSplitItem.PANR_KEY,0),
|
||||
new ControlValue(PedalBoardSplitItem.VOLR_KEY,-3)
|
||||
];
|
||||
|
||||
|
||||
return result;
|
||||
|
||||
|
||||
}
|
||||
createEmptyItem(): PedalBoardItem {
|
||||
let result: PedalBoardItem = new PedalBoardItem();
|
||||
result.uri = EMPTY_PEDALBOARD_ITEM_URI;
|
||||
result.instanceId = ++this.nextInstanceId;
|
||||
result.pluginName = "";
|
||||
result.isEnabled = true;
|
||||
|
||||
return result;
|
||||
}
|
||||
setItemEmpty(item: PedalBoardItem)
|
||||
{
|
||||
item.uri = EMPTY_PEDALBOARD_ITEM_URI;
|
||||
item.instanceId = ++this.nextInstanceId;
|
||||
item.pluginName = "";
|
||||
item.isEnabled = true;
|
||||
item.controlValues = [];
|
||||
|
||||
}
|
||||
|
||||
_replaceItem(items: PedalBoardItem[], instanceId: number, newItem: PedalBoardItem): boolean {
|
||||
for (let i = 0; i < items.length; ++i)
|
||||
{
|
||||
let item = items[i];
|
||||
if (item.instanceId === instanceId)
|
||||
{
|
||||
items[i] = newItem;
|
||||
return true;
|
||||
}
|
||||
if (items[i].isSplit())
|
||||
{
|
||||
let splitItem = item as PedalBoardSplitItem;
|
||||
if (this._replaceItem(splitItem.topChain,instanceId,newItem))
|
||||
return true;
|
||||
if (this._replaceItem(splitItem.bottomChain,instanceId,newItem))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
replaceItem(instanceId: number, newItem: PedalBoardItem)
|
||||
{
|
||||
let result = this._replaceItem(this.items,instanceId,newItem);
|
||||
if (!result)
|
||||
{
|
||||
throw new PiPedalArgumentError("instanceId not found.");
|
||||
}
|
||||
}
|
||||
_addItem(items: PedalBoardItem[], newItem: PedalBoardItem, instanceId: number, append: boolean)
|
||||
{
|
||||
for (let i = 0; i < items.length; ++i)
|
||||
{
|
||||
let item = items[i];
|
||||
if (item.instanceId === instanceId)
|
||||
{
|
||||
if (append)
|
||||
{
|
||||
items.splice(i+1,0,newItem);
|
||||
return true;
|
||||
} else {
|
||||
items.splice(i,0,newItem);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (item.isSplit())
|
||||
{
|
||||
let splitItem = item as PedalBoardSplitItem;
|
||||
if (this._addItem(splitItem.topChain,newItem,instanceId,append)) return true;
|
||||
if (this._addItem(splitItem.bottomChain,newItem,instanceId,append)) return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
addItem(newItem: PedalBoardItem, instanceId: number, append: boolean): void
|
||||
{
|
||||
this._addItem(this.items,newItem,instanceId,append);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function* itemGenerator_(items: PedalBoardItem[]): Generator<PedalBoardItem, void, undefined> {
|
||||
for (let i = 0; i < items.length; ++i) {
|
||||
let item = items[i];
|
||||
yield item;
|
||||
if (item.uri === SPLIT_PEDALBOARD_ITEM_URI) {
|
||||
|
||||
let splitItem = item as PedalBoardSplitItem;
|
||||
|
||||
let it = itemGenerator_(splitItem.topChain);
|
||||
while (true) {
|
||||
let v = it.next();
|
||||
if (v.done) break;
|
||||
yield v.value;
|
||||
}
|
||||
it = itemGenerator_(splitItem.bottomChain);
|
||||
while (true) {
|
||||
let v = it.next();
|
||||
if (v.done) break;
|
||||
yield v.value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||