First commit.

This commit is contained in:
Robin Davies
2021-08-15 12:42:46 -04:00
commit d8a6022947
264 changed files with 95068 additions and 0 deletions
+23
View File
@@ -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*
+142
View File
@@ -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
)
+46
View File
@@ -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 cant go back!**
If you arent 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 youre on your own.
You dont have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldnt feel obligated to use this feature. However we understand that this tool wouldnt be useful if you couldnt 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/).
+16532
View File
File diff suppressed because it is too large Load Diff
+45
View File
@@ -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"
]
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

+7
View File
@@ -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

+8
View File
@@ -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

+20
View File
@@ -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

+20
View File
@@ -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

+16
View File
@@ -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

+72
View File
@@ -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

+20
View File
@@ -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

+28
View File
@@ -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

+19
View File
@@ -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

+22
View File
@@ -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

+14
View File
@@ -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

+42
View File
@@ -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

+6
View File
@@ -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

+20
View File
@@ -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

+20
View File
@@ -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

+72
View File
@@ -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

+194
View File
@@ -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

+41
View File
@@ -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

+16
View File
@@ -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

+41
View File
@@ -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

+33
View File
@@ -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

+15
View File
@@ -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

+19
View File
@@ -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

+37
View File
@@ -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

+75
View File
@@ -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

+30
View File
@@ -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

+28
View File
@@ -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

+28
View File
@@ -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

+22
View File
@@ -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

+57
View File
@@ -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

+27
View File
@@ -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

+30
View File
@@ -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

+25
View File
@@ -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

+18
View File
@@ -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

+23
View File
@@ -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

+9
View File
@@ -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

+19
View File
@@ -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

+14
View File
@@ -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

File diff suppressed because it is too large Load Diff

After

Width:  |  Height:  |  Size: 870 KiB

+42
View File
@@ -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

+14
View File
@@ -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

+14
View File
@@ -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

+12
View File
@@ -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

+15
View File
@@ -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

+1
View File
@@ -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

+44
View File
@@ -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>
Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.6 KiB

+25
View File
@@ -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"
}
+3
View File
@@ -0,0 +1,3 @@
# https://www.robotstxt.org/robotstxt.html
User-agent: *
Disallow:
File diff suppressed because it is too large Load Diff
+4
View File
@@ -0,0 +1,4 @@
{
"directoryListing": false
}
+11
View File
@@ -0,0 +1,11 @@
{
"socket_server_port": 80,
"socket_server_address": "*",
"max_upload_size": 1048576,
"ui_plugins": [
]
}
+76
View File
@@ -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": []
}
]
}
+276
View File
@@ -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": []
}
]
}
]
}
File diff suppressed because it is too large Load Diff
+28
View File
@@ -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!
+223
View File
@@ -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 &#169; 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;
+1
View File
@@ -0,0 +1 @@
+9
View File
@@ -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();
});
+830
View File
@@ -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 }} >&nbsp;</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 }} >&nbsp;</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;
+470
View File
@@ -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;
+95
View File
@@ -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;
}
}
+58
View File
@@ -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} />
)
}
}
}
+479
View File
@@ -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;
+922
View File
@@ -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;
+212
View File
@@ -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;
+338
View File
@@ -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;
+13
View File
@@ -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;
+100
View File
@@ -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;
+112
View File
@@ -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">&nbsp;</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&nbsp;&nbsp;</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&nbsp;&nbsp;</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:&nbsp;{status.underruns+""}&nbsp;&nbsp;
</Typography>
</span>
<span style={{color: underrunError? RED_COLOR: GREEN_COLOR}}>
<Typography variant="caption" color="inherit">
CPU:&nbsp;{cpuDisplay(status.cpuUsage)}&nbsp;&nbsp;
</Typography>
</span>
<span style={{color: GREEN_COLOR}}>
<Typography variant="caption" color="inherit">{tempDisplay(status.temperaturemC)}</Typography>
</span>
</div>
);
}
}
};
+35
View File
@@ -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 "";
}
}
}
+169
View File
@@ -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;
+63
View File
@@ -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>
);
}
}
+453
View File
@@ -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}>&nbsp;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;
+441
View File
@@ -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}>&nbsp;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;
+371
View File
@@ -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[] = [];
}
+452
View File
@@ -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
+60
View File
@@ -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;
};
+299
View File
@@ -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:&nbsp;</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:&nbsp;</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:&nbsp;</Typography>
<NumericInput defaultValue={midiBinding.maxValue} ariaLabel='scale'
min={-100} max={100} onChange={(value) => { this.handleScaleChange(value); } } />
</div>
)
}
</div>
)
}
});
export default MidiBindingView;
+343
View File
@@ -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;
+131
View File
@@ -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;
+50
View File
@@ -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));
}
};
+40
View File
@@ -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>
);
}
}
);
+570
View File
@@ -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;
}
}
}
}
File diff suppressed because it is too large Load Diff

Some files were not shown because too many files have changed in this diff Show More