├── .github └── FUNDING.yml ├── .gitignore ├── .replit ├── LICENSE ├── gulpfile.js ├── package-lock.json ├── package.json ├── readme.md ├── src ├── css │ ├── align_buttons.css │ ├── app.css │ ├── base.css │ ├── button.css │ ├── color-tool.css │ ├── context-menu.css │ ├── darkmode.css │ ├── dialog.css │ ├── draginput.css │ ├── dropdown.css │ ├── fonts.css │ ├── jgraduate.css │ ├── jpicker.css │ ├── keyboard.css │ ├── loading.css │ ├── menu.css │ ├── method-draw.css │ ├── modal.css │ ├── palette.css │ ├── panel.css │ ├── rulers.css │ ├── select.css │ ├── shapelib.css │ ├── source.css │ ├── sponsors.css │ ├── text.css │ ├── tools.css │ └── zoom-dropdown.css ├── extensions │ └── ext-shapes.xml ├── images │ ├── AlphaBar.png │ ├── Bars.png │ ├── Maps.png │ ├── NoColor.svg │ ├── bar-opacity.png │ ├── drag.png │ ├── dragging.png │ ├── eyedropper.png │ ├── eyedropper.svg │ ├── favicon.svg │ ├── logo.png │ ├── map-opacity.png │ ├── mappoint.gif │ ├── mappoint_c.png │ ├── mappoint_f.png │ ├── pencil_cursor.png │ ├── placeholder.svg │ ├── preview-opacity.png │ ├── rangearrows.svg │ ├── rangearrows2.svg │ └── rotate.png ├── index.html ├── js │ ├── Canvas.js │ ├── ContextMenu.js │ ├── Darkmode.js │ ├── Image.js │ ├── Import.js │ ├── Keyboard.js │ ├── Menu.js │ ├── Modal.js │ ├── PaintBox.js │ ├── Palette.js │ ├── Pan.js │ ├── Panel.js │ ├── Rulers.js │ ├── Shapelib.js │ ├── Text.js │ ├── Textonpath.js │ ├── Title.js │ ├── Toolbar.js │ ├── Zoom.js │ ├── browser.js │ ├── dao.js │ ├── dialog.js │ ├── dragupload.js │ ├── draw.js │ ├── editor.js │ ├── exportHandler.js │ ├── eyedropper.js │ ├── fonts.js │ ├── grid.js │ ├── history.js │ ├── jquery.attr.js │ ├── lib │ │ ├── canvg.js │ │ ├── contextmenu.js │ │ ├── css.min.js │ │ ├── filesaver.js │ │ ├── jpicker.min.js │ │ ├── jquery-3.5.1.min.js │ │ ├── jquery-draginput.js │ │ ├── jquery-ui-1.8.17.custom.min.js │ │ ├── jquery.contextMenu.js │ │ ├── jquery.hotkeys.min.js │ │ ├── jquery.jgraduate.js │ │ ├── mousewheel.js │ │ ├── pathseg.js │ │ ├── requestanimationframe.js │ │ ├── rgbcolor.js │ │ ├── taphold.js │ │ └── touch.js │ ├── loading.js │ ├── math.js │ ├── method-draw.js │ ├── modalbackup.js │ ├── modals.js │ ├── paste.js │ ├── path.js │ ├── sanitize.js │ ├── select.js │ ├── selectedChanged.js │ ├── shapelib │ │ ├── arrow.json │ │ ├── dialog_balloon.json │ │ ├── flowchart.json │ │ ├── game.json │ │ ├── math.json │ │ ├── music.json │ │ ├── nature.json │ │ ├── object.json │ │ ├── social.json │ │ ├── symbol.json │ │ ├── ui.json │ │ └── weather.json │ ├── shapes.js │ ├── start.js │ ├── state.js │ ├── svgcanvas.js │ ├── svgtransformlist.js │ ├── svgutils.js │ ├── translate.js │ ├── units.js │ └── utils.js ├── shapelib │ ├── arrow.json │ ├── dialog_balloon.json │ ├── flowchart.json │ ├── game.json │ ├── math.json │ ├── music.json │ ├── nature.json │ ├── object.json │ ├── raphael.txt │ ├── social.json │ ├── symbol.json │ ├── ui.json │ └── weather.json └── site.webmanifest └── test ├── all_tests.html ├── contextmenu_test.html ├── draw_test.html ├── history_test.html ├── math_test.html ├── path_test.html ├── qunit ├── qunit.css └── qunit.js ├── select_test.html ├── svgtransformlist_test.html ├── svgutils_test.html └── units_test.html /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: [methodofaction] 4 | liberapay: methodofaction 5 | custom: ['https://www.paypal.com/paypalme/methodofaction'] 6 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/* 2 | dist/* 3 | .DS_Store 4 | *.sh -------------------------------------------------------------------------------- /.replit: -------------------------------------------------------------------------------- 1 | language = "html" 2 | run = "cd src; python -m http.server 8000" -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) by Mark MacKay 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /gulpfile.js: -------------------------------------------------------------------------------- 1 | const gulp = require('gulp'); 2 | const concat = require('gulp-concat'); 3 | const useref = require('gulp-useref'); 4 | const replace = require('gulp-replace'); 5 | const cachebust = require('gulp-cache-bust'); 6 | const minify = require('gulp-minify'); 7 | 8 | gulp.task('css', function () { 9 | return gulp.src('src/css/*.css') 10 | .pipe(concat('all.css')) 11 | .pipe(gulp.dest('dist')); 12 | }); 13 | 14 | gulp.task('js', function () { 15 | return gulp.src(['src/js/*.js', 'src/lib/*.js']) 16 | .pipe(concat('all.js')) 17 | .pipe(gulp.dest('dist')); 18 | }); 19 | 20 | gulp.task('loading', function () { 21 | return gulp.src('src/js/loading.js') 22 | .pipe(gulp.dest('dist')); 23 | }); 24 | 25 | gulp.task('index', function () { 26 | return gulp.src('src/*.html') 27 | .pipe(useref()) 28 | .pipe(cachebust({type: 'timestamp'})) 29 | .pipe(gulp.dest('dist')); 30 | }); 31 | 32 | // no service worker implemented yet 33 | gulp.task('cache', function(){ 34 | return gulp.src(['./src/serviceworker.js']) 35 | .pipe(replace('', Date.now())) 36 | .pipe(gulp.dest('./dist/')); 37 | }); 38 | 39 | gulp.task('manifest', function(){ 40 | return gulp.src(['./src/site.webmanifest']) 41 | .pipe(gulp.dest('./dist/')); 42 | }); 43 | 44 | gulp.task('images', function(){ 45 | return gulp.src(['src/images/**/*']) 46 | .pipe(gulp.dest('dist/images')); 47 | }); 48 | 49 | gulp.task('extensions', function(){ 50 | return gulp.src(['src/extensions/**/*']) 51 | .pipe(gulp.dest('dist/extensions')); 52 | }); 53 | 54 | gulp.task('shapelib', function(){ 55 | return gulp.src(['src/shapelib/**/*']) 56 | .pipe(gulp.dest('dist/shapelib')); 57 | }); 58 | 59 | gulp.task('canvg', function(){ 60 | return gulp.src(['src/js/lib/canvg.js', 'src/js/lib/rgbcolor.js']) 61 | .pipe(gulp.dest('dist/js/lib')); 62 | }); 63 | 64 | gulp.task('build', 65 | gulp.series( 66 | 'css', 67 | 'js', 68 | 'index', 69 | 'manifest', 70 | 'images', 71 | 'extensions', 72 | 'shapelib', 73 | 'canvg' 74 | ) 75 | ); -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "method-draw", 3 | "version": "0.0.1", 4 | "description": "Method Draw is an SVG Editor for the web", 5 | "main": "index.js", 6 | "directories": { 7 | "doc": "docs", 8 | "test": "test" 9 | }, 10 | "scripts": { 11 | "test": "test" 12 | }, 13 | "repository": { 14 | "type": "git", 15 | "url": "git+https://github.com/methodofaction/Method-Draw.git" 16 | }, 17 | "keywords": [ 18 | "SVG", 19 | "vector", 20 | "vector-editor" 21 | ], 22 | "author": "Mark MacKay", 23 | "license": "MIT", 24 | "bugs": { 25 | "url": "https://github.com/methodofaction/Method-Draw/issues" 26 | }, 27 | "homepage": "https://github.com/methodofaction/Method-Draw#readme", 28 | "devDependencies": { 29 | "gulp": "^4.0.2", 30 | "gulp-cache-bust": "^1.4.1", 31 | "gulp-concat": "^2.6.1", 32 | "gulp-minify": "^3.1.0", 33 | "gulp-replace": "^1.1.3", 34 | "gulp-useref": "^4.0.1" 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # Method Draw 2 | 3 | Method Draw is a web based vector drawing application. The purpose of Method Draw is to provide a simple and easy-to-use SVG editor experience. It purposely removes some features such as layers and line-caps/corners in exchange for a more simple and pleasant experience. If you are looking for a more complete vector editing open-source solution, please check out [SVG Edit](https://github.com/SVG-Edit/svgedit). 4 | 5 | #### [Try Method Draw](https://editor.method.ac) online. 6 | 7 | ![Method Draw](https://method.ac/img/method-draw2021.png) 8 | 9 | ## Development 10 | 11 | Develop and run a local web server under `src`; 12 | 13 | ``` 14 | cd src 15 | python -m SimpleHTTPServer 8000 16 | ``` 17 | 18 | or if you have Python 3: 19 | 20 | ``` 21 | cd src 22 | python -m http.server 8000 23 | ``` 24 | 25 | ## Build 26 | 27 | Install dev dependencies: 28 | 29 | `npm install` 30 | 31 | Then you can build into `dist` by running: 32 | 33 | `gulp build` 34 | 35 | Deploy `dist` to your static file server of choice. 36 | 37 | ## Release notes 38 | 39 | **2021.05.26** Minor redesign 40 | **2021.05.12** Solved stability issues 41 | **2021.02.15** Major code refactor 42 | **2021.01.15** Added new fonts 43 | **2021.01.01** Text handling improvements 44 | **2020.12.10** Gradient fixes on Windows and Safari MacOS 45 | **2020.08.04** Vast code simplification 46 | **2020.08.02** File clean-up and gulp build implemented 47 | **2020.08.01** Project thawed 48 | **2015.01.01** Project frozen 49 | **2013.01.01** Project forked from SVG-Edit 50 | 51 | Sponsor development by [donating to the author](https://method.ac/donate/). 52 | 53 | Method Draw is Copyright (c) 54 | Mark MacKay [mark@method.ac](mailto:mark@method.ac) 55 | 56 | Published under an MIT License. Enjoy. 57 | -------------------------------------------------------------------------------- /src/css/align_buttons.css: -------------------------------------------------------------------------------- 1 | .align_buttons { 2 | margin-bottom: var(--x2); 3 | } 4 | 5 | .align_button, 6 | .align_button * { 7 | transition: var(--transition); 8 | } 9 | 10 | .align_button:hover, 11 | .align_button:hover * { 12 | transition: none; 13 | } 14 | 15 | .align_button { 16 | float: left; 17 | height: var(--x13); 18 | width: var(--x13); 19 | line-height: var(--x16); 20 | text-align: center; 21 | cursor: pointer; 22 | position: relative; 23 | } 24 | 25 | .align_button:hover { 26 | background-color: var(--z0); 27 | border-color: var(--z3); 28 | } 29 | 30 | .align_button svg { 31 | fill: var(--z10); 32 | } 33 | 34 | .align_button:hover svg { 35 | fill: var(--z15); 36 | transition: none; 37 | } 38 | 39 | .align_button:nth-child(1) {border-radius: var(--x1) 0 0 0;} 40 | .align_button:nth-child(3) {border-radius: 0 var(--x1) 0 0;} 41 | .align_button:nth-child(4) {border-radius: 0 0 0 var(--x1);} 42 | .align_button:nth-child(6) {border-radius: 0 0 var(--x1) 0;} 43 | 44 | .align_buttons .push_button { 45 | display: block; 46 | float: left; 47 | } -------------------------------------------------------------------------------- /src/css/app.css: -------------------------------------------------------------------------------- 1 | #svgroot { 2 | -moz-user-select: none; 3 | -webkit-user-select: none; 4 | display: block; 5 | } 6 | 7 | #svg_editor { 8 | background: var(--z1); 9 | } 10 | 11 | #svgcanvas { 12 | background-color: var(--z2); 13 | display: block; 14 | } 15 | 16 | #workarea { 17 | display: block; 18 | position:absolute; 19 | top: var(--x8); 20 | left: var(--x12); 21 | bottom: var(--x10); 22 | right: calc(var(--panel-width) + var(--x4)); 23 | background-color: var(--z2); 24 | overflow: auto; 25 | align-items: center; 26 | justify-content: center; 27 | transition: transform 500ms cubic-bezier(0.13,0.66,0.24,0.92); 28 | border-radius: var(--x2); 29 | box-shadow: var(--shadow-bg); 30 | } 31 | 32 | .inverted #svgcanvas, 33 | .inverted #workarea { 34 | background: var(--z1); 35 | } 36 | 37 | #workarea.rect, #workarea.line, #workarea.ellipse, #workarea.path, #workarea.shapelib { 38 | cursor: crosshair; 39 | } 40 | 41 | #workarea.text { 42 | cursor: text; 43 | } 44 | 45 | #workarea.eyedropper { 46 | cursor: url(../images/eyedropper.svg) 0 16, crosshair; 47 | } 48 | 49 | #workarea.fhpath { 50 | cursor: url(../images/pencil_cursor.png) 0 16, crosshair; 51 | } 52 | 53 | #workarea.rotate * { 54 | cursor: url(../images/rotate.png) 12 12, auto; 55 | } 56 | 57 | #workarea.select text, #workarea.multiselect text { 58 | cursor: default; 59 | } 60 | 61 | #workarea.n-resize * {cursor: n-resize !important;} 62 | #workarea.e-resize * {cursor: e-resize !important;} 63 | #workarea.w-resize * {cursor: w-resize !important;} 64 | #workarea.s-resize * {cursor: s-resize !important;} 65 | 66 | #workarea.ne-resize * {cursor: ne-resize !important;} 67 | #workarea.se-resize * {cursor: se-resize !important;} 68 | #workarea.nw-resize * {cursor: nw-resize !important;} 69 | #workarea.sw-resize * {cursor: sw-resize !important;} 70 | 71 | #workspace.dragging *, 72 | body.dragging * { 73 | cursor: url(../images/dragging.png), move; 74 | cursor: -webkit-grabbing; 75 | cursor: -moz-grabbing; 76 | } 77 | 78 | #workspace.drag * { 79 | cursor: url(../images/dragging.png), move; 80 | cursor: -webkit-grabbing; 81 | cursor: -moz-grabbing; 82 | } 83 | 84 | #workarea.copy { 85 | cursor: copy; 86 | } 87 | 88 | #workarea.zoom { 89 | cursor: crosshair; 90 | cursor:-moz-zoom-in; 91 | cursor:-webkit-zoom-in; 92 | } 93 | #workarea.zoom.out { 94 | cursor: crosshair; 95 | cursor:-moz-zoom-out; 96 | cursor:-webkit-zoom-out; 97 | } 98 | 99 | #selectorRubberBand { 100 | shape-rendering: crispEdges; 101 | } 102 | -------------------------------------------------------------------------------- /src/css/base.css: -------------------------------------------------------------------------------- 1 | :root, .inverted-undo { /* red */ /* yellow */ /* green */ 2 | --z0: #111827; --a0: #2a1c26; --b0: #2a2429; --c0: #162730; 3 | --z1: #1e2433; --a1: #421f24; --b1: #45322a; --c1: #1c3b3b; 4 | --z2: #2b313f; --a2: #5a2122; --b2: #60422b; --c2: #1f4f47; 5 | --z3: #393e4c; --a3: #71231f; --b3: #7b522a; --c3: #226452; 6 | --z4: #474c59; --a4: #89221c; --b4: #976328; --c4: #227a5e; 7 | --z5: #565a66; --a5: #a22018; --b5: #b47424; --c5: #20906a; 8 | --z6: #656974; --a6: #bb1b13; --b6: #d1861d; --c6: #1ba776; 9 | --z7: #747782; --a7: #d4100b; --b7: #f0990f; --c7: #0ebf82; 10 | --z8: #848790; --a8: #e62b18; --b8: #ffa723; --c8: #31ce8f; 11 | --z9: #94969f; --a9: #ee5035; --b9: #ffb146; --c9: #59d49c; 12 | --z10: #a4a6ae; --a10: #f66d50;--b10: #ffbc63; --c10: #76dbaa; 13 | --z11: #b4b7bd; --a11: #fb866c;--b11: #ffc67d; --c11: #8fe0b7; 14 | --z12: #c5c7cc; --a12: #ff9f88;--b12: #ffd197; --c12: #a7e6c5; 15 | --z13: #d6d8db; --a13: #ffb7a5;--b13: #ffdbb1; --c13: #bdecd3; 16 | --z14: #e7e9eb; --a14: #ffcfc3;--b14: #ffe6cb; --c14: #d2f1e1; 17 | --z15: #f9fafb; --a15: #fee6e1;--b15: #fef1e5; --c15: #e8f6ef; 18 | 19 | --d0: #152239; --e0: #181e39; --f0: #1f1d39; --g0: #291d30; 20 | --d1: #1a3050; --e1: #202651; --f1: #2f2251; --g1: #43223b; 21 | --d2: #1d3d69; --e2: #282e69; --f2: #3f2769; --g2: #5e2647; 22 | --d3: #1e4c82; --e3: #303782; --f3: #502b82; --g3: #7a2953; 23 | --d4: #1e5a9d; --e4: #383f9d; --f4: #622e9d; --g4: #962a5f; 24 | --d5: #1c69b8; --e5: #4047b8; --f5: #7430b8; --g5: #b32a6c; 25 | --d6: #1779d4; --e6: #4750d4; --f6: #8732d4; --g6: #d12678; 26 | --d7: #0b89f0; --e7: #4e59f0; --f7: #9933f0; --g7: #ef2085; 27 | --d8: #3797ff; --e8: #6366ff; --f8: #aa43ff; --g8: #ff3892; 28 | --d9: #62a3ff; --e9: #7e78ff; --f9: #b85fff; --g9: #ff5c9f; 29 | --d10: #7fafff; --e10: #958aff; --f10: #c477ff;--g10: #ff78ac; 30 | --d11: #98bbfe; --e11: #a99cff; --f11: #cf8fff;--g11: #ff91b9; 31 | --d12: #aec8fe; --e12: #bbafff; --f12: #daa5ff;--g12: #ffa8c6; 32 | --d13: #c3d4fd; --e13: #cdc2fe; --f13: #e3bbfe;--g13: #ffbed4; 33 | --d14: #d7e2fd; --e14: #ddd6fd; --f14: #ebd1fd;--g14: #ffd4e1; 34 | --d15: #eaeffc; --e15: #ece9fc; --f15: #f3e8fc;--g15: #fde9ef; 35 | -webkit-font-smoothing: antialiased; 36 | --shadow-bg: 0 var(--x2) var(--x4) var(--z0); 37 | --shadow: 0 var(--x1) var(--x8) rgba(0,0,0,0.4); 38 | --accent0: var(--d0); --comp0: var(--g0); 39 | --accent1: var(--d1); --comp1: var(--g1); 40 | --accent2: var(--d2); --comp2: var(--g2); 41 | --accent3: var(--d3); --comp3: var(--g3); 42 | --accent4: var(--d4); --comp4: var(--g4); 43 | --accent5: var(--d5); --comp5: var(--g5); 44 | --accent6: var(--d6); --comp6: var(--g6); 45 | --accent7: var(--d7); --comp7: var(--g7); 46 | --accent8: var(--d8); --comp8: var(--g8); 47 | --accent9: var(--d9); --comp9: var(--g9); 48 | --accent10: var(--d10); --comp10: var(--g10); 49 | --accent11: var(--d11); --comp11: var(--g11); 50 | --accent12: var(--d12); --comp12: var(--g12); 51 | --accent13: var(--d13); --comp13: var(--g13); 52 | --accent14: var(--d14); --comp14: var(--g14); 53 | --accent15: var(--d15); --comp15: var(--g15); 54 | 55 | } 56 | 57 | .inverted { 58 | --z0: #ffffff; --a0: #ffebe5; --b0: #fff5e9; --c0: #edfaf3; 59 | --z1: #ececee; --a1: #ffd2c6; --b1: #ffeace; --c1: #d7f5e4; 60 | --z2: #dadade; --a2: #ffbaa7; --b2: #ffdfb3; --c2: #c1efd5; 61 | --z3: #c8c8ce; --a3: #ffa28a; --b3: #ffd399; --c3: #aae9c7; 62 | --z4: #b6b6be; --a4: #fe886d; --b4: #ffc87f; --c4: #92e2b9; 63 | --z5: #a4a5ae; --a5: #f76e51; --b5: #ffbd64; --c5: #78dcab; 64 | --z6: #93949e; --a6: #ef5135; --b6: #ffb247; --c6: #5ad59d; 65 | --z7: #82838f; --a7: #e62c18; --b7: #ffa723; --c7: #31ce8f; 66 | --z8: #727380; --a8: #d30d0a; --b8: #ef980e; --c8: #0dbe81; 67 | --z9: #616371; --a9: #b91612; --b9: #cf841c; --c9: #18a574; 68 | --z10: #525463; --a10: #9e1a17; --b10: #b17122; --c10: #1d8c68; 69 | --z11: #424555; --a11: #851b1a; --b11: #935e26; --c11: #1e745b; 70 | --z12: #333647; --a12: #6c1b1c; --b12: #764c27; --c12: #1d5d4e; 71 | --z13: #25283a; --a13: #53191e; --b13: #5a3a27; --c13: #1a4742; 72 | --z14: #171b2d; --a14: #3b1620; --b14: #3e2a26; --c14: #153236; 73 | --z15: #080c21; --a15: #221320; --b15: #231a23; --c15: #0f1e2a; 74 | 75 | --d0: #eff3ff; --e0: #f2eeff; --f0: #f9ecff; --g0: #ffedf3; 76 | --d1: #dbe5ff; --e1: #e2d9ff; --f1: #f0d5ff; --g1: #ffd7e5; 77 | --d2: #c7d8ff; --e2: #d1c5ff; --f2: #e7bfff; --g2: #ffc1d6; 78 | --d3: #b2caff; --e3: #bfb2ff; --f3: #dda8ff; --g3: #ffabc8; 79 | --d4: #9bbdff; --e4: #ac9eff; --f4: #d290ff; --g4: #ff93bb; 80 | --d5: #81b0ff; --e5: #978bff; --f5: #c679ff; --g5: #ff7aad; 81 | --d6: #63a3ff; --e6: #7f78ff; --f6: #b960ff; --g6: #ff5da0; 82 | --d7: #3897ff; --e7: #6366ff; --f7: #ab44ff; --g7: #ff3992; 83 | --d8: #0788f0; --e8: #4d58f0; --f8: #9932f0; --g8: #ef1e85; 84 | --d9: #1077d2; --e9: #444ed2; --f9: #842fd2; --g9: #cf2277; 85 | --d10: #1466b5; --e10: #3b44b5; --f10: #702cb5; --g10: #b02369; 86 | --d11: #165599; --e11: #323a99; --f11: #5d2899; --g11: #91235c; 87 | --d12: #15467e; --e12: #29317e; --f12: #4a247e; --g12: #74214f; 88 | --d13: #143664; --e13: #212764; --f13: #381f64; --g13: #581e42; 89 | --d14: #11284b; --e14: #181e4b; --f14: #271a4b; --g14: #3c1a36; 90 | --d15: #0d1a33; --e15: #101633; --f15: #161433; --g15: #21142a; 91 | -webkit-font-smoothing: auto; 92 | --shadow-bg: 0 var(--x2) var(--x4) var(--z3); 93 | --shadow: 0 var(--x1) var(--x8) rgba(0,0,0,0.1); 94 | --accent0: var(--d0); --comp0: var(--g0); 95 | --accent1: var(--d1); --comp1: var(--g1); 96 | --accent2: var(--d2); --comp2: var(--g2); 97 | --accent3: var(--d3); --comp3: var(--g3); 98 | --accent4: var(--d4); --comp4: var(--g4); 99 | --accent5: var(--d5); --comp5: var(--g5); 100 | --accent6: var(--d6); --comp6: var(--g6); 101 | --accent7: var(--d7); --comp7: var(--g7); 102 | --accent8: var(--d8); --comp8: var(--g8); 103 | --accent9: var(--d9); --comp9: var(--g9); 104 | --accent10: var(--d10); --comp10: var(--g10); 105 | --accent11: var(--d11); --comp11: var(--g11); 106 | --accent12: var(--d12); --comp12: var(--g12); 107 | --accent13: var(--d13); --comp13: var(--g13); 108 | --accent14: var(--d14); --comp14: var(--g14); 109 | --accent15: var(--d15); --comp15: var(--g15); 110 | } 111 | 112 | :root { 113 | --x1: 4px; 114 | --x2: 8px; 115 | --x3: 12px; 116 | --x4: 16px; 117 | --x5: 20px; 118 | --x6: 24px; 119 | --x7: 28px; 120 | --x8: 32px; 121 | --x9: 36px; 122 | --x10: 40px; 123 | --x11: 44px; 124 | --x12: 48px; 125 | --x13: 52px; 126 | --x14: 56px; 127 | --x15: 60px; 128 | --x16: 64px; 129 | --mono-font: "SFMono-Regular", Consolas, "Liberation Mono", Menlo, Courier, monospace; 130 | --ui-font: -apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Oxygen-Sans,Ubuntu,Cantarell,"Helvetica Neue",sans-serif; 131 | --panel-width: calc(var(--x10)*2*2 + var(--x1)); 132 | --transition-duration: 200ms; 133 | --transition: all var(--transition-duration) ease; 134 | } -------------------------------------------------------------------------------- /src/css/button.css: -------------------------------------------------------------------------------- 1 | .button-container { 2 | clear: both; 3 | margin-top: var(--x4); 4 | } 5 | 6 | .button { 7 | height: var(--x12); 8 | cursor: pointer; 9 | line-height: var(--x12); 10 | text-align: left; 11 | color: var(--z13); 12 | border: solid var(--z5) 2px; 13 | border-radius: var(--x1); 14 | white-space: nowrap; 15 | margin: var(--x2) var(--x2) var(--x2) 0; 16 | padding: 0 var(--x4); 17 | display: inline-block; 18 | font-weight: bold; 19 | background-color: var(--z1); 20 | } 21 | 22 | .button:hover { 23 | background-color: var(--z2); 24 | } 25 | 26 | .button.full { 27 | display: block; 28 | text-align: center; 29 | } 30 | 31 | .button svg { 32 | vertical-align: center; 33 | fill: var(--z5); 34 | } 35 | 36 | .button:hover svg { 37 | fill: var(--z10); 38 | } 39 | 40 | .button.current { 41 | background-color: var(--z0); 42 | } 43 | 44 | .button.current svg { 45 | fill: var(--accent15); 46 | } 47 | 48 | .button.disabled svg { 49 | background-color: #aaa; 50 | cursor: not-allowed; 51 | fill: var(--z3); 52 | pointer-events: none; 53 | } -------------------------------------------------------------------------------- /src/css/color-tool.css: -------------------------------------------------------------------------------- 1 | #color_tools #tool_fill .color_block > div { 2 | position: absolute; 3 | top: 0; 4 | left: 0; 5 | } 6 | 7 | #color_tools #tool_fill .color_block #fill_bg, #color_tools #tool_stroke .color_block #stroke_bg { 8 | position: absolute; 9 | top: 1px; 10 | left: 1px; 11 | bottom: 1px; 12 | right: 1px; 13 | } 14 | 15 | #stroke_color:after { 16 | content: ''; 17 | position: absolute; 18 | display: block; 19 | left: var(--x1); 20 | right: var(--x1); 21 | top: var(--x1); 22 | bottom: var(--x1); 23 | background: var(--z1); 24 | box-shadow: 0 0 0 1px var(--z13); 25 | } 26 | 27 | #tool_switch { 28 | cursor: pointer; 29 | width: 11px; 30 | height: 11px; 31 | position: absolute; 32 | top: -5px; 33 | right: 0; 34 | cursor: nwse-resize 35 | } 36 | 37 | #tool_switch svg { 38 | fill: var(--z6); 39 | } 40 | 41 | #tool_switch:hover svg { 42 | fill: var(--z15); 43 | } 44 | 45 | #stroke_color:hover:after { 46 | box-shadow: 0 0 0 1px var(--z15); 47 | } 48 | 49 | .color_block { 50 | width: var(--x5); 51 | height: var(--x5); 52 | overflow: hidden; 53 | border: solid var(--z12) 1px; 54 | cursor: pointer; 55 | } 56 | 57 | .color_block svg { 58 | width: 22px; 59 | height: 22px; 60 | } 61 | 62 | #tool_stroke .color_block > div { 63 | position: absolute; 64 | bottom: 0; 65 | right: 0; 66 | } 67 | 68 | #color_tools { 69 | position: relative; 70 | width: 48px; 71 | height: 48px; 72 | margin: 12px 6px 0 6px; 73 | } 74 | 75 | #color_tools { 76 | width: auto; 77 | height: auto; 78 | } 79 | 80 | #tool_fill { 81 | position: absolute; 82 | top: 0; 83 | left: 0; 84 | z-index: 1; 85 | } 86 | 87 | #tool_fill.active, 88 | #tool_stroke.active { 89 | z-index: 2; 90 | } 91 | 92 | #tool_stroke { 93 | top: 14px; 94 | left: 14px; 95 | } 96 | 97 | #tool_fill, #tool_stroke, #tool_canvas { 98 | box-shadow: 0 0 0 1px var(--z0); 99 | position: absolute; 100 | } 101 | 102 | #tool_canvas .color_block { 103 | width: 58px; 104 | height: 38px; 105 | } 106 | 107 | #tool_canvas .color_block svg { 108 | width: auto; 109 | height: auto; 110 | } 111 | 112 | -------------------------------------------------------------------------------- /src/css/context-menu.css: -------------------------------------------------------------------------------- 1 | 2 | /* Generic context menu styles */ 3 | .contextMenu { 4 | position: absolute; 5 | z-index: 99999; 6 | background: var(--z15); 7 | padding: 5px 0; 8 | margin: 0px; 9 | display: none; 10 | font: 14px/18px -apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Oxygen-Sans,Ubuntu,Cantarell,"Helvetica Neue",sans-serif; 11 | border-radius: 5px; 12 | box-shadow: 2px 5px 10px rgba(0,0,0,.3); 13 | } 14 | 15 | .contextMenu LI { 16 | list-style: none; 17 | padding: 0px; 18 | margin: 0px; 19 | } 20 | 21 | .contextMenu .shortcut { 22 | width: 115px; 23 | text-align:right; 24 | float:right; 25 | } 26 | 27 | .contextMenu A { 28 | -moz-user-select: none; 29 | -webkit-user-select: none; 30 | color: var(--z2); 31 | text-decoration: none; 32 | display: block; 33 | line-height: 20px; 34 | height: 20px; 35 | background-position: 6px center; 36 | background-repeat: no-repeat; 37 | outline: none; 38 | padding: 0px 15px 1px 20px; 39 | } 40 | 41 | .contextMenu LI.hover A { 42 | background-color: var(--accent7); 43 | color: var(--z15); 44 | cursor: default; 45 | } 46 | 47 | .contextMenu LI.disabled A { 48 | color: #999; 49 | } 50 | 51 | .contextMenu LI.hover.disabled A { 52 | background-color: transparent; 53 | } 54 | 55 | .contextMenu LI.separator { 56 | border-top: solid 1px #E3E3E3; 57 | padding-top: 5px; 58 | margin-top: 5px; 59 | } -------------------------------------------------------------------------------- /src/css/darkmode.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/methodofaction/Method-Draw/c01c2f72b4497027eecb2a27e066c9bc5bf754c0/src/css/darkmode.css -------------------------------------------------------------------------------- /src/css/dialog.css: -------------------------------------------------------------------------------- 1 | 2 | #dialog_box { 3 | display: none; 4 | } 5 | 6 | #dialog_box_overlay { 7 | background: var(--z0); 8 | opacity: .5; 9 | height: 100%; 10 | left:0; 11 | position:absolute; 12 | top:0; 13 | width:100%; 14 | z-index: 6; 15 | } 16 | 17 | #dialog_content { 18 | height: 95px; 19 | padding: 32px; 20 | overflow: auto; 21 | text-align: left; 22 | font-size: 16px; 23 | } 24 | 25 | #dialog_content h4 { 26 | margin: 0; 27 | } 28 | 29 | 30 | #dialog_buttons input:last-child { 31 | position: absolute; 32 | left: 10px; 33 | bottom: 10px; 34 | } 35 | 36 | #dialog_buttons input:first-child { 37 | position: absolute; 38 | right: 10px; 39 | bottom: 10px; 40 | } 41 | 42 | #dialog_content.prompt { 43 | height: 75px; 44 | } 45 | 46 | #dialog_content p { 47 | line-height: 1.3em; 48 | } 49 | 50 | #dialog_container { 51 | position: absolute; 52 | left: 50%; 53 | top: 50%; 54 | width: 400px; 55 | transform: translate(-50%, -50%); 56 | position:fixed; 57 | z-index:50001; 58 | background: var(--z15); 59 | } 60 | 61 | 62 | #dialog_container, #dialog_content { 63 | border-radius: 3px; 64 | } 65 | 66 | #dialog_buttons input[type=text] { 67 | width: 90%; 68 | display: block; 69 | margin: 0 0 5px 11px; 70 | } 71 | 72 | #dialog_buttons input[type=button] { 73 | margin: 0 1em; 74 | } 75 | -------------------------------------------------------------------------------- /src/css/dropdown.css: -------------------------------------------------------------------------------- 1 | .dropdown { 2 | position: relative; 3 | float: left; 4 | } 5 | 6 | .dropdown button { 7 | width: 21px; 8 | height: 22px; 9 | padding: 0 3px 0 3px; 10 | border: none; 11 | background-color: var(--z6); 12 | border-radius: 0 2px 2px 0; 13 | margin-left: -1px; 14 | position: relative; 15 | } 16 | 17 | .dropdown button:hover { 18 | background-color: var(--z7); 19 | } 20 | 21 | .dropdown button:after { 22 | content: ''; 23 | position: absolute; 24 | border: solid transparent 4px; 25 | border-top-color: var(--z9); 26 | top: 9px; 27 | left: 6px; 28 | } 29 | 30 | .dropdown button.down { 31 | border-left: 1px solid #808080; 32 | border-top: 1px solid #808080; 33 | border-right: 1px solid #FFFFFF; 34 | border-bottom: 1px solid #FFFFFF; 35 | background-color: #B0B0B0; 36 | } 37 | 38 | .dropdown ul { 39 | list-style: none; 40 | position: absolute; 41 | margin: 0; 42 | padding: 0; 43 | left: -80px; 44 | top: 26px; 45 | z-index: 4; 46 | display: none; 47 | } 48 | 49 | .dropup ul { 50 | top: auto; 51 | bottom: 26px; 52 | border-radius: 3px; 53 | box-shadow: 0 5px 10px #000; 54 | } 55 | 56 | .dropup ul:after { 57 | content: ''; 58 | display: block; 59 | position: absolute; 60 | bottom: -10px; 61 | right: 50%; 62 | top: auto; 63 | width: 0; 64 | height: 0; 65 | border: solid transparent 5px; 66 | border-top-color: #fff; 67 | } 68 | 69 | .dropdown li { 70 | display: block; 71 | width: 120px; 72 | padding: 5px 10px; 73 | color: #333; 74 | background: #fff; 75 | margin: 0; 76 | line-height: 16px; 77 | } 78 | 79 | .dropdown li:first-child {border-radius: 3px 3px 0 0;} 80 | .dropdown li:last-child {border-radius: 0 0 3px 3px;} 81 | 82 | .dropdown li:hover { 83 | background-color: #ddd; 84 | color: #000; 85 | } 86 | 87 | .dropdown li.special { 88 | padding: 10px; 89 | background: white; 90 | border: none; 91 | box-shadow: 0 3px 10px black; 92 | border-radius: 3px !important; 93 | } 94 | 95 | .dropdown li.special:after { 96 | content: ''; 97 | display: block; 98 | position: absolute; 99 | top: -10px; 100 | right: 50%; 101 | border: solid transparent 5px; 102 | border-bottom-color: #fff; 103 | 104 | } 105 | 106 | .dropdown li.special.down:after { 107 | bottom: -10px; 108 | right: 50%; 109 | top: auto; 110 | border: solid transparent 5px; 111 | border-top-color: #fff; 112 | 113 | } 114 | 115 | .flyout_arrow_horiz { 116 | position: absolute; 117 | bottom: -1px; 118 | right: 0; 119 | z-index: 10; 120 | } -------------------------------------------------------------------------------- /src/css/fonts.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/methodofaction/Method-Draw/c01c2f72b4497027eecb2a27e066c9bc5bf754c0/src/css/fonts.css -------------------------------------------------------------------------------- /src/css/jpicker.css: -------------------------------------------------------------------------------- 1 | table.jPicker { 2 | border-collapse: collapse; 3 | margin-top: var(--x4); 4 | } 5 | 6 | table.jPicker td { 7 | padding: var(--x1); 8 | } 9 | 10 | .jPicker .Icon { 11 | display: inline-block; 12 | height: var(--x6); 13 | position: relative; 14 | text-align: left; 15 | width: var(--x6); 16 | } 17 | .jPicker .Icon span.Color, .jPicker .Icon span.Alpha { 18 | background-position: 2px 2px; 19 | display: block; 20 | height: 100%; 21 | left: 0; 22 | position: absolute; 23 | top: 0; 24 | width: 100%} 25 | .jPicker .Icon span.Image { 26 | background-repeat: no-repeat; 27 | cursor: pointer; 28 | display: block; 29 | height: 100%; 30 | left: 0; 31 | position: absolute; 32 | top: 0; 33 | width: 100%} 34 | .jPicker.Container { 35 | z-index: 10; 36 | } 37 | 38 | .jPicker .Move { 39 | background-color: var(--z12); 40 | border-color: var(--z15) var(--z6) var(--z6) var(--z15); 41 | border-style: solid; 42 | border-width: 1px; 43 | cursor: move; 44 | height: 12px; 45 | padding: 0; 46 | } 47 | .jPicker .Title { 48 | display: none; 49 | } 50 | 51 | .jPicker .Map { 52 | border: solid var(--z1) 1px; 53 | height: 256px; 54 | width: 256px; 55 | cursor: crosshair; 56 | margin: 0; 57 | overflow: hidden; 58 | padding: 0; 59 | position: relative; 60 | } 61 | 62 | .jPicker .Bar { 63 | border: solid var(--z1) 1px; 64 | cursor: n-resize; 65 | height: 260px; 66 | margin: 0; 67 | overflow: hidden; 68 | padding: 0; 69 | position: relative; 70 | width: 24px; 71 | height: 256px; 72 | width: 20px; 73 | } 74 | 75 | .jPicker .Map .Map1, 76 | .jPicker .Map .Map2, 77 | .jPicker .Map .Map3, 78 | .jPicker .Bar .Map1, 79 | .jPicker .Bar .Map2, 80 | .jPicker .Bar .Map3, 81 | .jPicker .Bar .Map4, 82 | .jPicker .Bar .Map5, 83 | .jPicker .Bar .Map6 { 84 | background-color: transparent; 85 | background-image: none; 86 | display: block; 87 | left: 0; 88 | position: absolute; 89 | top: 0; 90 | } 91 | 92 | .jPicker .Map .Map1, 93 | .jPicker .Map .Map2, 94 | .jPicker .Map .Map3 { 95 | height: 2596px; 96 | width: 256px; 97 | } 98 | .jPicker .Bar .Map1, .jPicker .Bar .Map2, .jPicker .Bar .Map3, .jPicker .Bar .Map4 { 99 | height: 3896px; 100 | width: 20px; 101 | } 102 | .jPicker .Bar .Map5, .jPicker .Bar .Map6 { 103 | height: 256px; 104 | width: 20px; 105 | } 106 | .jPicker .Map .Map1, .jPicker .Map .Map2, .jPicker .Bar .Map6 { 107 | background-repeat: no-repeat; 108 | } 109 | .jPicker .Map .Map3, .jPicker .Bar .Map5 { 110 | background-repeat: repeat; 111 | } 112 | .jPicker .Bar .Map1, .jPicker .Bar .Map2, .jPicker .Bar .Map3, .jPicker .Bar .Map4 { 113 | background-repeat: repeat-x; 114 | } 115 | .jPicker .Map .Arrow { 116 | display: block; 117 | position: absolute; 118 | } 119 | .jPicker .Bar .Arrow { 120 | display: block; 121 | left: 0; 122 | position: absolute; 123 | } 124 | .jPicker .Preview { 125 | font-size: 9px; 126 | text-align: center; 127 | } 128 | .jPicker .Preview div.bgt { 129 | height: 62px; 130 | margin: 0 auto; 131 | padding: 0; 132 | width: 62px; 133 | } 134 | 135 | .jPicker .Preview div span { 136 | border: 1px solid var(--z0); 137 | display: block; 138 | height: 30px; 139 | margin: 0 auto; 140 | padding: 0; 141 | width: 60px; 142 | } 143 | .jPicker .Preview .Active { 144 | border-bottom-width: 0; 145 | } 146 | .jPicker .Preview .Current { 147 | border-top-width: 0; 148 | cursor: pointer; 149 | } 150 | .jPicker .Button { 151 | text-align: center; 152 | width: 115px; 153 | } 154 | .jPicker .Button input { 155 | width: 100px; 156 | cursor: pointer; 157 | } 158 | 159 | .jPicker td.Radio { 160 | margin: 0; 161 | padding: 0 0 0 var(--x2); 162 | width: var(--x8); 163 | white-space: nowrap; 164 | } 165 | .jPicker td.Radio input { 166 | margin: 0 var(--x1) 0 0; 167 | padding: 0; 168 | cursor: pointer; 169 | } 170 | .jPicker td.Text { 171 | font-size: 12px!important; 172 | height: 22px; 173 | margin: 0; 174 | padding: 0; 175 | text-align: left; 176 | width: 70px; 177 | white-space: nowrap; 178 | } 179 | .jPicker tr.Hex td.Text { 180 | width: 100px; 181 | } 182 | 183 | .jPicker tr.Hex td.Text label { 184 | margin-left: var(--x4); 185 | } 186 | 187 | .jPicker tr.Hex td.Text span { 188 | width: 100px; 189 | color: #333; 190 | } 191 | 192 | .jPicker td.Text input { 193 | background-color: var(--z15); 194 | height: var(--x4); 195 | margin: 0 0 0 var(--x1);; 196 | text-align: left; 197 | width: var(--x8); 198 | color: var(--z15); 199 | } 200 | 201 | #color_picker input[type=text], #color_picker input[type=number] { 202 | -webkit-appearance: none; 203 | width: 30px; 204 | background: var(--z0); 205 | border: none; 206 | border-radius: var(--x1); 207 | } 208 | 209 | #color_picker input[type=radio] { 210 | position: relative; 211 | top: 2px; 212 | } 213 | 214 | #color_picker .jPicker tr.Hex td.Text input.Hex { 215 | width: var(--x12); 216 | display: inline-block; 217 | float: none; 218 | } 219 | 220 | .jPicker tr.Hex td.Text input.AHex { 221 | width: var(--x5); 222 | display: none; 223 | } 224 | .jPicker .Grid { 225 | text-align: center; 226 | float: right; 227 | width: 108px; 228 | } 229 | .jPicker .Grid span.QuickColor { 230 | cursor: url(../images/eyedropper.svg) 0 23, crosshair; 231 | background-repeat: no-repeat; 232 | display: inline-block; 233 | height: var(--x4); 234 | line-height: var(--x4); 235 | margin: 0; 236 | padding: 0; 237 | width: var(--x4); 238 | } 239 | 240 | .jPicker .Grid span.QuickColor:hover { 241 | position: relative; 242 | z-index: 10; 243 | outline: solid var(--z0) 1px; 244 | } 245 | 246 | .jPicker td { 247 | vertical-align: top; 248 | } 249 | .jPicker td.colorsquare { 250 | width: 275px; 251 | } 252 | 253 | .jPicker .prev_div { 254 | margin-top: -15px; 255 | } 256 | 257 | .jPicker .actions { 258 | position: absolute; 259 | bottom: 20px; 260 | left: 20px; 261 | right: 20px; 262 | } 263 | 264 | .jPicker .actions .Ok{ 265 | position: absolute; 266 | top: 0; 267 | right: 0px; 268 | } 269 | 270 | .jPicker .actions .Cancel{ 271 | position: absolute; 272 | top: 0; 273 | left: 0px; 274 | } 275 | 276 | .jPicker .color_preview { 277 | width: 62px; 278 | margin: 0 auto; 279 | } 280 | -------------------------------------------------------------------------------- /src/css/keyboard.css: -------------------------------------------------------------------------------- 1 | #shortcuts { 2 | columns: 4; 3 | } 4 | .shortcut-keys { 5 | display: flex; 6 | margin: var(--x1) 0; 7 | break-inside: avoid; 8 | } 9 | 10 | .shortcut-key { 11 | text-transform: uppercase; 12 | color: var(--z4); 13 | border: solid var(--z4) 1px; 14 | border-radius: var(--x1); 15 | padding: 0 var(--x1); 16 | margin: 2px; 17 | font-size: 12px; 18 | font-weight: 600; 19 | } 20 | 21 | .shortcut-name { 22 | margin-left: var(--x2); 23 | line-height: 160%; 24 | white-space: nowrap; 25 | } -------------------------------------------------------------------------------- /src/css/loading.css: -------------------------------------------------------------------------------- 1 | #workarea > svg { 2 | display: none; 3 | } 4 | 5 | .loading #svgcanvas { 6 | position: absolute; 7 | top: 0; 8 | left: 0; 9 | right: 0; 10 | bottom: 0; 11 | display: flex; 12 | align-items: center; 13 | justify-content: center; 14 | } 15 | 16 | #svgcanvas:after { 17 | transition: opacity var(--transition-duration) 300ms; 18 | opacity: 0; 19 | } 20 | 21 | .loading #svgcanvas:after { 22 | position: absolute; 23 | content: attr(title); 24 | font-size: 13px; 25 | font-weight: 600; 26 | line-height: var(--x6); 27 | padding: 0 var(--x2); 28 | background: var(--z2); 29 | color: var(--z10); 30 | z-index: 100; 31 | opacity: 1; 32 | border-radius: var(--x1); 33 | } 34 | 35 | .loading #workarea > svg { 36 | display: block; 37 | position: absolute; 38 | background: white; 39 | top: 50%; 40 | left: 50%; 41 | transform: translate(-50%, -50%); 42 | opacity: 0.7; 43 | } 44 | 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /src/css/menu.css: -------------------------------------------------------------------------------- 1 | #menu_bar { 2 | padding: 0 0 0 var(--x12); 3 | position: relative; 4 | z-index: 2; 5 | height: var(--x8); 6 | display: flex; 7 | } 8 | 9 | #menu_bar.active .menu.open .menu_list { 10 | display: block; 11 | } 12 | 13 | .menu { 14 | position: relative; 15 | z-index: 5; 16 | color: var(--z3); 17 | display: inline-block; 18 | } 19 | 20 | .menu_title { 21 | cursor: pointer; 22 | display: inline-block; 23 | z-index: 10; 24 | color: var(--z15); 25 | position: relative; 26 | line-height: var(--x8); 27 | padding: 0 var(--x3); 28 | vertical-align: top; 29 | } 30 | 31 | .menu .menu_title:hover { 32 | background: var(--z2); 33 | } 34 | 35 | .menu_list .separator { 36 | margin: var(--x1) 0; 37 | border-top: solid var(--z14) 1px; 38 | } 39 | 40 | .menu_list { 41 | position: absolute; 42 | left: 0; 43 | top: var(--x8); 44 | white-space: nowrap; 45 | background: var(--z15); 46 | padding: var(--x2) 0; 47 | border-radius: 0 var(--x2) var(--x2) var(--x2); 48 | box-shadow: var(--shadow); 49 | } 50 | 51 | 52 | #menu_bar.active .menu.open .menu_title { 53 | background: var(--z15); 54 | color: var(--z1); 55 | } 56 | 57 | .inverted #menu_bar.active .menu.open .menu_title { 58 | background: var(--z0); 59 | color: var(--z15); 60 | } 61 | 62 | .menu .menu_list { 63 | display: none; 64 | position: absolute; 65 | } 66 | 67 | .menu_list .menu_item { 68 | position: relative; 69 | overflow: hidden; 70 | line-height: var(--x6); 71 | padding: var(--x1) var(--x16) var(--x1) var(--x6); 72 | cursor: pointer; 73 | color: var(--z3); 74 | } 75 | 76 | .menu_list input[type=file] { 77 | -webkit-appearance: none; 78 | position: absolute; 79 | opacity: 0; 80 | cursor: pointer; 81 | left: 0; 82 | right: 0; 83 | top: 0; 84 | bottom: 0; 85 | display: block; 86 | background: red; 87 | z-index: 100; 88 | } 89 | 90 | 91 | .menu_list .menu_item.tool_button { 92 | background: transparent; 93 | border: none; 94 | margin: 0; 95 | padding: var(--x1) var(--x16) var(--x1) var(--x6); 96 | height: auto; 97 | width: auto; 98 | } 99 | 100 | .menu_list .menu_item.push_button_pressed:before { 101 | content: '✔'; 102 | position: absolute; 103 | display: block; 104 | left: 6px; 105 | top: var(--x1); 106 | } 107 | 108 | .menu_list .menu_item:hover, 109 | .menu_list .menu_item.push_button_pressed:hover { 110 | background: var(--accent15); 111 | color: #000; 112 | } 113 | 114 | .menu_list .menu_item.disabled:hover, 115 | .menu_list .menu_item.push_button_pressed.disabled:hover { 116 | background: transparent; 117 | color: var(--z3); 118 | } 119 | 120 | .menu_list .menu_item.push_button_pressed { 121 | background: transparent; 122 | border: none; 123 | width: auto; 124 | height: auto; 125 | margin: 0; 126 | } 127 | 128 | .menu_list .menu_item span { 129 | display: block; 130 | position: absolute; 131 | right: var(--x2); 132 | padding: 0 var(--x2); 133 | background: var(--z14); 134 | top: 50%; 135 | transform: translate(0, -50%); 136 | text-align: center; 137 | font-size: 12px; 138 | line-height: var(--x6); 139 | border-radius: var(--x1); 140 | font-weight: 600; 141 | } 142 | 143 | .menu_list .menu_item:not(.disabled):hover span { 144 | background: var(--accent13); 145 | } 146 | 147 | #modal_donate { 148 | display: none; 149 | } 150 | 151 | .menu-right { 152 | margin-left: auto; 153 | } 154 | 155 | .menu-right button { 156 | background: transparent; 157 | line-height: var(--x5); 158 | } 159 | 160 | .menu-right button:hover { 161 | background: transparent; 162 | } 163 | 164 | .menu-right button:active { 165 | box-shadow: none; 166 | outline: none; 167 | } 168 | 169 | .menu-right button:hover svg { 170 | fill: var(--z12); 171 | } 172 | 173 | .menu-right svg { 174 | fill: var(--z5); 175 | } 176 | 177 | .inverted .menu-right svg { 178 | fill: var(--z10); 179 | } 180 | 181 | #logo svg { 182 | pointer-events: none; 183 | margin-top: var(--x2); 184 | fill: var(--z7); 185 | } 186 | 187 | #logo svg path:last-child { 188 | fill: var(--z13); 189 | } 190 | 191 | #logo:hover svg { 192 | fill: var(--z9); 193 | } 194 | 195 | #logo svg path:last-child { 196 | fill: var(--z15); 197 | } 198 | 199 | -------------------------------------------------------------------------------- /src/css/method-draw.css: -------------------------------------------------------------------------------- 1 | /* Comment to prevent wrong concat */ 2 | 3 | body { 4 | background: var(--z1); 5 | font: 14px/120% -apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Oxygen-Sans,Ubuntu,Cantarell,"Helvetica Neue",sans-serif; 6 | -webkit-touch-callout: none; 7 | -webkit-user-select: none; 8 | -khtml-user-select: none; 9 | -moz-user-select: none; 10 | -ms-user-select: none; 11 | user-select: none; 12 | margin: 0; 13 | padding: 0; 14 | } 15 | 16 | body.inverted { 17 | background: var(--z2); 18 | } 19 | 20 | ::selection {background: #000; color: #fff; /* Safari */} 21 | ::-moz-selection {background: #000; color: #fff; /* Firefox */} 22 | 23 | html, body { 24 | overscroll-behavior-x: none; 25 | overflow: hidden; 26 | width: 100%; 27 | height: 100%; 28 | } 29 | 30 | ::-webkit-scrollbar { 31 | width: var(--x1); 32 | height: var(--x1); 33 | background: transparent; 34 | position: absolute; 35 | } 36 | 37 | ::-webkit-scrollbar-track { 38 | border-radius: 10px; 39 | background: transparent; 40 | } 41 | 42 | ::-webkit-scrollbar-thumb { 43 | border-radius: 10px; 44 | background: var(--z3); 45 | } 46 | 47 | ::-webkit-scrollbar-corner { 48 | background: transparent; 49 | } 50 | 51 | .inverted ::-webkit-scrollbar-thumb { 52 | background: var(--z3); 53 | } 54 | 55 | 56 | div#font-selector { 57 | width:140px; 58 | height:300px; 59 | overflow:auto; 60 | margin:0 auto; 61 | position:absolute; 62 | top:27px; 63 | right:0; 64 | border:1px solid black; 65 | padding:10px; 66 | display:none; 67 | background-color:white; 68 | z-index: 10; 69 | border-radius: 3px; 70 | box-shadow: 0 5px 10px rgba(0,0,0,0.7); 71 | } 72 | 73 | div#font-selector img { 74 | width: 100%; 75 | } 76 | 77 | div#font-selector .font-item { 78 | border-bottom: solid #ddd 1px; 79 | padding: 5px 10px; 80 | margin: 0 -10px; 81 | } 82 | 83 | div#font-selector .font-item:hover { 84 | background-color: #eee; 85 | } 86 | 87 | .dropdown_set input[type=text], .dropdown_set input[type=number] { 88 | width: 50px; 89 | } 90 | 91 | input[type=text].wide, input[type=number].wide {width: 110px;} 92 | input[type=text].tuco, input[type=number].tuco {width: 150px;} 93 | 94 | input[type=submit], input[type=button], button { 95 | background: var(--accent7); 96 | color: var(--z15); 97 | border-radius: var(--x1); 98 | padding: var(--x2) var(--x4); 99 | border: none; 100 | line-height: 140%; 101 | font-size: 16px; 102 | font-weight: 550; 103 | cursor: pointer; 104 | } 105 | 106 | input[type=submit]:hover, 107 | input[type=button]:hover, 108 | button:hover { 109 | background: var(--accent9); 110 | } 111 | 112 | input[type=submit]:active, 113 | input[type=button]:active, 114 | button:active { 115 | box-shadow: 0 0 0 var(--x1) var(--accent14); 116 | outline: none; 117 | } 118 | 119 | input[type=submit]:focus, 120 | input[type=button]:focus, 121 | button:focus { 122 | outline: none; 123 | } 124 | 125 | button.cancel, input.Cancel, input.cancel, input.jGraduate_Cancel, button.cancel { 126 | -webkit-appearance: none; 127 | background-color: var(--z12); 128 | color: var(--z3); 129 | margin: 0; 130 | } 131 | 132 | button.cancel:hover, input.Cancel:hover, input.cancel:hover, input.jGraduate_Cancel:hover, button.cancel:hover { 133 | background-color: var(--z13); 134 | } 135 | 136 | button.warning { 137 | -webkit-appearance: none; 138 | background-color: var(--b6); 139 | color: var(--z15); 140 | margin: 0; 141 | } 142 | 143 | button.warning:hover { 144 | background-color: var(--b7); 145 | } 146 | 147 | #svgcontent { 148 | overflow: hidden; 149 | } 150 | 151 | .wireframe #svgcontent * { 152 | fill: none; 153 | stroke: #000; 154 | stroke-width: 1px; 155 | stroke-opacity: 1.0; 156 | stroke-dasharray: 0; 157 | opacity: 1; 158 | pointer-events: stroke; 159 | vector-effect: non-scaling-stroke; 160 | filter: none; 161 | } 162 | 163 | .wireframe #svgcontent text { 164 | fill: #000; 165 | stroke: none; 166 | } 167 | 168 | #canvas_panel { 169 | display: block; 170 | } 171 | 172 | #multiselected_panel .selected_tool { 173 | vertical-align: 12px; 174 | } 175 | 176 | #cur_context_panel { 177 | position: absolute; 178 | top: 47px; 179 | left: 68px; 180 | line-height: 22px; 181 | overflow: auto; 182 | border-bottom: none; 183 | border-right: none; 184 | padding-left: 5px; 185 | font-size: 12px; 186 | background: black; 187 | color: #999; 188 | opacity: 0.5; 189 | padding: 0 10px; 190 | border-radius: 0 10px 10px 0; 191 | } 192 | 193 | #cur_context_panel a { 194 | float: none; 195 | text-decoration: none; 196 | color: #fff; 197 | } 198 | 199 | #cur_context_panel a:hover { 200 | text-decoration: underline; 201 | } 202 | 203 | #main_menu li#tool_open, #main_menu li#tool_import { 204 | position: relative; 205 | overflow: hidden; 206 | } 207 | 208 | #tool_image { 209 | overflow: hidden; 210 | } 211 | 212 | .disabled { 213 | opacity: 0.5; 214 | cursor: default; 215 | } 216 | 217 | .width_label { 218 | padding-right: 5px; 219 | } 220 | 221 | #tool_bold span, #tool_italic span { 222 | position: absolute; 223 | width: 100%; 224 | height: 100%; 225 | top: 0; left: 0; 226 | background: #ccc; 227 | opacity: 0; 228 | } 229 | 230 | #tools_bottom { 231 | position: absolute; 232 | left: var(--x12); 233 | right: 0; 234 | bottom: 0; 235 | height: var(--x10); 236 | overflow: visible; 237 | } 238 | 239 | ul li.current { 240 | background-color: #F4E284; 241 | } 242 | 243 | .toolbar_button button .svg_icon { 244 | display: none; 245 | } 246 | 247 | .invisible { 248 | visibility: none; 249 | } 250 | 251 | /* For modern browsers */ 252 | .clearfix:before, 253 | .clearfix:after { 254 | content:""; 255 | display:table; 256 | } 257 | 258 | .clearfix:after { 259 | clear:both; 260 | } 261 | 262 | input[readonly=readonly]:focus { 263 | box-shadow: none; 264 | } 265 | 266 | #color_canvas_tools, #fill_bg, #stroke_bg { 267 | background: var(--z15) url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAAMAQMAAABsu86kAAAAA3NCSVQICAjb4U/gAAAABlBMVEXu7u7///8o06qaAAAACXBIWXMAAAsSAAALEgHS3X78AAAAFXRFWHRDcmVhdGlvbiBUaW1lADcvMjIvMTL7FNdCAAAAHHRFWHRTb2Z0d2FyZQBBZG9iZSBGaXJld29ya3MgQ1M1cbXjNgAAABFJREFUCJljYP7AgIb+MKAhAM8/C5vWL6zSAAAAAElFTkSuQmCC) top left repeat; 268 | } 269 | 270 | #color_canvas_tools { 271 | width: 60px; 272 | height: 40px; 273 | margin: 23px 5px 5px 5px; 274 | position: relative; 275 | overflow: hidden; 276 | } 277 | 278 | #color_canvas_tools { 279 | display: block; 280 | } 281 | 282 | #stroke_style_label { 283 | font-size: 30px; 284 | margin-top: 33px; 285 | letter-spacing: -1px; 286 | } 287 | 288 | .stroke_tool .caret { 289 | top: 60%; 290 | } -------------------------------------------------------------------------------- /src/css/modal.css: -------------------------------------------------------------------------------- 1 | .modal { 2 | background: rgba(50,50,50,0.8); 3 | position: absolute; 4 | z-index: 1000; 5 | top: 0; 6 | left: 0; 7 | right: 0; 8 | bottom: 0; 9 | display: flex; 10 | align-items: center; 11 | justify-content: center; 12 | opacity: 1; 13 | font-size: 16px; 14 | line-height: 150%; 15 | } 16 | 17 | .modal.hidden { 18 | opacity: 0; 19 | pointer-events: none; 20 | transition: opacity 300ms ease; 21 | } 22 | 23 | .modal-item { 24 | background-color: white; 25 | position: relative; 26 | border-radius: var(--x1); 27 | padding: var(--x12); 28 | max-width: 600px; 29 | margin: 32px; 30 | } 31 | 32 | .modal-item.modal-item-wide { 33 | max-width: none; 34 | width: 900px; 35 | } 36 | 37 | .modal-item.modal-item-source { 38 | height: 504px; 39 | max-width: none; 40 | width: 704px; 41 | } 42 | 43 | .modal h1:first-child { 44 | margin-top: 0; 45 | } -------------------------------------------------------------------------------- /src/css/palette.css: -------------------------------------------------------------------------------- 1 | 2 | #palette { 3 | display: block; 4 | position: absolute; 5 | z-index: 2; 6 | left: 10px; 7 | bottom: 5px; 8 | width: 410px; 9 | right: 145px; 10 | height: 30px; 11 | } 12 | 13 | .palette_item { 14 | height: 20%; 15 | width: 5.2%; 16 | float: left; 17 | cursor: url(../images/eyedropper.svg) 0 16, crosshair; 18 | } 19 | 20 | .palette_item.transparent, .palette_item.white, .palette_item.black { 21 | background: #fff; 22 | position: absolute; 23 | width: 10px; 24 | height: 10px; 25 | left: -10px; 26 | top: 0; 27 | } 28 | 29 | .palette_item.transparent { 30 | background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAABx0RVh0U29mdHdhcmUAQWRvYmUgRmlyZXdvcmtzIENTNXG14zYAAAAVdEVYdENyZWF0aW9uIFRpbWUAOC8yMi8xMg1cp6sAAABESURBVBiVjdBBCgAgCETRr/e/87SJKFPLnfIcQRMIwMBIShIAvgZzoSrfkzrs8WyF1+kX9r3p8AE7nL7kSpSshN+JEQ8GixkF0mSevgAAAABJRU5ErkJggg==); 31 | } 32 | 33 | .palette_item.black { 34 | background: #000; 35 | top: 10px; 36 | } 37 | 38 | .palette_item.white { 39 | background: #fff; 40 | top: 20px; 41 | } 42 | -------------------------------------------------------------------------------- /src/css/panel.css: -------------------------------------------------------------------------------- 1 | #panels { 2 | position: absolute; 3 | box-sizing: border-box; 4 | width: var(--panel-width); 5 | right: var(--x1); 6 | top: var(--x5); 7 | bottom: 0; 8 | border-bottom: none; 9 | overflow: auto; 10 | padding: 0; 11 | } 12 | 13 | .context_panel { 14 | display: none; 15 | } 16 | 17 | .context_panel h4 { 18 | color: var(--z15); 19 | font-weight: normal; 20 | margin: 0; 21 | padding: 10px 0 5px 0; 22 | } 23 | 24 | .context_panel label { 25 | display: block; 26 | color: var(--z13); 27 | white-space: nowrap; 28 | } 29 | 30 | #align_tools { 31 | display: none; 32 | } 33 | 34 | #panels.multiselected #multiselected_panel { 35 | display: block !important; 36 | } 37 | 38 | #panels.multiselected #multiselected_panel .hidable { 39 | display: none; 40 | } 41 | 42 | #tool_text_on_path { 43 | display: none !important; 44 | } -------------------------------------------------------------------------------- /src/css/rulers.css: -------------------------------------------------------------------------------- 1 | #rulers > div { 2 | position: absolute; 3 | background: var(--z2); 4 | z-index: 1; 5 | overflow: hidden; 6 | -webkit-font-smoothing: none; 7 | } 8 | 9 | .inverted #rulers > div { 10 | background: var(--z1); 11 | } 12 | 13 | #rulers #ruler_corner { 14 | top: var(--x8); 15 | left: var(--x12); 16 | width: var(--x4); 17 | height: var(--x4); 18 | z-index: 2; 19 | border-radius: var(--x2) 0 0 0; 20 | } 21 | 22 | #ruler_x { 23 | height: var(--x4); 24 | top: var(--x8); 25 | left: var(--x12); 26 | right: calc(var(--panel-width) + var(--x4));; 27 | overflow: hidden; 28 | border-radius: var(--x2); 29 | } 30 | 31 | #ruler_y { 32 | width: var(--x4); 33 | top: var(--x8); 34 | left: var(--x12); 35 | bottom: var(--x10); 36 | overflow: hidden; 37 | border-radius: var(--x2); 38 | } 39 | 40 | #ruler_x > div, 41 | #ruler_y > div { 42 | overflow: hidden; 43 | } 44 | -------------------------------------------------------------------------------- /src/css/select.css: -------------------------------------------------------------------------------- 1 | .select-input { 2 | margin: var(--x2) 0; 3 | position: relative; 4 | } 5 | 6 | .select-input:after { 7 | content: '↓'; 8 | font-weight: bold; 9 | position: absolute; 10 | right: var(--x6); 11 | top: 50%; 12 | color: var(--z7); 13 | transform: translate(0, -50%); 14 | } 15 | 16 | .select-input select { 17 | -webkit-appearance: none; 18 | background-color: var(--z3); 19 | color: var(--z14); 20 | height: var(--x8); 21 | display: block; 22 | width: calc(100% - var(--x2)); 23 | padding: 0 var(--x4); 24 | border: none; 25 | border-radius: var(--x1); 26 | } 27 | 28 | .select-input select:focus { 29 | outline: none; 30 | box-shadow: 0 0 0 1px var(--accent7); 31 | 32 | } -------------------------------------------------------------------------------- /src/css/shapelib.css: -------------------------------------------------------------------------------- 1 | #shape_buttons { 2 | overflow: auto; 3 | top: 0; 4 | bottom: 0; 5 | left: 136px; 6 | right: 0; 7 | position: absolute; 8 | vertical-align: top; 9 | } 10 | 11 | #shape_cats { 12 | min-width: 110px; 13 | display: block; 14 | position: absolute; 15 | left: 0; 16 | top: 0; 17 | height: 320px; 18 | background: var(--z14); 19 | border-radius: var(--x1) 0 0 var(--x1); 20 | z-index: 2; 21 | } 22 | 23 | #shape_cats > div { 24 | padding: 0 var(--x4); 25 | background: var(--z14); 26 | color: var(--z6); 27 | height: var(--x7); 28 | line-height: var(--x7); 29 | transition: background-color 100ms ease; 30 | } 31 | 32 | #shape_cats > div:first-child { 33 | border-radius: var(--x1) 0 0 0; 34 | } 35 | 36 | #shape_cats > div:last-child { 37 | border-radius: 0 0 0 var(--x1); 38 | } 39 | 40 | #shape_cats div:hover { 41 | color: var(--z0); 42 | } 43 | 44 | #shape_cats div.current { 45 | font-weight: bold; 46 | background: var(--accent7); 47 | color: var(--z15); 48 | } 49 | 50 | .tools_flyout { 51 | position: absolute; 52 | display: none; 53 | cursor: pointer; 54 | width: 410px; 55 | z-index: 10; 56 | left: var(--x12); 57 | top: 230px; 58 | height: 336px; 59 | background: var(--z15); 60 | border-radius: var(--x1); 61 | box-shadow: var(--shadow); 62 | } 63 | 64 | .tools_flyout .tool_button { 65 | float: left; 66 | background-color: var(--z15); 67 | height: var(--x6); 68 | width: var(--x6); 69 | } 70 | 71 | /* Necessary to keep the flyouts sized properly */ 72 | #shape_buttons {background: var(--z15); border-radius: 0 3px 3px 0; padding: 10px} 73 | 74 | .tools_flyout .tool_button, 75 | .tools_flyout .tool_flyout { 76 | background: var(--z15); 77 | width: var(--x10); 78 | height: var(--x10); 79 | margin: var(--x1); 80 | } 81 | 82 | .tools_flyout .tool_button svg, 83 | .inverted .tools_flyout .tool_button svg { 84 | fill: var(--z4); 85 | } 86 | 87 | .tools_flyout .tool_button:hover svg { 88 | fill: var(--z1); 89 | } 90 | 91 | -------------------------------------------------------------------------------- /src/css/source.css: -------------------------------------------------------------------------------- 1 | #svg_source_editor { 2 | position: relative; 3 | width: 800px; 4 | height: 600px; 5 | margin: -48px; 6 | } 7 | 8 | #svg_source_container { 9 | width: 800px; 10 | position: relative; 11 | height: 100%; 12 | } 13 | 14 | #svg_source_editor form { 15 | position: absolute; 16 | top: 0; 17 | bottom: 55px; 18 | left: 0; 19 | right: 0; 20 | font-size: 14px; 21 | background-color: var(--z14); 22 | border-radius: 8px 8px 0 0; 23 | } 24 | 25 | #svg_source_textarea { 26 | width: calc(100% - 64px); 27 | height: calc(100% - 64px); 28 | line-height: 140%; 29 | font-family: var(--mono-font); 30 | font-size: 14px; 31 | color: var(--z4); 32 | border: none; 33 | padding: 32px; 34 | background-color: transparent; 35 | } 36 | 37 | #svg_source_editor #tool_source_back { 38 | position: absolute; 39 | display: flex; 40 | justify-content: space-between; 41 | bottom: 0; 42 | left: 0; 43 | right: 0; 44 | padding: var(--x2) var(--x2); 45 | } 46 | -------------------------------------------------------------------------------- /src/css/sponsors.css: -------------------------------------------------------------------------------- 1 | #sponsors { 2 | display: none; 3 | position: absolute; 4 | right: calc(var(--panel-width) + var(--x4)); 5 | } 6 | 7 | .sponsor a { 8 | transition: all 200ms ease; 9 | background-color: var(--z3); 10 | line-height: var(--x6); 11 | margin: var(--x1) 0; 12 | border-radius: var(--x1); 13 | display: inline-block; 14 | text-decoration: none; 15 | text-transform: uppercase; 16 | font-weight: 600; 17 | color: var(--z14); 18 | padding: 0 var(--x2); 19 | font-size: 12px; 20 | position: relative; 21 | } 22 | 23 | .sponsor a:hover { 24 | color: var(--z15); 25 | background: var(--accent7); 26 | } 27 | 28 | .sponsor .deta { 29 | padding-left: var(--x7); 30 | position: relative; 31 | } 32 | 33 | .sponsor .deta span { 34 | display: block; 35 | position: absolute; 36 | border-radius: 100%; 37 | top: 2px; 38 | left: var(--x1); 39 | transform: scale(0.6); 40 | transition: transform var(--transition-duration) ease; 41 | } 42 | 43 | .sponsors .deta span:nth-child(1) { 44 | width: var(--x5); 45 | height: var(--x5); 46 | background-color: #EF39A8; 47 | margin: 0; 48 | transition-delay: 0; 49 | } 50 | 51 | .sponsors .deta span:nth-child(2) { 52 | width: var(--x4); 53 | height: var(--x4); 54 | background-color: #BD399C; 55 | margin: 2px; 56 | transition-delay: 100ms; 57 | } 58 | 59 | .sponsors .deta span:nth-child(3) { 60 | width: var(--x3); 61 | height: var(--x3); 62 | background-color: #93388E; 63 | margin: 4px; 64 | transition-delay: 200ms; 65 | } 66 | 67 | .sponsors .deta span:nth-child(4) { 68 | width: var(--x2); 69 | height: var(--x2); 70 | background-color: rgb(96, 48, 162); 71 | margin: 6px; 72 | transition-delay: 300ms; 73 | } 74 | 75 | .sponsors .deta:hover span { 76 | transform: scale(0.9); 77 | } 78 | 79 | .sponsor a:hover { 80 | color: var(--z0); 81 | background: var(--accent7); 82 | } 83 | 84 | .sponsor a:hover + .sponsor-description { 85 | opacity: 1; 86 | transform: translate(0,0); 87 | transform: translate3d(0,0,0); 88 | } 89 | 90 | .sponsor-description { 91 | transition: all var(--transition-duration) ease; 92 | position: absolute; 93 | right: 0; 94 | top: var(--x10); 95 | width: calc(var(--x16)*4); 96 | z-index: 100; 97 | background: var(--z15); 98 | padding: var(--x6); 99 | border-radius: var(--x1); 100 | line-height: 150%; 101 | box-shadow: var(--shadow); 102 | margin-left: 0; 103 | opacity: 0; 104 | pointer-events: none; 105 | transform: translate(0,8px); 106 | transform: translate3d(0,8px,0); 107 | } 108 | 109 | .sponsor-description:after { 110 | content: ''; 111 | border: solid transparent var(--x2); 112 | border-bottom-color: var(--z15); 113 | position: absolute; 114 | top: calc(var(--x4)*-1); 115 | right: var(--x10); 116 | } 117 | 118 | .sponsor-description p { 119 | color: var(--z6); 120 | } 121 | 122 | .sponsor-description strong { 123 | display: block; 124 | margin-bottom: var(--x2); 125 | } 126 | 127 | .sponsor-logo { 128 | display: block; 129 | width: 180px; 130 | margin: var(--x4) auto var(--x1); 131 | } 132 | 133 | .sponsor-disclaimer { 134 | color: var(--z11); 135 | text-align: center; 136 | font-size: 0.8em; 137 | } 138 | -------------------------------------------------------------------------------- /src/css/text.css: -------------------------------------------------------------------------------- 1 | #group_title {display: none;} 2 | 3 | #tool_bold, #tool_italic { 4 | font: bold 20px/35px serif; 5 | text-align: center; 6 | position: absolute; 7 | padding: 0 0 0 0; 8 | color: var(--z13); 9 | background: transparent; 10 | border: none; 11 | margin: 0; 12 | width: 50%; 13 | top: var(--x8); 14 | line-height: var(--x10); 15 | bottom: 0; 16 | border-top: solid var(--z1) 2px; 17 | cursor: pointer; 18 | } 19 | 20 | #tool_bold { 21 | right: 0; 22 | } 23 | 24 | #tool_italic { 25 | border-right: solid var(--z1) 2px; 26 | line-height: var(--x9); 27 | left: 0; 28 | font-weight: bold; 29 | font-style: italic; 30 | font-size: 24px; 31 | } 32 | 33 | #tool_bold:hover, #tool_italic:hover { 34 | color: var(--z15); 35 | background-color: var(--z3); 36 | } 37 | 38 | #tool_bold.disabled, 39 | #tool_italic.disabled { 40 | opacity: 1; 41 | color: var(--z4); 42 | pointer-events: none; 43 | } 44 | 45 | #tool_bold.active, #tool_italic.active { 46 | color: #50A0FF; 47 | } 48 | 49 | #preview_font { 50 | color: var(--z15); 51 | font-size: 2em; 52 | line-height: 80px; 53 | padding: 0 var(--x2); 54 | white-space: nowrap; 55 | } 56 | 57 | #preview_font + .caret { 58 | right: var(--x2); 59 | } 60 | 61 | .draginput:hover #preview_font:after { 62 | border-right-color: var(--z3); 63 | background: linear-gradient(to right, rgba(0,0,0,0), var(--z3)); 64 | } 65 | 66 | #preview_font:after { 67 | content: ''; 68 | position: absolute; 69 | right: 0; 70 | top: 3px; 71 | bottom: 3px; 72 | width: 15px; 73 | border-right: solid var(--z2) 10px; 74 | background: linear-gradient(to right, rgba(0,0,0,0), var(--z2)); 75 | } 76 | 77 | #textpath-panel { 78 | display: none; 79 | } -------------------------------------------------------------------------------- /src/css/tools.css: -------------------------------------------------------------------------------- 1 | #tools_left { 2 | position: absolute; 3 | border-right: none; 4 | width: var(--x12); 5 | top: var(--x8); 6 | bottom: 0; 7 | left: 0; 8 | z-index: 4; 9 | } 10 | 11 | .tool_button { 12 | height: var(--x12); 13 | width: var(--x12); 14 | cursor: pointer; 15 | line-height: 60px; 16 | text-align: center; 17 | transform: scale(0.8); 18 | } 19 | 20 | .inverted .tool_button svg { 21 | fill: var(--z13); 22 | } 23 | 24 | .tool_button:hover { 25 | transform: scale(1); 26 | } 27 | 28 | .tool_button:hover svg { 29 | fill: var(--z13); 30 | } 31 | 32 | .tool_button.current { 33 | transform: scale(1.4); 34 | } 35 | 36 | .tool_button svg { 37 | vertical-align: center; 38 | fill: var(--z5); 39 | } 40 | 41 | .tool_button.current svg { 42 | fill: var(--accent8); 43 | } 44 | 45 | .tool_button.current svg { 46 | fill: var(--accent7); 47 | } 48 | 49 | 50 | .tool_button.disabled svg { 51 | background-color: #aaa; 52 | cursor: not-allowed; 53 | fill: var(--z3); 54 | pointer-events: none; 55 | } 56 | 57 | #tool_rect svg { 58 | position: relative; 59 | top: -1px; 60 | left: 1px; 61 | } 62 | 63 | #tool_ellipse svg { 64 | position: relative; 65 | top: 2px; 66 | left: 1px; 67 | } -------------------------------------------------------------------------------- /src/css/zoom-dropdown.css: -------------------------------------------------------------------------------- 1 | #zoom_panel, 2 | #zoom_select { 3 | padding: var(--x2) 0; 4 | right: calc(var(--panel-width) + var(--x2)); 5 | position: absolute; 6 | cursor: pointer; 7 | width: 100px; 8 | } 9 | 10 | #zoom_label:after { 11 | content: ''; 12 | } 13 | 14 | #zoom_select { 15 | opacity: 0; 16 | z-index: 100; 17 | } 18 | 19 | #zoom_select:hover + div { 20 | opacity: 1; 21 | } 22 | 23 | #zoom_label { 24 | position: absolute; 25 | background: transparent; 26 | width: 100%; 27 | margin: 0; 28 | } 29 | 30 | #zoom_panel .caret { 31 | margin-top: -5px; 32 | } 33 | 34 | #zoom_label img, #zoom_label svg { 35 | width: var(--x4); 36 | height: var(--x4); 37 | } 38 | 39 | #zoomLabel { 40 | width: var(--x4); 41 | height: var(--x4); 42 | cursor: pointer; 43 | } 44 | 45 | #zoomLabel svg { 46 | fill: var(--z10); 47 | position: relative; 48 | top: 2px; 49 | } 50 | 51 | #zoom_label input { 52 | -webkit-appearance: none; 53 | color: var(--z10); 54 | font-size: 13px; 55 | height: auto; 56 | width: var(--x12); 57 | padding: 0; 58 | cursor: default; 59 | position: absolute; 60 | background: transparent; 61 | border: none; 62 | top: 2px; 63 | left: var(--x6); 64 | pointer-events: none; 65 | } 66 | 67 | #zoom_label select { 68 | -webkit-appearance: none; 69 | position: absolute; 70 | top: 0; 71 | left: 0; 72 | bottom: 0; 73 | right: 0; 74 | } 75 | 76 | #zoom_label span { 77 | top: 0; 78 | left: 0; 79 | } -------------------------------------------------------------------------------- /src/extensions/ext-shapes.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /src/images/AlphaBar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/methodofaction/Method-Draw/c01c2f72b4497027eecb2a27e066c9bc5bf754c0/src/images/AlphaBar.png -------------------------------------------------------------------------------- /src/images/Bars.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/methodofaction/Method-Draw/c01c2f72b4497027eecb2a27e066c9bc5bf754c0/src/images/Bars.png -------------------------------------------------------------------------------- /src/images/Maps.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/methodofaction/Method-Draw/c01c2f72b4497027eecb2a27e066c9bc5bf754c0/src/images/Maps.png -------------------------------------------------------------------------------- /src/images/NoColor.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /src/images/bar-opacity.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/methodofaction/Method-Draw/c01c2f72b4497027eecb2a27e066c9bc5bf754c0/src/images/bar-opacity.png -------------------------------------------------------------------------------- /src/images/drag.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/methodofaction/Method-Draw/c01c2f72b4497027eecb2a27e066c9bc5bf754c0/src/images/drag.png -------------------------------------------------------------------------------- /src/images/dragging.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/methodofaction/Method-Draw/c01c2f72b4497027eecb2a27e066c9bc5bf754c0/src/images/dragging.png -------------------------------------------------------------------------------- /src/images/eyedropper.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/methodofaction/Method-Draw/c01c2f72b4497027eecb2a27e066c9bc5bf754c0/src/images/eyedropper.png -------------------------------------------------------------------------------- /src/images/eyedropper.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /src/images/favicon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/methodofaction/Method-Draw/c01c2f72b4497027eecb2a27e066c9bc5bf754c0/src/images/logo.png -------------------------------------------------------------------------------- /src/images/map-opacity.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/methodofaction/Method-Draw/c01c2f72b4497027eecb2a27e066c9bc5bf754c0/src/images/map-opacity.png -------------------------------------------------------------------------------- /src/images/mappoint.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/methodofaction/Method-Draw/c01c2f72b4497027eecb2a27e066c9bc5bf754c0/src/images/mappoint.gif -------------------------------------------------------------------------------- /src/images/mappoint_c.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/methodofaction/Method-Draw/c01c2f72b4497027eecb2a27e066c9bc5bf754c0/src/images/mappoint_c.png -------------------------------------------------------------------------------- /src/images/mappoint_f.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/methodofaction/Method-Draw/c01c2f72b4497027eecb2a27e066c9bc5bf754c0/src/images/mappoint_f.png -------------------------------------------------------------------------------- /src/images/pencil_cursor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/methodofaction/Method-Draw/c01c2f72b4497027eecb2a27e066c9bc5bf754c0/src/images/pencil_cursor.png -------------------------------------------------------------------------------- /src/images/placeholder.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /src/images/preview-opacity.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/methodofaction/Method-Draw/c01c2f72b4497027eecb2a27e066c9bc5bf754c0/src/images/preview-opacity.png -------------------------------------------------------------------------------- /src/images/rangearrows.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /src/images/rangearrows2.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /src/images/rotate.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/methodofaction/Method-Draw/c01c2f72b4497027eecb2a27e066c9bc5bf754c0/src/images/rotate.png -------------------------------------------------------------------------------- /src/js/Canvas.js: -------------------------------------------------------------------------------- 1 | MD.Canvas = function(){ 2 | 3 | const el = document.getElementById("svgcanvas"); 4 | var workarea = document.getElementById("workarea"); 5 | 6 | workarea.addEventListener("mouseup", function(){ 7 | const mode = svgCanvas.getMode(); 8 | // todo why? 9 | //if (mode !== "textedit" && mode !== "pathedit") state.set("canvasMode", mode); 10 | workarea.className = mode; 11 | }) 12 | 13 | $('#resolution').change(function(){ 14 | var w = $('#canvas_width')[0]; 15 | var h = $('#canvas_height')[0]; 16 | if(!this.selectedIndex) { 17 | $('#resolution_label').html("Custom"); 18 | w.removeAttribute("readonly"); 19 | w.focus(); 20 | w.select(); 21 | if(w.value === 'fit') { 22 | w.value = 100 23 | h.value = 100 24 | } 25 | } else if(this.value === 'content') { 26 | $('#resolution_label').html("Custom"); 27 | w.value = 'fit' 28 | h.value = 'fit' 29 | changeSize(); 30 | var res = svgCanvas.getResolution() 31 | w.value = res.w 32 | h.value = res.h 33 | 34 | } else { 35 | var dims = this.value.split('x'); 36 | dims[0] = parseInt(dims[0]); 37 | dims[1] = parseInt(dims[1]); 38 | var diff_w = dims[0] - w.value; 39 | var diff_h = dims[1] - h.value; 40 | //animate 41 | var start = Date.now(); 42 | var duration = 1000; 43 | var animateCanvasSize = function(timestamp) { 44 | var progress = Date.now() - start; 45 | var tick = progress / duration; 46 | tick = (Math.pow((tick-1), 3) +1); 47 | w.value = (dims[0] - diff_w + (tick*diff_w)).toFixed(0); 48 | h.value = (dims[1] - diff_h + (tick*diff_h)).toFixed(0); 49 | changeSize(); 50 | if (tick >= 1) { 51 | var res = svgCanvas.getResolution() 52 | $('#canvas_width').val(res.w.toFixed()) 53 | $('#canvas_height').val(res.h.toFixed()) 54 | $('#resolution_label').html("
" + res.w + "×
" + res.h + "
"); 55 | } 56 | else { 57 | requestAnimationFrame(animateCanvasSize) 58 | } 59 | } 60 | animateCanvasSize() 61 | 62 | } 63 | }); 64 | 65 | function resize(w, h){ 66 | const res = svgCanvas.setResolution(w, h); 67 | if (!res) return $.alert("No content to fit to"); 68 | if (w === 'fit' || h === 'fit') state.set("canvasSize", res); 69 | $("#canvas_width").val(w); 70 | $("#canvas_height").val(h); 71 | } 72 | 73 | function changeSize(attr, val, completed){ 74 | const w = $("#canvas_width").val(); 75 | const h = $("#canvas_height").val(); 76 | state.set("canvasSize", [w,h]); 77 | if (completed) editor.saveCanvas(); 78 | } 79 | 80 | function update(center, new_ctr) { 81 | var w = $(workarea).width(), h = $(workarea).height(); 82 | var w_orig = w, h_orig = h; 83 | var zoom = svgCanvas.getZoom(); 84 | var cnvs = $("#svgcanvas"); 85 | 86 | var old_ctr = { 87 | x: workarea.scrollLeft + w_orig/2, 88 | y: workarea.scrollTop + h_orig/2 89 | }; 90 | 91 | var multi = 2; 92 | w = Math.max(w_orig, svgCanvas.contentW * zoom * multi); 93 | h = Math.max(h_orig, svgCanvas.contentH * zoom * multi); 94 | 95 | workarea.style.overflow = (w == w_orig && h == h_orig) ? "hidden" : "scroll"; 96 | 97 | var old_can_y = cnvs.height()/2; 98 | var old_can_x = cnvs.width()/2; 99 | cnvs.width(w).height(h); 100 | var new_can_y = h/2; 101 | var new_can_x = w/2; 102 | var offset = svgCanvas.updateCanvas(w, h); 103 | 104 | var ratio = new_can_x / old_can_x; 105 | 106 | var scroll_x = w/2 - w_orig/2; 107 | var scroll_y = h/2 - h_orig/2; 108 | 109 | if(!new_ctr) { 110 | 111 | var old_dist_x = old_ctr.x - old_can_x; 112 | var new_x = new_can_x + old_dist_x * ratio; 113 | 114 | var old_dist_y = old_ctr.y - old_can_y; 115 | var new_y = new_can_y + old_dist_y * ratio; 116 | 117 | new_ctr = { 118 | x: new_x, 119 | y: new_y 120 | }; 121 | 122 | } else { 123 | new_ctr.x += offset.x, 124 | new_ctr.y += offset.y; 125 | } 126 | 127 | if(center) { 128 | workarea.scrollLeft = scroll_x; 129 | workarea.scrollTop = scroll_y; 130 | 131 | } else { 132 | workarea.scrollLeft = new_ctr.x - w_orig/2; 133 | workarea.scrollTop = new_ctr.y - h_orig/2; 134 | } 135 | 136 | editor.rulers.update(); 137 | workarea.scroll(); 138 | } 139 | 140 | function rename(str) { 141 | if (str.length) { 142 | $('#canvas_title').val(str); 143 | svgCanvas.setDocumentTitle(str); 144 | } 145 | } 146 | 147 | rename(state.get("canvasTitle")); 148 | 149 | this.resize = resize; 150 | this.update = update; 151 | this.rename = rename; 152 | this.changeSize = changeSize; 153 | } -------------------------------------------------------------------------------- /src/js/ContextMenu.js: -------------------------------------------------------------------------------- 1 | MD.ContextMenu = function(){ 2 | $("#workarea").contextMenu({ 3 | menu: 'cmenu_canvas', 4 | inSpeed: 0 5 | }, 6 | function(action, el, pos) { 7 | switch ( action ) { 8 | case 'delete': 9 | editor.deleteSelected(); 10 | break; 11 | case 'cut': 12 | editor.cutSelected(); 13 | break; 14 | case 'copy': 15 | editor.copySelected(); 16 | break; 17 | case 'paste': 18 | editor.pasteSelected(); 19 | break; 20 | case 'paste_in_place': 21 | svgCanvas.pasteElements('in_place'); 22 | break; 23 | case 'group': 24 | editor.groupSelected(); 25 | break; 26 | case 'ungroup': 27 | editor.ungroupSelected(); 28 | break; 29 | case 'move_front': 30 | editor.moveToTopSelected(); 31 | break; 32 | case 'move_up': 33 | editor.moveUpSelected(); 34 | break; 35 | case 'move_down': 36 | editor.moveDownSelected(); 37 | break; 38 | case 'move_back': 39 | editor.moveToBottomSelected(); 40 | break; 41 | default: 42 | if(svgedit.contextmenu && svgedit.contextmenu.hasCustomHandler(action)){ 43 | svgedit.contextmenu.getCustomHandler(action).call(); 44 | } 45 | break; 46 | } 47 | 48 | }); 49 | 50 | $('.contextMenu li').mousedown(function(ev) { 51 | ev.preventDefault(); 52 | }) 53 | 54 | $('#cmenu_canvas li').disableContextMenu(); 55 | $("#cmenu_canvas").enableContextMenuItems('#delete,#cut,#copy'); 56 | } -------------------------------------------------------------------------------- /src/js/Darkmode.js: -------------------------------------------------------------------------------- 1 | MD.Darkmode = function(){ 2 | 3 | const button = document.querySelector("#darkmode-button"); 4 | const body = document.body; 5 | if (!button) return false; 6 | 7 | function set(isDark) { 8 | 9 | button.setAttribute("title", isDark ? "Switch to lightmode" : "Switch to darkmode") 10 | body.classList.toggle("inverted", !isDark); 11 | body.classList.add("cancel-transitions"); 12 | setTimeout(function(){ 13 | body.classList.remove("cancel-transitions"); 14 | }, 0) 15 | editor.rulers.update(); 16 | } 17 | 18 | button.addEventListener("click", ()=>{ 19 | state.set("darkmode", !state.get("darkmode")); 20 | }); 21 | 22 | const isDark = state.get("darkmode"); 23 | set(isDark); 24 | 25 | this.set = set 26 | 27 | } -------------------------------------------------------------------------------- /src/js/Image.js: -------------------------------------------------------------------------------- 1 | MD.Image = function(){ 2 | 3 | const reader = new FileReader(); 4 | 5 | function importImage(e){ 6 | const file = (e.type === "drop") ? e.dataTransfer.files[0] : this.files[0]; 7 | if (!file || file.type.indexOf("image") ) return alert("That doesn't seem to be an image"); 8 | 9 | if(file.type.indexOf("svg") != -1) importSvg(file) 10 | else importImageFile(file); 11 | } 12 | 13 | function importImageFile(file) { 14 | 15 | reader.onloadend = function(e) { 16 | // lets insert the new image until we know its dimensions 17 | insertNewImage = function(img_width, img_height){ 18 | var newImage = svgCanvas.addSvgElementFromJson({ 19 | "element": "image", 20 | "attr": { 21 | "x": 0, 22 | "y": 0, 23 | "width": img_width, 24 | "height": img_height, 25 | "id": svgCanvas.getNextId(), 26 | "style": "pointer-events:inherit" 27 | } 28 | }); 29 | svgCanvas.setHref(newImage, e.target.result); 30 | svgCanvas.selectOnly([newImage]) 31 | svgCanvas.alignSelectedElements("m", "page") 32 | svgCanvas.alignSelectedElements("c", "page") 33 | updateContextPanel(); 34 | } 35 | // put a placeholder img so we know the default dimensions 36 | var img_width = 100; 37 | var img_height = 100; 38 | var img = new Image() 39 | img.src = e.target.result 40 | document.body.appendChild(img); 41 | img.onload = function() { 42 | img_width = img.offsetWidth 43 | img_height = img.offsetHeight 44 | insertNewImage(img_width, img_height); 45 | document.body.removeChild(img); 46 | } 47 | }; 48 | reader.readAsDataURL(file) 49 | } 50 | 51 | function importSvgFile(file){ 52 | reader.onloadend = function(e) { 53 | importSvg(e.target.result); 54 | }; 55 | reader.readAsText(file); 56 | } 57 | 58 | function importSvg(string) { 59 | svgCanvas.importSvgString(e.target.result, true); 60 | svgCanvas.alignSelectedElements("m", "page") 61 | svgCanvas.alignSelectedElements("c", "page") 62 | } 63 | 64 | const workarea = document.getElementById("workarea"); 65 | 66 | workarea.addEventListener('dragenter', function(e){ 67 | e.stopPropagation(); 68 | e.preventDefault(); 69 | workarea.style.transform = "scale(1.1)"; 70 | }, false) 71 | 72 | workarea.addEventListener('dragleave', function(e){ 73 | e.stopPropagation(); 74 | e.preventDefault(); 75 | workarea.style.transform = "scale(1)"; 76 | }, false) 77 | 78 | workarea.addEventListener('drop', function(e){ 79 | e.stopPropagation(); 80 | e.preventDefault(); 81 | workarea.style.transform = "scale(1)"; 82 | importImage(e); 83 | }, false) 84 | 85 | 86 | this.importImage = importImage; 87 | } 88 | 89 | -------------------------------------------------------------------------------- /src/js/Import.js: -------------------------------------------------------------------------------- 1 | MD.Import = function(){ 2 | const workarea = document.getElementById("workarea"); 3 | const $importInput = $('#tool_import input'); 4 | const $openInput = $('#tool_open input'); 5 | 6 | $importInput.on("change", importImage); 7 | $openInput.on("change", openImage); 8 | 9 | function importImage(e){ 10 | $('#menu_bar').removeClass('active') 11 | if (!window.FileReader) return; 12 | //e.stopPropagation(); 13 | //e.preventDefault(); 14 | workarea.removeAttribute("style"); 15 | $('#main_menu').hide(); 16 | var file = null; 17 | if (e.type === "drop") file = e.dataTransfer.files[0] 18 | else file = this.files[0]; 19 | if (!file) return $.alert("File not found"); 20 | if (file.type.indexOf("image") === -1) return $.alert("File is not image"); 21 | 22 | //svg handing 23 | if(file.type.indexOf("svg") != -1) { 24 | var reader = new FileReader(); 25 | reader.onloadend = function(e) { 26 | svgCanvas.importSvgString(e.target.result, true); 27 | //svgCanvas.ungroupSelectedElement(); 28 | svgCanvas.alignSelectedElements("m", "page"); 29 | svgCanvas.alignSelectedElements("c", "page"); 30 | }; 31 | reader.readAsText(file); 32 | } 33 | 34 | //image handling 35 | else { 36 | var reader = new FileReader(); 37 | reader.onloadend = function(e) { 38 | // lets insert the new image until we know its dimensions 39 | insertNewImage = function(img_width, img_height){ 40 | var newImage = svgCanvas.addSvgElementFromJson({ 41 | "element": "image", 42 | "attr": { 43 | "x": 0, 44 | "y": 0, 45 | "width": img_width, 46 | "height": img_height, 47 | "id": svgCanvas.getNextId(), 48 | "style": "pointer-events:inherit" 49 | } 50 | }); 51 | svgCanvas.setHref(newImage, e.target.result); 52 | svgCanvas.selectOnly([newImage]) 53 | svgCanvas.alignSelectedElements("m", "page") 54 | svgCanvas.alignSelectedElements("c", "page") 55 | editor.panel.updateContextPanel(); 56 | } 57 | // put a placeholder img so we know the default dimensions 58 | var img_width = 100; 59 | var img_height = 100; 60 | var img = new Image() 61 | img.src = e.target.result 62 | document.body.appendChild(img); 63 | img.onload = function() { 64 | img_width = img.offsetWidth 65 | img_height = img.offsetHeight 66 | insertNewImage(img_width, img_height); 67 | document.body.removeChild(img); 68 | } 69 | }; 70 | reader.readAsDataURL(file) 71 | } 72 | 73 | //editor.saveCanvas(); 74 | } 75 | 76 | function loadSvgString(str, callback) { 77 | var success = svgCanvas.setSvgString(str) !== false; 78 | callback = callback || $.noop; 79 | if(success) { 80 | callback(true); 81 | editor.saveCanvas(); 82 | state.set("canvasTitle", svgCanvas.getDocumentTitle()); 83 | } else { 84 | $.alert("Error: Unable to load SVG data", function() { 85 | callback(false); 86 | }); 87 | } 88 | } 89 | 90 | function openImage(e){ 91 | $('#menu_bar').removeClass('active') 92 | const f = this; 93 | if(f.files.length === 1) { 94 | svgCanvas.clear(); 95 | var reader = new FileReader(); 96 | reader.onloadend = function(e) { 97 | loadSvgString(e.target.result); 98 | editor.canvas.update(true); 99 | }; 100 | reader.readAsText(f.files[0]); 101 | } 102 | } 103 | 104 | function onDragEnter(e) { 105 | e.stopPropagation(); 106 | e.preventDefault(); 107 | workarea.style.transform = "scale(1.1)"; 108 | } 109 | 110 | function onDragOver(e) { 111 | e.stopPropagation(); 112 | e.preventDefault(); 113 | } 114 | 115 | function onDragLeave(e) { 116 | workarea.removeAttribute("style"); 117 | e.stopPropagation(); 118 | e.preventDefault(); 119 | } 120 | 121 | function place(){ 122 | $importInput.trigger("click"); 123 | } 124 | 125 | function open(){ 126 | $openInput.trigger("click"); 127 | } 128 | 129 | workarea.addEventListener('dragenter', onDragEnter, false); 130 | workarea.addEventListener('dragover', onDragOver, false); 131 | workarea.addEventListener('dragleave', onDragLeave, false); 132 | workarea.addEventListener('drop', importImage, false); 133 | 134 | this.place = place; 135 | this.open = open; 136 | this.loadSvgString = loadSvgString; 137 | 138 | } -------------------------------------------------------------------------------- /src/js/Menu.js: -------------------------------------------------------------------------------- 1 | MD.Menu = function(){ 2 | 3 | $('#tool_wireframe').on("click", editor.toggleWireframe); 4 | $('#tool_move_top').on("click", editor.moveToTopSelected); 5 | $('#tool_move_up').on("click", editor.moveUpSelected); 6 | $('#tool_move_bottom').on("click", editor.moveToBottomSelected); 7 | $('#tool_move_down').on("click", editor.moveDownSelected); 8 | $('#tool_topath').on("click", editor.convertToPath); 9 | $('#tool_group').on("click", editor.groupSelected); 10 | $('#tool_ungroup').on("click", editor.ungroupSelected); 11 | if (window.location.host === "editor.method.ac") { 12 | $('#modal_donate').show(); 13 | $('#sponsors').show(); 14 | } 15 | // top dropdown menus 16 | $('.menu_title') 17 | .on('mousedown', function() { 18 | $("#menu_bar").toggleClass('active'); 19 | $('.menu').removeClass('open'); 20 | $(this).parent().addClass('open'); 21 | }) 22 | .on('mouseover', function() { 23 | $('.menu').removeClass('open'); 24 | $(this).parent().addClass('open'); 25 | }); 26 | 27 | function blink(el) { 28 | el.style.background = "#fff"; 29 | setTimeout(()=> el.style.background = "#ddd", 50); 30 | setTimeout(()=> el.style.background = "#fff", 150); 31 | setTimeout(()=> el.style.background = "#ddd", 200); 32 | setTimeout(()=> el.style.background = "", 200); 33 | setTimeout(()=> $('#menu_bar').removeClass('active'), 250); 34 | return false; 35 | } 36 | 37 | function close(e){ 38 | if (e.target.nodeName && e.target.nodeName.toLowerCase() === "input") return false; 39 | if (!$(e.target).hasClass("menu_title") && !$(e.target).parent().hasClass("menu_title")) { 40 | if(!$(e.target).hasClass("disabled") && $(e.target).hasClass("menu_item")) blink(e.target) 41 | else $('#menu_bar').removeClass('active') 42 | } 43 | } 44 | 45 | function flash($menu){ 46 | var menu_title = $menu.prev(); 47 | menu_title.css({ 48 | "background": "white", 49 | "color": "black" 50 | }); 51 | setTimeout(function(){menu_title.removeAttr("style")}, 200); 52 | } 53 | 54 | // This puts the correct shortcuts in the menus 55 | if (!svgedit.browser.isMac()) { 56 | $('.shortcut').each(function(){ 57 | var text = $(this).text(); 58 | $(this).text(text.split("⌘").join("Ctrl+")) 59 | }); 60 | } 61 | 62 | $('.menu_item').on('click', function(e){ 63 | const action = this.getAttribute("data-action"); 64 | if (action && editor[action]) { 65 | editor[action](); 66 | blink(this); 67 | } 68 | }); 69 | 70 | $("body").on('mousedown', close); 71 | 72 | this.flash = flash; 73 | 74 | } 75 | -------------------------------------------------------------------------------- /src/js/Modal.js: -------------------------------------------------------------------------------- 1 | MD.Modal = function(config){ 2 | 3 | const el = document.createElement("div"); 4 | el.classList.add("modal", "hidden"); 5 | 6 | const item = document.createElement("div"); 7 | item.innerHTML = config.html; 8 | item.classList.add("modal-item"); 9 | el.appendChild(item); 10 | 11 | el.addEventListener("click", close); 12 | 13 | item.addEventListener("click", function(e){ 14 | e.stopPropagation(); 15 | }); 16 | 17 | document.body.appendChild(el); 18 | 19 | 20 | 21 | function open(){ 22 | el.classList.remove("hidden"); 23 | } 24 | 25 | function close(){ 26 | el.classList.add("hidden"); 27 | } 28 | 29 | function confirm(cb){ 30 | if (cb) cb(); 31 | close(); 32 | } 33 | 34 | this.open = open; 35 | this.close = close; 36 | this.confirm = confirm; 37 | this.cb = config.cb || function(){}; 38 | this.el = el; 39 | 40 | if (config.js) { 41 | const el = this.el; 42 | config.js(el); 43 | } 44 | 45 | return this 46 | 47 | } -------------------------------------------------------------------------------- /src/js/Palette.js: -------------------------------------------------------------------------------- 1 | MD.Palette = function(){ 2 | var palette = [ 3 | "#444444", "#482816", "#422C10", "#3B2F0E", "#32320F", 4 | "#293414", "#1F361B", "#153723", "#0C372C", "#083734", 5 | "#0E353B", "#1A333F", "#273141", "#332D40", "#3E2A3C", 6 | "#462735", "#4B252D", "#4D2425", "#4C261D", "#666666", 7 | "#845335", "#7B572D", "#6F5C2A", "#62612C", "#546433", 8 | "#46673D", "#396849", "#306856", "#2D6862", "#33666C", 9 | "#426373", "#535F75", "#645A73", "#74556D", "#805064", 10 | "#884D58", "#8B4D4B", "#894F3F", "#999999", "#C48157", 11 | "#B8874D", "#A98E49", "#97944B", "#849854", "#729C62", 12 | "#619E73", "#559E84", "#529D94", "#5B9BA2", "#6D97AB", 13 | "#8391AE", "#9A8AAB", "#AF84A3", "#BF7E96", "#C97A86", 14 | "#CE7975", "#CC7C65", "#BBBBBB", "#FFB27C", "#FABA6F", 15 | "#E6C36A", "#CFCA6D", "#B8D078", "#A0D58A", "#8CD79F", 16 | "#7DD8B5", "#7AD6CA", "#84D3DB", "#9ACEE6", "#B6C7EA", 17 | "#D3BEE7", "#EDB6DC", "#FFAFCC", "#FFAAB8", "#FFA9A2", 18 | "#FFAC8D", "#DDDDDD", "#FFE7A2", "#FFF093", "#FFFA8D", 19 | "#FFFF91", "#EEFF9F", "#D1FFB4", "#B9FFCE", "#A8FFE9", 20 | "#A4FFFF", "#B1FFFF", "#CBFFFF", "#EDFFFF", "#FFF5FF", 21 | "#FFEBFF", "#FFE2FF", "#FFDCEC", "#FFDBD2", "#FFDFB8" 22 | ]; 23 | 24 | var str = '
\ 25 |
\ 26 |
' 27 | palette.forEach(function(item, i){ 28 | str += '
'; 29 | }); 30 | $('#palette').append(str); 31 | 32 | var toolStroke = document.getElementById('tool_stroke'); 33 | var picking = false; 34 | 35 | $(document).on("mouseup", function(){picking = false;}) 36 | 37 | $('#palette').on("mousemove mousedown touchstart touchmove", ".palette_item", function(evt){ 38 | 39 | evt.preventDefault(); 40 | if (evt.type === "mousedown" || evt.type === "touchstart") picking = true; 41 | if (!picking) return; 42 | 43 | var isStroke = toolStroke.classList.contains('active') || evt.shiftKey; 44 | var picker = isStroke ? "stroke" : "fill"; 45 | var color = this.getAttribute('data-rgb'); 46 | var paint = null; 47 | var noUndo = true; 48 | 49 | paint = color === 'none' 50 | ? new $.jGraduate.Paint() 51 | : new $.jGraduate.Paint({alpha: 100, solidColor: color.substr(1)}); 52 | 53 | editor.paintBox[picker].setPaint(paint); 54 | 55 | if (isStroke) { 56 | svgCanvas.setColor('stroke', color, noUndo); 57 | if (color != 'none' && svgCanvas.getStrokeOpacity() != 1) { 58 | svgCanvas.setPaintOpacity('stroke', 1.0); 59 | } 60 | } else { 61 | svgCanvas.setColor('fill', color, noUndo); 62 | if (color != 'none' && svgCanvas.getFillOpacity() != 1) { 63 | svgCanvas.setPaintOpacity('fill', 1.0); 64 | } 65 | } 66 | }).bind('contextmenu', function(e) {e.preventDefault()}); 67 | }; 68 | 69 | 70 | -------------------------------------------------------------------------------- /src/js/Pan.js: -------------------------------------------------------------------------------- 1 | MD.Pan = function(){ 2 | const workarea = document.getElementById("workarea"); 3 | 4 | let panning = false; 5 | let last_x = null; 6 | let last_y = null; 7 | 8 | function startPan(e){ 9 | panning = true; 10 | svgCanvas.spaceKey = true; 11 | workarea.classList.add("dragging"); 12 | } 13 | 14 | function stopPan(){ 15 | panning = false; 16 | svgCanvas.spaceKey = false; 17 | workarea.classList.remove("dragging"); 18 | } 19 | 20 | var move_pan = function(evt) { 21 | if(!panning) return; 22 | workarea.scrollLeft -= (evt.clientX - last_x); 23 | workarea.scrollTop -= (evt.clientY - last_y); 24 | last_x = evt.clientX; 25 | last_y = evt.clientY; 26 | } 27 | 28 | var start_pan = function(evt) { 29 | if(!panning) return; 30 | last_x = evt.clientX; 31 | last_y = evt.clientY; 32 | } 33 | 34 | $('#svgcanvas') 35 | .on('mousemove', move_pan) 36 | .on("mousedown", start_pan) 37 | 38 | this.startPan = startPan; 39 | this.stopPan = stopPan; 40 | } -------------------------------------------------------------------------------- /src/js/Rulers.js: -------------------------------------------------------------------------------- 1 | MD.Rulers = function(){ 2 | 3 | $('#tool_rulers').on("click", toggleRulers); 4 | 5 | const workarea = document.getElementById("workarea"); 6 | const svgcanvas = document.getElementById("svgcanvas"); 7 | const ruler_x = document.getElementById("ruler_x"); 8 | const ruler_y = document.getElementById("ruler_y"); 9 | 10 | $("#ruler_x, #ruler_y").append("
"); 11 | 12 | function clear(){ 13 | ruler_x.innerHTML = ""; 14 | ruler_y.innerHTML = ""; 15 | ruler_x.appendChild(canvas_x); 16 | ruler_y.appendChild(canvas_y); 17 | } 18 | 19 | function toggleRulers(){ 20 | editor.menu.flash($('#view_menu')); 21 | var rulers = !$('#tool_rulers').hasClass('push_button_pressed'); 22 | if (rulers) show(); 23 | else hide(); 24 | } 25 | 26 | function show(){ 27 | $('#tool_rulers').addClass('push_button_pressed'); 28 | $('#show_rulers').attr("checked", true); 29 | $('#rulers').show(); 30 | state.set("canvasRulers", true); 31 | } 32 | 33 | function hide(){ 34 | $('#tool_rulers').removeClass('push_button_pressed'); 35 | $('#show_rulers').attr("checked", false); 36 | $('#rulers').hide(); 37 | state.set("canvasRulers", false); 38 | } 39 | 40 | workarea.addEventListener("scroll", function() { 41 | ruler_x.scrollLeft = workarea.scrollLeft; 42 | ruler_y.scrollTop = workarea.scrollTop; 43 | }); 44 | 45 | window.addEventListener("resize", function(){ 46 | editor.canvas.update(true); 47 | update(); 48 | }) 49 | 50 | var r_intervals = []; 51 | for(var i = .1; i < 1E5; i *= 10) { 52 | r_intervals.push(1 * i); 53 | r_intervals.push(2 * i); 54 | r_intervals.push(5 * i); 55 | } 56 | 57 | function update(zoom) { 58 | const isDark = state.get("darkmode"); 59 | const gray = getComputedStyle(document.body).getPropertyValue(isDark ? '--z5' : '--z6') || "#999"; 60 | if(!zoom) zoom = svgCanvas.getZoom(); 61 | var limit = 30000; 62 | var c_elem = svgCanvas.getContentElem(); 63 | var unit = 1; 64 | 65 | for(var d = 0; d < 2; d++) { 66 | var is_x = (d === 0); 67 | var dim = is_x ? 'x' : 'y'; 68 | var lentype = is_x ?'width':'height'; 69 | var notlentype = is_x ?'height':'width'; 70 | var content_d = c_elem.getAttribute(dim); 71 | 72 | var hcanv = document.querySelector('#ruler_' + dim + ' canvas'); 73 | 74 | // Set the canvas size to the width of the container 75 | var ruler_len = svgcanvas[lentype === "width" ? "offsetWidth" : "offsetHeight"]; 76 | var total_len = ruler_len; 77 | hcanv.parentNode.style[lentype] = total_len + 'px'; 78 | 79 | var canv_count = 1; 80 | var ctx_num = 0; 81 | var ctx_arr; 82 | var ctx = hcanv.getContext("2d"); 83 | var scale = window.devicePixelRatio*2 || 1; 84 | hcanv.style[lentype] = total_len + "px"; 85 | hcanv.style[notlentype] = 16 + "px"; 86 | hcanv[lentype] = Math.floor(total_len * scale); 87 | hcanv[notlentype] = Math.floor(16 * scale); 88 | ctx.scale(scale,scale); 89 | 90 | // Remove any existing canvasses 91 | $(hcanv).siblings().remove(); 92 | 93 | // Create multiple canvases when necessary (due to browser limits) 94 | if(ruler_len >= limit) { 95 | var num = parseInt(ruler_len / limit) + 1; 96 | ctx_arr = Array(num); 97 | ctx_arr[0] = ctx; 98 | for(var i = 1; i < num; i++) { 99 | hcanv[lentype] = limit; 100 | var copy = hcanv.cloneNode(true); 101 | hcanv.parentNode.appendChild(copy); 102 | ctx_arr[i] = copy.getContext('2d'); 103 | } 104 | 105 | copy[lentype] = ruler_len % limit; 106 | 107 | // set copy width to last 108 | ruler_len = limit; 109 | } 110 | 111 | hcanv[lentype] = ruler_len * scale; 112 | 113 | var u_multi = unit * zoom; 114 | 115 | // Calculate the main number interval 116 | var raw_m = 50 / u_multi; 117 | var multi = 1; 118 | for(var i = 0; i < r_intervals.length; i++) { 119 | var num = r_intervals[i]; 120 | multi = num; 121 | if(raw_m <= num) { 122 | break; 123 | } 124 | } 125 | 126 | var big_int = multi * u_multi; 127 | ctx.font = "600 9px -apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen-Sans,Ubuntu,Cantarell,'Helvetica Neue',sans-serif"; 128 | ctx.fillStyle = gray; 129 | ctx.strokeStyle = gray; 130 | ctx.scale(scale,scale); 131 | var ruler_d = ((content_d / u_multi) % multi) * u_multi - 50; 132 | var label_pos = ruler_d - big_int; 133 | for (; ruler_d < total_len; ruler_d += big_int) { 134 | label_pos += big_int; 135 | var real_d = ruler_d - content_d; 136 | 137 | var cur_d = Math.round(ruler_d); 138 | if(is_x) { 139 | ctx.moveTo(cur_d, 16); 140 | ctx.lineTo(cur_d, 0); 141 | } else { 142 | ctx.moveTo(16, cur_d); 143 | ctx.lineTo(0, cur_d); 144 | } 145 | 146 | var num = (label_pos - content_d) / u_multi; 147 | var label; 148 | if(multi >= 1) { 149 | label = Math.round(num); 150 | } else { 151 | var decs = (multi+'').split('.')[1].length; 152 | label = num.toFixed(decs)-0; 153 | } 154 | 155 | // Change 1000s to Ks 156 | if(label !== 0 && label !== 1000 && label % 1000 === 0) { 157 | label = (label / 1000) + 'K'; 158 | } 159 | 160 | if(is_x) { 161 | ctx.fillText(label, ruler_d+2, 8); 162 | ctx.fillStyle = gray; 163 | } else { 164 | var str = (label+'').split(''); 165 | for(var i = 0; i < str.length; i++) { 166 | ctx.fillText(str[i], 1, (ruler_d+9) + i*9); 167 | ctx.fillStyle = gray; 168 | } 169 | } 170 | 171 | var part = big_int / 10; 172 | for(var i = 1; i < 10; i++) { 173 | var sub_d = Math.round(ruler_d + part * i) + .5; 174 | if(ctx_arr && sub_d > ruler_len) { 175 | ctx_num++; 176 | ctx.stroke(); 177 | if(ctx_num >= ctx_arr.length) { 178 | i = 10; 179 | ruler_d = total_len; 180 | continue; 181 | } 182 | ctx = ctx_arr[ctx_num]; 183 | ruler_d -= limit; 184 | sub_d = Math.round(ruler_d + part * i) + .5; 185 | } 186 | 187 | var line_num = (i % 2)?12:10; 188 | if(is_x) { 189 | ctx.moveTo(sub_d, 15); 190 | ctx.lineTo(sub_d, line_num); 191 | } else { 192 | ctx.moveTo(15, sub_d); 193 | ctx.lineTo(line_num ,sub_d); 194 | } 195 | } 196 | } 197 | ctx.strokeStyle = gray; 198 | ctx.stroke(); 199 | } 200 | } 201 | 202 | this.update = update; 203 | this.toggleRulers = toggleRulers; 204 | 205 | } 206 | -------------------------------------------------------------------------------- /src/js/Text.js: -------------------------------------------------------------------------------- 1 | MD.Text = function(){ 2 | 3 | function setBold(){ 4 | if ($(this).hasClass("disabled")) return; 5 | svgCanvas.setBold( !svgCanvas.getBold() ); 6 | editor.panel.updateContextPanel(); 7 | } 8 | 9 | function setItalic(){ 10 | if ($(this).hasClass("disabled")) return; 11 | svgCanvas.setItalic( !svgCanvas.getItalic() ); 12 | editor.panel.updateContextPanel(); 13 | } 14 | 15 | $('#font_family').change(function() { 16 | svgCanvas.setFontFamily(this.value); 17 | }); 18 | 19 | $("#tool_bold").on("click", setBold); 20 | $("#tool_italic").on("click", setItalic); 21 | 22 | $('#font_family_dropdown').change(function() { 23 | var fam = this.options[this.selectedIndex].value; 24 | const isSystemFont = fam === "sans-serif" || fam === "serif" || fam === "monospace"; 25 | const font = isSystemFont ? {Bold: true, Italic: true, "BoldItalic": true} : fonts[fam]; 26 | if (!isSystemFont) fam = `'${fam}'`; 27 | 28 | 29 | console.log(font.axes) 30 | 31 | $("#tool_bold") 32 | .removeClass("active") 33 | .toggleClass("disabled", !font.axes.wght); 34 | 35 | $("#tool_italic") 36 | .removeClass("active") 37 | .toggleClass("disabled", !font.axes.ital); 38 | 39 | var fam_display = this.options[this.selectedIndex].text; 40 | $('#preview_font').html(fam_display).css("font-family", fam); 41 | $('#font_family').val(fam).change(); 42 | 43 | document.fonts.onloading = function(fontFaceSetEvent) { 44 | $("#tool_font_family").addClass("loading"); 45 | }; 46 | 47 | document.fonts.onloadingdone = function(fontFaceSetEvent) { 48 | const els = svgCanvas.getSelectedElems(); 49 | els.forEach(el => { 50 | var selector = svgCanvas.selectorManager.requestSelector(el); 51 | selector.resize(); 52 | }); 53 | $("#tool_font_family").removeClass("loading"); 54 | }; 55 | 56 | }); 57 | 58 | $('#text') 59 | .keydown(function(e){ 60 | e.stopPropagation(); 61 | }) 62 | .keyup(function(e){ 63 | e.stopPropagation(); 64 | if (e.key === "Escape" || e.key === "Enter") { 65 | svgCanvas.textActions.toSelectMode(); 66 | this.blur(); 67 | editor.saveCanvas(); 68 | return editor.escapeMode(); 69 | } 70 | svgCanvas.setTextContent(this.value); 71 | var elems = svgCanvas.getSelectedElems(); 72 | svgCanvas.selectorManager.requestSelector(elems[0]).reset(elems[0]); 73 | }) 74 | .click(function(e) { 75 | this.focus(); 76 | this.select(); 77 | }); 78 | 79 | function changeFontSize(attr, value, completed){ 80 | svgCanvas.setFontSize(value); 81 | } 82 | 83 | this.setBold = setBold; 84 | this.setItalic = setItalic; 85 | this.changeFontSize = changeFontSize; 86 | 87 | } -------------------------------------------------------------------------------- /src/js/Textonpath.js: -------------------------------------------------------------------------------- 1 | MD.TextOnPath = function(){ 2 | console.log("hi") 3 | } -------------------------------------------------------------------------------- /src/js/Title.js: -------------------------------------------------------------------------------- 1 | MD.Title = function(){ 2 | 3 | $('#canvas_title') 4 | .keydown(function(e){ 5 | e.stopPropagation(); 6 | if (e.key === "Escape") { 7 | this.blur(); 8 | } 9 | if (e.key === "Enter") { 10 | this.blur(); 11 | } 12 | }) 13 | .keyup(function(e){ 14 | e.stopPropagation(); 15 | svgCanvas.setDocumentTitle(this.value); 16 | state.set("canvasTitle", this.value); 17 | }) 18 | .click(function(e) { 19 | this.focus(); 20 | this.select(); 21 | }) 22 | 23 | } -------------------------------------------------------------------------------- /src/js/Toolbar.js: -------------------------------------------------------------------------------- 1 | MD.Toolbar = function(){ 2 | 3 | // tools left 4 | $("#tools_left .tool_button").on("click", function(){ 5 | const mode = this.getAttribute("data-mode"); 6 | state.set("canvasMode", mode) 7 | if (mode === "shapelib") showShapeLib() 8 | }); 9 | 10 | function setMode(mode) { 11 | $(".tool_button").removeClass("current"); 12 | $("#tool_" + mode).addClass("current"); 13 | $("#workarea").attr("class", mode); 14 | svgCanvas.setMode(mode); 15 | } 16 | 17 | function showShapeLib(){ 18 | $("#tools_shapelib").show(); 19 | } 20 | 21 | this.setMode = setMode; 22 | } -------------------------------------------------------------------------------- /src/js/Zoom.js: -------------------------------------------------------------------------------- 1 | MD.Zoom = function(){ 2 | 3 | const $zoom = $("#zoom"); 4 | const $workarea = $("#workarea") 5 | 6 | const accumulatedDelta = 0 7 | $('#workarea').on('mousewheel', function(e, delta, deltaX, deltaY){ 8 | if (e.altKey || e.ctrlKey) { 9 | e.preventDefault(); 10 | zoom = parseInt($("#zoom").val()) 11 | $zoom.val(parseInt(zoom + deltaY*(e.altKey ? 10 : 5))).change() 12 | } 13 | }); 14 | 15 | $('#zoom_select').on("change", function() { 16 | var val = this.options[this.selectedIndex].text 17 | val = val.split("%")[0] 18 | $("#zoom").val(val).trigger("change") 19 | }); 20 | 21 | $('#zoom').change(function(ctl){ 22 | 23 | var zoomlevel = this.value / 100; 24 | if(zoomlevel < .001) { 25 | ctl.value = .1; 26 | return; 27 | } 28 | var zoom = svgCanvas.getZoom(); 29 | changed(window, { 30 | width: 0, 31 | height: 0, 32 | // center pt of scroll position 33 | x: ($workarea.scrollLeft() + $workarea.width()/2)/zoom, 34 | y: ($workarea.scrollTop() + $workarea.height()/2)/zoom, 35 | zoom: zoomlevel 36 | }, true); 37 | }) 38 | 39 | function changed(window, bbox) { 40 | const scrbar = 15; 41 | const res = svgCanvas.getResolution(); 42 | const canvas_pos = $('#svgcanvas').position(); 43 | const updateCanvas = editor.canvas.update; 44 | const z_info = svgCanvas.setBBoxZoom(bbox, $workarea.width()-scrbar, $workarea.height()-scrbar); 45 | const zoomlevel = z_info.zoom; 46 | const bb = z_info.bbox; 47 | 48 | if(!z_info) return; 49 | 50 | if (typeof animatedZoom !== 'undefined') window.cancelAnimationFrame(animatedZoom) 51 | // zoom duration 500ms 52 | var start = Date.now(); 53 | var duration = 500; 54 | var diff = (zoomlevel) - (res.zoom) 55 | var zoom = $('#zoom')[0] 56 | var current_zoom = res.zoom 57 | 58 | var animateZoom = function(timestamp) { 59 | var progress = Date.now() - start 60 | var tick = progress / duration; 61 | editor.rulers.update(); 62 | tick = (Math.pow((tick-1), 3) +1); 63 | svgCanvas.setZoom(current_zoom + (diff*tick)); 64 | var isCentered = !Boolean(bbox.width); 65 | updateCanvas(isCentered, {x: bbox.x + bbox.width/2, y: bbox.y + bbox.height/2}); 66 | if (tick < 1 && tick > -.90) { 67 | window.animatedZoom = requestAnimationFrame(animateZoom) 68 | } 69 | else { 70 | $zoom.val(parseInt(zoomlevel*100)) 71 | $("option", "#zoom_select").removeAttr("selected") 72 | $("option[value="+ parseInt(zoomlevel*100) +"]", "#zoom_select").attr("selected", "selected") 73 | } 74 | } 75 | animateZoom() 76 | 77 | if(svgCanvas.getMode() === 'zoom' && bb.width) { 78 | // Go to select if a zoom box was drawn 79 | state.set("canvasMode", "select"); 80 | } 81 | } 82 | 83 | var multiply = function(multiplier = 1) { 84 | var res = svgCanvas.getResolution(); 85 | $('#zoom').val(multiplier * res.zoom * 100); 86 | svgCanvas.setZoom(multiplier); 87 | editor.canvas.update(true); 88 | }; 89 | 90 | function reset(){ 91 | multiply(1); 92 | } 93 | 94 | this.multiply = multiply; 95 | this.reset = reset; 96 | this.changed = changed; 97 | } -------------------------------------------------------------------------------- /src/js/browser.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Package: svgedit.browser 3 | * 4 | * Licensed under the Apache License, Version 2 5 | * 6 | * Copyright(c) 2010 Jeff Schiller 7 | * Copyright(c) 2010 Alexis Deveria 8 | */ 9 | 10 | // Dependencies: 11 | // 1) jQuery (for $.alert()) 12 | 13 | var svgedit = svgedit || {}; 14 | 15 | (function() { 16 | 17 | if (!svgedit.browser) { 18 | svgedit.browser = {}; 19 | } 20 | var supportsSvg_ = (function() { 21 | return !!document.createElementNS && !!document.createElementNS('http://www.w3.org/2000/svg', 'svg').createSVGRect; 22 | })(); 23 | svgedit.browser.supportsSvg = function() { return supportsSvg_; } 24 | if(!svgedit.browser.supportsSvg()) { 25 | window.location = "browser-not-supported.html"; 26 | } 27 | else{ 28 | 29 | var svgns = 'http://www.w3.org/2000/svg'; 30 | var userAgent = navigator.userAgent; 31 | var svg = document.createElementNS(svgns, 'svg'); 32 | 33 | // Note: Browser sniffing should only be used if no other detection method is possible 34 | var isOpera_ = !!window.opera; 35 | var isWebkit_ = userAgent.indexOf("AppleWebKit") >= 0; 36 | var isGecko_ = userAgent.indexOf('Gecko/') >= 0; 37 | var isIE_ = userAgent.indexOf('MSIE') >= 0; 38 | var isChrome_ = userAgent.indexOf('Chrome/') >= 0; 39 | var isWindows_ = userAgent.indexOf('Windows') >= 0; 40 | var isMac_ = userAgent.indexOf('Macintosh') >= 0; 41 | var isTouch_ = 'ontouchstart' in window; 42 | 43 | var supportsSelectors_ = (function() { 44 | return !!svg.querySelector; 45 | })(); 46 | 47 | var supportsXpath_ = (function() { 48 | return !!document.evaluate; 49 | })(); 50 | 51 | // text character positioning (for IE9) 52 | var supportsGoodTextCharPos_ = (function() { 53 | var retValue = false; 54 | var svgroot = document.createElementNS(svgns, 'svg'); 55 | var svgcontent = document.createElementNS(svgns, 'svg'); 56 | document.documentElement.appendChild(svgroot); 57 | svgcontent.setAttribute('x', 5); 58 | svgroot.appendChild(svgcontent); 59 | var text = document.createElementNS(svgns,'text'); 60 | text.textContent = 'a'; 61 | svgcontent.appendChild(text); 62 | var pos = text.getStartPositionOfChar(0) 63 | pos = pos.x; //if you put it on one line it fails when compiled 64 | document.documentElement.removeChild(svgroot); 65 | return (pos === 0); 66 | })(); 67 | 68 | var supportsPathBBox_ = (function() { 69 | var svgcontent = document.createElementNS(svgns, 'svg'); 70 | document.documentElement.appendChild(svgcontent); 71 | var path = document.createElementNS(svgns, 'path'); 72 | path.setAttribute('d','M0,0 C0,0 10,10 10,0'); 73 | svgcontent.appendChild(path); 74 | var bbox = path.getBBox(); 75 | document.documentElement.removeChild(svgcontent); 76 | return (bbox.height > 4 && bbox.height < 5); 77 | })(); 78 | 79 | // Support for correct bbox sizing on groups with horizontal/vertical lines 80 | var supportsHVLineContainerBBox_ = (function() { 81 | var svgcontent = document.createElementNS(svgns, 'svg'); 82 | document.documentElement.appendChild(svgcontent); 83 | var path = document.createElementNS(svgns, 'path'); 84 | path.setAttribute('d','M0,0 10,0'); 85 | var path2 = document.createElementNS(svgns, 'path'); 86 | path2.setAttribute('d','M5,0 15,0'); 87 | var g = document.createElementNS(svgns, 'g'); 88 | g.appendChild(path); 89 | g.appendChild(path2); 90 | svgcontent.appendChild(g); 91 | var bbox = g.getBBox(); 92 | document.documentElement.removeChild(svgcontent); 93 | // Webkit gives 0, FF gives 10, Opera (correctly) gives 15 94 | return (bbox.width === 15); 95 | })(); 96 | 97 | var supportsEditableText_ = (function() { 98 | // TODO: Find better way to check support for this 99 | return isOpera_; 100 | })(); 101 | 102 | var supportsGoodDecimals_ = (function() { 103 | // Correct decimals on clone attributes (Opera < 10.5/win/non-en) 104 | var rect = document.createElementNS(svgns, 'rect'); 105 | rect.setAttribute('x',.1); 106 | var crect = rect.cloneNode(false); 107 | var retValue = (crect.getAttribute('x').indexOf(',') === -1); 108 | if(!retValue) { 109 | $.alert("NOTE: This version of Opera is known to contain bugs in SVG-edit.\n\ 110 | Please upgrade to the latest version in which the problems have been fixed."); 111 | } 112 | return retValue; 113 | })(); 114 | 115 | var supportsNonScalingStroke_ = (function() { 116 | var rect = document.createElementNS(svgns, 'rect'); 117 | rect.setAttribute('style','vector-effect:non-scaling-stroke'); 118 | return rect.style.vectorEffect === 'non-scaling-stroke'; 119 | })(); 120 | 121 | var supportsNativeSVGTransformLists_ = (function() { 122 | var rect = document.createElementNS(svgns, 'rect'); 123 | var rxform = rect.transform.baseVal; 124 | 125 | var t1 = svg.createSVGTransform(); 126 | rxform.appendItem(t1); 127 | return rxform.getItem(0) === t1; 128 | })(); 129 | 130 | var supportsBlobs_ = (function() { 131 | if (typeof Blob != 'function') return false; 132 | // check if download is supported 133 | var svg = new Blob( 134 | [""], 135 | {type: "image/svg+xml;charset=utf-8"} 136 | ); 137 | var img = new Image(); 138 | var support = false; 139 | img.onload = function() { svgedit.browser.supportsBlobs = function() {return true} }; 140 | img.onerror = function() { svgedit.browser.supportsBlobs = function() {return false} }; 141 | img.src = URL.createObjectURL(svg); 142 | return false; 143 | })(); 144 | 145 | 146 | 147 | // Public API 148 | 149 | svgedit.browser.isOpera = function() { return isOpera_; } 150 | svgedit.browser.isWebkit = function() { return isWebkit_; } 151 | svgedit.browser.isGecko = function() { return isGecko_; } 152 | svgedit.browser.isIE = function() { return isIE_; } 153 | svgedit.browser.isChrome = function() { return isChrome_; } 154 | svgedit.browser.isWindows = function() { return isWindows_; } 155 | svgedit.browser.isMac = function() { return isMac_; } 156 | svgedit.browser.isTouch = function() { return isTouch_; } 157 | 158 | svgedit.browser.supportsSelectors = function() { return supportsSelectors_; } 159 | svgedit.browser.supportsXpath = function() { return supportsXpath_; } 160 | 161 | svgedit.browser.supportsPathBBox = function() { return supportsPathBBox_; } 162 | svgedit.browser.supportsHVLineContainerBBox = function() { return supportsHVLineContainerBBox_; } 163 | svgedit.browser.supportsGoodTextCharPos = function() { return supportsGoodTextCharPos_; } 164 | svgedit.browser.supportsEditableText = function() { return supportsEditableText_; } 165 | svgedit.browser.supportsGoodDecimals = function() { return supportsGoodDecimals_; } 166 | svgedit.browser.supportsNonScalingStroke = function() { return supportsNonScalingStroke_; } 167 | svgedit.browser.supportsNativeTransformLists = function() { return supportsNativeSVGTransformLists_; } 168 | svgedit.browser.supportsBlobs = function() {return supportsBlobs_; } 169 | } 170 | 171 | })(); 172 | -------------------------------------------------------------------------------- /src/js/dao.js: -------------------------------------------------------------------------------- 1 | const dao = [ 2 | 3 | // public, appears in builder 4 | 5 | { 6 | name: "canvasId", 7 | label: "Canvas ID", 8 | type: "id", 9 | default: "", 10 | private: true, 11 | save: true 12 | }, 13 | 14 | { 15 | name: "canvasTitle", 16 | label: "Canvas Title", 17 | type: "string", 18 | default: "Drawing", 19 | private: false, 20 | save: true 21 | }, 22 | 23 | { 24 | name: "canvasSize", 25 | label: "Canvas Size", 26 | type: "array", 27 | default: [800, 600], 28 | private: false, 29 | save: true 30 | }, 31 | 32 | { 33 | name: "canvasSnap", 34 | label: "Snap to Grid", 35 | type: "boolean", 36 | default: false, 37 | private: false, 38 | save: true 39 | }, 40 | 41 | { 42 | name: "canvasSnapStep", 43 | label: "Snap Step", 44 | type: "number", 45 | default: 10, 46 | private: false, 47 | save: true 48 | }, 49 | 50 | { 51 | name: "canvasRulers", 52 | label: "Canvas Rulers", 53 | type: "boolean", 54 | default: true, 55 | private: false, 56 | save: true 57 | }, 58 | 59 | { 60 | name: "canvasContent", 61 | label: "Canvas Content", 62 | type: "string", 63 | default: "", 64 | private: true, 65 | save: true 66 | }, 67 | 68 | { 69 | name: "canvasMode", 70 | label: "Canvas Mode", 71 | type: "string", 72 | default: "select", 73 | private: true, 74 | save: true 75 | }, 76 | 77 | { 78 | name: "canvasFill", 79 | label: "Canvas Fill", 80 | type: "object", 81 | default: {type: "solidColor", solidColor: 'ffffff', alpha: 100}, 82 | private: true, 83 | save: true 84 | }, 85 | 86 | { 87 | name: "canvasStroke", 88 | label: "Canvas Stroke", 89 | type: "object", 90 | default: {type: "solidColor", solidColor: '000000', alpha: 100}, 91 | private: true, 92 | save: true 93 | }, 94 | 95 | { 96 | name: "canvasBackground", 97 | label: "Canvas Background", 98 | type: "object", 99 | default: {type: "solidColor", solidColor: 'ffffff', alpha: 100}, 100 | private: true, 101 | save: true 102 | }, 103 | 104 | { 105 | name: "canvasCreationDate", 106 | label: "Canvas Creation Date", 107 | type: "string", 108 | default: new Date().toString(), 109 | private: true, 110 | save: false 111 | }, 112 | // When this page was created 113 | { 114 | name: "canvasLastModified", 115 | label: "Canvas Last Modified", 116 | type: "string", 117 | default: new Date().toString(), 118 | private: true, 119 | save: false 120 | }, 121 | 122 | // system level fields 123 | { 124 | name: "darkmode", 125 | label: "Dark Mode", 126 | type: "boolean", 127 | default: true, 128 | private: true, 129 | save: true, 130 | }, 131 | // future use 132 | { 133 | name: "language", 134 | label: "Language", 135 | type: "string", 136 | default: null, 137 | private: true, 138 | save: true, 139 | }, 140 | // if it is the first time visitor we can onboard them 141 | { 142 | name: "visited", 143 | label: "Has visited before", 144 | type: "boolean", 145 | default: false, 146 | private: true, 147 | save: true, 148 | }, 149 | 150 | ]; 151 | 152 | dao.forEach(thing => { 153 | thing.clean = function(value){ 154 | if (thing.type === "number") return isNaN(value) ? 0 : parseInt(value, 10); 155 | if (thing.type === "string") return value || ""; 156 | if (thing.type === "boolean") return value === "true" || value === true ? true : false; 157 | if (thing.type === "url") return value || ""; 158 | if (thing.type === "id") return value || 0; 159 | if (thing.type === "array") return typeof value === "object" ? value : value ? value.split(",") : []; 160 | if (thing.type === "object") return typeof value === "object" ? value : value ? JSON.parse(value) : {}; 161 | else throw "type " + thing.type + " does not exist"; 162 | } 163 | }); 164 | -------------------------------------------------------------------------------- /src/js/dialog.js: -------------------------------------------------------------------------------- 1 | // This sets up alternative dialog boxes. They mostly work the same way as 2 | // their UI counterparts, expect instead of returning the result, a callback 3 | // needs to be included that returns the result as its first parameter. 4 | // In the future we may want to add additional types of dialog boxes, since 5 | // they should be easy to handle this way. 6 | 7 | (function() { 8 | $('#dialog_container').draggable({cancel:'#dialog_content, #dialog_buttons *', containment: 'window'}); 9 | var box = $('#dialog_box'), btn_holder = $('#dialog_buttons'); 10 | 11 | var dbox = function(type, msg, callback, defText) { 12 | $('#dialog_content').html(msg) 13 | .toggleClass('prompt',(type==='prompt')); 14 | btn_holder.empty(); 15 | 16 | var ok = $('').appendTo(btn_holder); 17 | 18 | if(type != 'alert') { 19 | $('') 20 | .appendTo(btn_holder) 21 | .on("click touchstart", function() { box.hide();callback(false)}); 22 | } 23 | 24 | if(type === 'prompt') { 25 | var input = $('').prependTo(btn_holder); 26 | input.val(defText || ''); 27 | input.bind('keydown', 'return', function() {ok.trigger("click touchstart");}); 28 | } 29 | 30 | if(type === 'process') { 31 | ok.hide(); 32 | } 33 | 34 | box.show(); 35 | 36 | ok.on("click touchstart", function() { 37 | box.hide(); 38 | var resp = (type === 'prompt')?input.val():true; 39 | if(callback) callback(resp); 40 | }).focus(); 41 | 42 | if(type === 'prompt') input.focus(); 43 | } 44 | 45 | $.alert = function(msg, cb) { dbox('alert', msg, cb);}; 46 | $.confirm = function(msg, cb) { dbox('confirm', msg, cb);}; 47 | $.process_cancel = function(msg, cb) { dbox('process', msg, cb);}; 48 | $.prompt = function(msg, txt, cb) { dbox('prompt', msg, cb, txt);}; 49 | }()); -------------------------------------------------------------------------------- /src/js/dragupload.js: -------------------------------------------------------------------------------- 1 | window.onload = function () { 2 | document.querySelector('body').addEventListener('drop', function(e) { 3 | e.preventDefault(); 4 | var reader = new FileReader(); 5 | reader.onload = function(evt) { 6 | //document.querySelector('img').src = evt.target.result; 7 | }; 8 | 9 | reader.readAsDataURL(e.dataTransfer.files[0]); 10 | }, false); 11 | } -------------------------------------------------------------------------------- /src/js/exportHandler.js: -------------------------------------------------------------------------------- 1 | function exportHandler(window, data) { 2 | var issues = data.issues; 3 | 4 | if(!$('#export_canvas').length) { 5 | $('', {id: 'export_canvas'}).hide().appendTo('body'); 6 | } 7 | var c = $('#export_canvas')[0]; 8 | 9 | c.width = svgCanvas.contentW; 10 | c.height = svgCanvas.contentH; 11 | canvg(c, data.svg, {renderCallback: function() { 12 | var datauri = c.toDataURL('image/png'); 13 | if (!datauri) return false; 14 | var filename = "Method Draw Image"; 15 | var type = 'image/png'; 16 | var file = svgedit.utilities.dataURItoBlob(datauri, type); 17 | if (window.navigator.msSaveOrOpenBlob) // IE10+ 18 | window.navigator.msSaveOrOpenBlob(file, filename); 19 | else { // Others 20 | var a = document.createElement("a"), 21 | url = URL.createObjectURL(file); 22 | a.href = url; 23 | a.download = filename; 24 | document.body.appendChild(a); 25 | a.click(); 26 | setTimeout(function() { 27 | document.body.removeChild(a); 28 | window.URL.revokeObjectURL(url); 29 | }, 0); 30 | } 31 | }}); 32 | } -------------------------------------------------------------------------------- /src/js/eyedropper.js: -------------------------------------------------------------------------------- 1 | /* 2 | * ext-eyedropper.js 3 | * 4 | * Licensed under the Apache License, Version 2 5 | * 6 | * Copyright(c) 2010 Jeff Schiller 7 | * 8 | */ 9 | 10 | // Dependencies: 11 | // 1) jQuery 12 | // 2) history.js 13 | // 3) svg_editor.js 14 | // 4) svgcanvas.js 15 | 16 | MD.Eyedropper = function() { 17 | var svgcontent = svgCanvas.svgcontent, 18 | svgns = "http://www.w3.org/2000/svg", 19 | svgdoc = svgCanvas.svgroot.parentNode.ownerDocument, 20 | ChangeElementCommand = svgedit.history.ChangeElementCommand, 21 | addToHistory = function(cmd) { svgCanvas.undoMgr.addCommandToHistory(cmd); }, 22 | currentStyle = {fillPaint: "red", fillOpacity: 1.0, 23 | strokePaint: "black", strokeOpacity: 1.0, 24 | strokeWidth: 5, strokeDashArray: null, 25 | opacity: 1.0, 26 | strokeLinecap: 'butt', 27 | strokeLinejoin: 'miter' 28 | }; 29 | function getStyle(opts) { 30 | // if we are in eyedropper mode, we don't want to disable the eye-dropper tool 31 | var mode = svgCanvas.getMode(); 32 | if (mode === "eyedropper") return; 33 | var tool = $('#tool_eyedropper'); 34 | 35 | } 36 | 37 | var getPaint = function(color, opac, type) { 38 | // update the editor's fill paint 39 | var opts = null; 40 | if (color.indexOf("url(#") === 0) { 41 | var refElem = svgCanvas.getRefElem(color); 42 | if(refElem) { 43 | refElem = refElem.cloneNode(true); 44 | } else { 45 | refElem = $("#" + type + "_color defs *")[0]; 46 | } 47 | 48 | opts = { alpha: opac }; 49 | opts[refElem.tagName] = refElem; 50 | } 51 | else if (color.indexOf("#") === 0) { 52 | opts = { 53 | alpha: opac, 54 | solidColor: color.substr(1) 55 | }; 56 | } 57 | else { 58 | opts = { 59 | alpha: opac, 60 | solidColor: 'none' 61 | }; 62 | } 63 | return new $.jGraduate.Paint(opts); 64 | }; 65 | 66 | return { 67 | name: "eyedropper", 68 | svgicons: "extensions/eyedropper-icon.xml", 69 | buttons: [{ 70 | id: "tool_eyedropper", 71 | type: "mode", 72 | title: "Eye Dropper Tool", 73 | position: 8, 74 | key: "I", 75 | events: { 76 | "click": function() { 77 | state.set("canvasMode", "eyedropper"); 78 | } 79 | } 80 | }], 81 | 82 | mouseDown: function(opts) { 83 | var mode = svgCanvas.getMode(); 84 | var e = opts.event; 85 | var target = (e.target.id === "svgroot") ? document.getElementById('canvas_background') : e.target; 86 | if (mode === "eyedropper" && target) { 87 | currentStyle.fillPaint = target.getAttribute("fill") || "white"; 88 | currentStyle.fillOpacity = target.getAttribute("fill-opacity") || 1.0; 89 | currentStyle.strokePaint = target.getAttribute("stroke") || 'none'; 90 | currentStyle.strokeOpacity = target.getAttribute("stroke-opacity") || 1.0; 91 | currentStyle.strokeWidth = target.getAttribute("stroke-width"); 92 | currentStyle.strokeDashArray = target.getAttribute("stroke-dasharray"); 93 | currentStyle.strokeLinecap = target.getAttribute("stroke-linecap"); 94 | currentStyle.strokeLinejoin = target.getAttribute("stroke-linejoin"); 95 | currentStyle.opacity = target.getAttribute("opacity") || 1.0; 96 | opts.selectedElements = opts.selectedElements.filter(Boolean) 97 | if (!opts.selectedElements.length) { //nothing selected, just update colors 98 | var fill = getPaint(currentStyle.fillPaint, currentStyle.fillOpacity*100, "fill"); 99 | var stroke = getPaint(currentStyle.strokePaint, currentStyle.strokeOpacity*100, "stroke"); 100 | editor.paintBox.fill.setPaint(fill) 101 | editor.paintBox.stroke.setPaint(stroke) 102 | return; 103 | } 104 | if ($.inArray(opts.selectedElements.nodeName, ['g', 'use']) === -1) { 105 | var changes = {}; 106 | var change = function(elem, attrname, newvalue) { 107 | changes[attrname] = elem.getAttribute(attrname); 108 | elem.setAttribute(attrname, newvalue); 109 | }; 110 | var batchCmd = new svgedit.history.BatchCommand(); 111 | opts.selectedElements.forEach(function(element){ 112 | if (currentStyle.fillPaint) change(element, "fill", currentStyle.fillPaint); 113 | if (currentStyle.fillOpacity) change(element, "fill-opacity", currentStyle.fillOpacity); 114 | if (currentStyle.strokePaint) change(element, "stroke", currentStyle.strokePaint); 115 | if (currentStyle.strokeOpacity) change(element, "stroke-opacity", currentStyle.strokeOpacity); 116 | if (currentStyle.strokeWidth) change(element, "stroke-width", currentStyle.strokeWidth); 117 | if (currentStyle.strokeDashArray) change(element, "stroke-dasharray", currentStyle.strokeDashArray); 118 | if (currentStyle.opacity) change(element, "opacity", currentStyle.opacity); 119 | if (currentStyle.strokeLinecap) change(element, "stroke-linecap", currentStyle.strokeLinecap); 120 | if (currentStyle.strokeLinejoin) change(element, "stroke-linejoin", currentStyle.strokeLinejoin); 121 | batchCmd.addSubCommand(new ChangeElementCommand(element, changes)); 122 | changes = {}; 123 | }); 124 | var fill = getPaint(currentStyle.fillPaint, currentStyle.fillOpacity*100, "fill") 125 | var stroke = getPaint(currentStyle.strokePaint, currentStyle.strokeOpacity*100, "stroke") 126 | editor.paintBox.fill.update(true) 127 | editor.paintBox.stroke.update(true) 128 | addToHistory(batchCmd); 129 | } 130 | } 131 | } 132 | }; 133 | }; -------------------------------------------------------------------------------- /src/js/grid.js: -------------------------------------------------------------------------------- 1 | /* 2 | * ext-grid.js 3 | * 4 | * Licensed under the Apache License, Version 2 5 | * 6 | * Copyright(c) 2010 Redou Mine 7 | * Copyright(c) 2010 Alexis Deveria 8 | * 9 | */ 10 | 11 | // Dependencies: 12 | // 1) units.js 13 | // 2) everything else 14 | 15 | methodDraw.addExtension("view_grid", function(s) { 16 | if (!document.getElementById("canvasGrid")){ 17 | var svgdoc = document.getElementById("svgcanvas").ownerDocument, 18 | svgns = "http://www.w3.org/2000/svg", 19 | dims = methodDraw.curConfig.dimensions, 20 | svgroot = s.svgroot; 21 | var svgCanvas = methodDraw.canvas; 22 | var showGrid = false; 23 | var assignAttributes = s.assignAttributes; 24 | 25 | var hcanvas = document.createElement('canvas'); 26 | $(hcanvas).hide().appendTo('body'); 27 | 28 | var canvasgrid = svgdoc.createElementNS(svgns, "g"); 29 | assignAttributes(canvasgrid, { 30 | 'id': 'canvasGrid', 31 | 'width': '100%', 32 | 'height': '100%', 33 | 'x': 0, 34 | 'y': 0, 35 | 'overflow': 'visible', 36 | 'display': 'none' 37 | }); 38 | 39 | var canvBG = $('#canvas_background'); 40 | canvBG.after(canvasgrid); 41 | 42 | 43 | 44 | // grid-pattern 45 | var gridPattern = svgdoc.createElementNS(svgns, "pattern"); 46 | assignAttributes(gridPattern, { 47 | 'id': 'gridpattern', 48 | 'patternUnits': 'userSpaceOnUse', 49 | 'x': 0, //-(value.strokeWidth / 2), // position for strokewidth 50 | 'y': 0, //-(value.strokeWidth / 2), // position for strokewidth 51 | 'width': 100, 52 | 'height': 100 53 | }); 54 | 55 | var gridimg = svgdoc.createElementNS(svgns, "image"); 56 | assignAttributes(gridimg, { 57 | 'x': 0, 58 | 'y': 0, 59 | 'width': 100, 60 | 'height': 100 61 | }); 62 | 63 | gridPattern.appendChild(gridimg); 64 | $('#svgroot defs').append(gridPattern); 65 | 66 | // grid-box 67 | var gridBox = svgdoc.createElementNS(svgns, "rect"); 68 | assignAttributes(gridBox, { 69 | 'width': '100%', 70 | 'height': '100%', 71 | 'x': 0, 72 | 'y': 0, 73 | 'stroke-width': 0, 74 | 'stroke': 'none', 75 | 'fill': 'url(#gridpattern)', 76 | 'style': 'pointer-events: none; display:visible;' 77 | }); 78 | $('#canvasGrid').append(gridBox); 79 | } 80 | // }); 81 | 82 | function updateGrid(zoom) { 83 | // TODO: Try this with elements, then compare performance difference 84 | 85 | var bgwidth = +canvBG.attr('width'); 86 | var bgheight = +canvBG.attr('height'); 87 | 88 | var units = svgedit.units.getTypeMap(); 89 | var unit = units[methodDraw.curConfig.baseUnit]; // 1 = 1px 90 | var r_intervals = [.01, .1, 1, 10, 100, 1000]; 91 | 92 | var d = 0; 93 | var is_x = (d === 0); 94 | var dim = is_x ? 'x' : 'y'; 95 | var lentype = is_x?'width':'height'; 96 | var c_elem = svgCanvas.getContentElem(); 97 | var content_d = c_elem.getAttribute(dim)-0; 98 | 99 | var hcanv = hcanvas; 100 | 101 | var u_multi = unit * zoom; 102 | 103 | // Calculate the main number interval 104 | var raw_m = 100 / u_multi; 105 | var multi = 1; 106 | for(var i = 0; i < r_intervals.length; i++) { 107 | var num = r_intervals[i]; 108 | multi = num; 109 | if(raw_m <= num) { 110 | break; 111 | } 112 | } 113 | 114 | var big_int = multi * u_multi; 115 | 116 | // Set the canvas size to the width of the container 117 | hcanv.width = big_int; 118 | hcanv.height = big_int; 119 | var ctx = hcanv.getContext("2d"); 120 | 121 | var ruler_d = 0; 122 | var cur_d = .5; 123 | 124 | var part = big_int / 10; 125 | 126 | ctx.globalAlpha = 0.2; 127 | ctx.strokeStyle = "#000"; 128 | for(var i = 1; i < 10; i++) { 129 | var sub_d = Math.round(part * i) + .5; 130 | // var line_num = (i % 2)?12:10; 131 | var line_num = 0; 132 | ctx.moveTo(sub_d, big_int); 133 | ctx.lineTo(sub_d, line_num); 134 | ctx.moveTo(big_int, sub_d); 135 | ctx.lineTo(line_num ,sub_d); 136 | } 137 | ctx.stroke(); 138 | ctx.beginPath(); 139 | ctx.globalAlpha = 0.5; 140 | ctx.moveTo(cur_d, big_int); 141 | ctx.lineTo(cur_d, 0); 142 | 143 | ctx.moveTo(big_int, cur_d); 144 | ctx.lineTo(0, cur_d); 145 | ctx.stroke(); 146 | 147 | var datauri = hcanv.toDataURL('image/png'); 148 | gridimg.setAttribute('width', big_int); 149 | gridimg.setAttribute('height', big_int); 150 | gridimg.parentNode.setAttribute('width', big_int); 151 | gridimg.parentNode.setAttribute('height', big_int); 152 | svgCanvas.setHref(gridimg, datauri); 153 | } 154 | 155 | return { 156 | name: "view_grid", 157 | zoomChanged: function(zoom) { 158 | // update size 159 | if(showGrid) updateGrid(zoom); 160 | }, 161 | 162 | buttons: [{ 163 | id: "view_grid", 164 | type: "menu", 165 | after: "tool_wireframe", 166 | panel: "view_menu", 167 | title: "View Grid", 168 | events: { 169 | 'click': function() { 170 | var gr = !$('#view_grid').hasClass('push_button_pressed'); 171 | if (gr) { 172 | methodDraw.curConfig.showGrid = showGrid = true; 173 | $('#view_grid').addClass('push_button_pressed'); 174 | $('#canvasGrid').attr('display', 'inline'); 175 | updateGrid(svgCanvas.getZoom()); 176 | } 177 | else { 178 | methodDraw.curConfig.showGrid = showGrid = false; 179 | $('#view_grid').removeClass('push_button_pressed'); 180 | $('#canvasGrid').attr('display', 'none'); 181 | } 182 | } 183 | } 184 | }] 185 | }; 186 | }); 187 | -------------------------------------------------------------------------------- /src/js/jquery.attr.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | 3 | // This fixes $(...).attr() to work as expected with SVG elements. 4 | // Does not currently use *AttributeNS() since we rarely need that. 5 | 6 | // See http://api.jquery.com/attr/ for basic documentation of .attr() 7 | 8 | // Additional functionality: 9 | // - When getting attributes, a string that's a number is return as type number. 10 | // - If an array is supplied as first parameter, multiple values are returned 11 | // as an object with values for each given attributes 12 | 13 | var proxied = jQuery.fn.attr, svgns = "http://www.w3.org/2000/svg"; 14 | jQuery.fn.attr = function(key, value) { 15 | var len = this.length; 16 | if(!len) return proxied.apply(this, arguments); 17 | for(var i=0; i"); 46 | } 47 | var shortcut = menuItem.shortcut || ""; 48 | $("#cmenu_canvas").append("
  • " 49 | + menuItem.label + "" 50 | + shortcut + "
  • "); 51 | } 52 | 53 | var menuItemIsValid = function(menuItem) { 54 | return menuItem && menuItem.id && menuItem.label && menuItem.action && typeof menuItem.action == 'function'; 55 | } 56 | 57 | for (menuItem in contextMenuExtensions) { 58 | injectExtendedContextMenuItemIntoDom(contextMenuExtensions[menuItem]); 59 | } 60 | 61 | svgedit.contextmenu.resetCustomMenus = function(){self.contextMenuExtensions = {}} 62 | svgedit.contextmenu.add = addContextMenuItem; 63 | svgedit.contextmenu.hasCustomHandler = hasCustomHandler; 64 | svgedit.contextmenu.getCustomHandler = getCustomHandler; 65 | })(); 66 | -------------------------------------------------------------------------------- /src/js/lib/css.min.js: -------------------------------------------------------------------------------- 1 | /*! css.js 27-02-2018 */ 2 | 3 | !function(e){"use strict";var t=function(){this.cssImportStatements=[],this.cssKeyframeStatements=[],this.cssRegex=new RegExp("([\\s\\S]*?){([\\s\\S]*?)}","gi"),this.cssMediaQueryRegex="((@media [\\s\\S]*?){([\\s\\S]*?}\\s*?)})",this.cssKeyframeRegex="((@.*?keyframes [\\s\\S]*?){([\\s\\S]*?}\\s*?)})",this.combinedCSSRegex="((\\s*?(?:\\/\\*[\\s\\S]*?\\*\\/)?\\s*?@media[\\s\\S]*?){([\\s\\S]*?)}\\s*?})|(([\\s\\S]*?){([\\s\\S]*?)})",this.cssCommentsRegex="(\\/\\*[\\s\\S]*?\\*\\/)",this.cssImportStatementRegex=new RegExp("@import .*?;","gi")};t.prototype.stripComments=function(e){var t=new RegExp(this.cssCommentsRegex,"gi");return e.replace(t,"")},t.prototype.parseCSS=function(e){if(void 0===e)return[];for(var t=[];;){var s=this.cssImportStatementRegex.exec(e);if(null===s)break;this.cssImportStatements.push(s[0]),t.push({selector:"@imports",type:"imports",styles:s[0]})}e=e.replace(this.cssImportStatementRegex,"");for(var r,i=new RegExp(this.cssKeyframeRegex,"gi");null!==(r=i.exec(e));)t.push({selector:"@keyframes",type:"keyframes",styles:r[0]});e=e.replace(i,"");for(var n=new RegExp(this.combinedCSSRegex,"gi");null!==(r=n.exec(e));){var o="";o=void 0===r[2]?r[5].split("\r\n").join("\n").trim():r[2].split("\r\n").join("\n").trim();var l=new RegExp(this.cssCommentsRegex,"gi"),p=l.exec(o);if(null!==p&&(o=o.replace(l,"").trim()),-1!==(o=o.replace(/\n+/,"\n")).indexOf("@media")){var a={selector:o,type:"media",subStyles:this.parseCSS(r[3]+"\n}")};null!==p&&(a.comments=p[0]),t.push(a)}else{var c={selector:o,rules:this.parseRules(r[6])};"@font-face"===o&&(c.type="font-face"),null!==p&&(c.comments=p[0]),t.push(c)}}return t},t.prototype.parseRules=function(e){var t=[];e=(e=e.split("\r\n").join("\n")).split(";");for(var s=0;s0&&t.push({directive:"",value:r,defective:!0})}return t},t.prototype.findCorrespondingRule=function(e,t,s){void 0===s&&(s=!1);for(var r=!1,i=0;i-1;o--)if(e[o].selector===r){i=e[o];break}if(!1===i)e.push(t);else if("media"!==t.type)for(var l=0;l-1||n.selector.indexOf("keyframes")>-1||n.selector.indexOf("@import")>-1||n.selector.indexOf(".form-all")>-1||n.selector.indexOf("#stage")>-1))if("media"!==n.type){for(var o=n.selector.split(","),l=[],p=0;p","/":"?","\\":"|"}};function a(d){if(typeof d.data!=="string"){return}var c=d.handler,e=d.data.toLowerCase().split(" ");d.handler=function(n){if(this!==n.target&&(/textarea|select/i.test(n.target.nodeName)||n.target.type==="text")){return}var h=n.type!=="keypress"&&b.hotkeys.specialKeys[n.which],o=String.fromCharCode(n.which).toLowerCase(),k,m="",g={};if(n.altKey&&h!=="alt"){m+="alt+"}if(n.ctrlKey&&h!=="ctrl"){m+="ctrl+"}if(n.metaKey&&!n.ctrlKey&&h!=="meta"){m+="meta+"}if(n.shiftKey&&h!=="shift"){m+="shift+"}if(h){g[m+h]=true}else{g[m+o]=true;g[m+b.hotkeys.shiftNums[o]]=true;if(m==="shift+"){g[b.hotkeys.shiftNums[o]]=true}}for(var j=0,f=e.length;j 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | * @author Guido Marucci Blas - guido@zaubersoftware.com 17 | * @description Adds a handler for a custom event 'taphold' that handles a 18 | * tap and hold on touch interfaces. 19 | */ 20 | (function($) { 21 | var TAP_AND_HOLD_TRIGGER_TIMER = 1000; 22 | var MAX_DISTANCE_ALLOWED_IN_TAP_AND_HOLD_EVENT = 5; 23 | var TOUCHSTART = "touchstart"; 24 | var TOUCHEND = "touchend"; 25 | var TOUCHMOVE = "touchmove"; 26 | 27 | // For debugging only 28 | // var TOUCHSTART = "mousedown"; 29 | // var TOUCHEND = "mouseup"; 30 | // var TOUCHMOVE = "mousemove"; 31 | 32 | var tapAndHoldTimer = null; 33 | 34 | function calculateEuclideanDistance(x1, y1, x2, y2) { 35 | var diffX = (x2 - x1); 36 | var diffY = (y2 - y1); 37 | return Math.sqrt((diffX * diffX) + (diffY * diffY)); 38 | }; 39 | 40 | function onTouchStart(event) { 41 | var e = event.originalEvent; 42 | 43 | // Only start detector if and only if one finger is over the widget 44 | if (!e.touches || (e.targetTouches.length === 1 && e.touches.length === 1)) { 45 | startTapAndHoldDetector.call(this, event) 46 | var element = $(this); 47 | element.bind(TOUCHMOVE, onTouchMove); 48 | element.bind(TOUCHEND, onTouchEnd); 49 | } else { 50 | stopTapAndHoldDetector.call(this); 51 | } 52 | }; 53 | 54 | function onTouchMove(event) { 55 | if (tapAndHoldTimer == null) { 56 | return; 57 | } 58 | 59 | var e = event.originalEvent; 60 | var x = (e.changedTouches) ? e.changedTouches[0].pageX: e.pageX; 61 | var y = (e.changedTouches) ? e.changedTouches[0].pageY: e.pageY; 62 | 63 | var tapAndHoldPoint = $(this).data("taphold.point"); 64 | var euclideanDistance = calculateEuclideanDistance(tapAndHoldPoint.x, tapAndHoldPoint.y, x, y); 65 | 66 | if (euclideanDistance > MAX_DISTANCE_ALLOWED_IN_TAP_AND_HOLD_EVENT) { 67 | stopTapAndHoldDetector.call(this); 68 | } 69 | }; 70 | 71 | function onTouchEnd(event) { 72 | stopTapAndHoldDetector.call(this); 73 | }; 74 | 75 | function onTapAndHold(event) { 76 | clear.call(this); 77 | $(this).data("taphold.handler").call(this, event); 78 | }; 79 | 80 | function clear() { 81 | tapAndHoldTimer = null; 82 | $(this).unbind(TOUCHMOVE, onTouchMove); 83 | $(this).unbind(TOUCHEND, onTouchEnd); 84 | }; 85 | 86 | function startTapAndHoldDetector(event) { 87 | if (tapAndHoldTimer != null) { 88 | return; 89 | } 90 | var self = this; 91 | tapAndHoldTimer = setTimeout(function(){ 92 | onTapAndHold.call(self, event) 93 | }, TAP_AND_HOLD_TRIGGER_TIMER); 94 | 95 | // Stores tap x & y 96 | var e = event.originalEvent; 97 | var tapAndHoldPoint = {}; 98 | tapAndHoldPoint.x = (e.changedTouches) ? e.changedTouches[0].pageX: e.pageX; 99 | tapAndHoldPoint.y = (e.changedTouches) ? e.changedTouches[0].pageY: e.pageY; 100 | $(this).data("taphold.point", tapAndHoldPoint); 101 | }; 102 | 103 | function stopTapAndHoldDetector() { 104 | clearTimeout(tapAndHoldTimer); 105 | clear.call(this); 106 | }; 107 | 108 | $.event.special["taphold"] = { 109 | setup: function() { 110 | 111 | }, 112 | 113 | add: function(handleObj) { 114 | $(this).data("taphold.handler", handleObj.handler); 115 | if (handleObj.data) { 116 | $(this).bind(TOUCHSTART, handleObj.data, onTouchStart); 117 | } else { 118 | $(this).bind(TOUCHSTART, onTouchStart); 119 | } 120 | }, 121 | 122 | remove: function(handleObj) { 123 | stopTapAndHoldDetector.call(this); 124 | if (handleObj.data) { 125 | $(this).unbind(TOUCHSTART, handleObj.data, onTouchStart); 126 | } else { 127 | $(this).unbind(TOUCHSTART, onTouchStart); 128 | } 129 | }, 130 | 131 | teardown: function() { 132 | 133 | } 134 | }; 135 | 136 | })(jQuery); -------------------------------------------------------------------------------- /src/js/lib/touch.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | 3 | document.addEventListener("touchstart", touchHandler, true); 4 | document.addEventListener("touchmove", touchHandler, true); 5 | document.addEventListener("touchend", touchHandler, true); 6 | document.addEventListener("touchcancel", touchHandler, true); 7 | 8 | function touchHandler(event) { 9 | var touches = event.changedTouches, 10 | first = touches[0], 11 | type = ""; 12 | switch(event.type) { 13 | case "touchstart": type="mousedown"; break; 14 | case "touchmove": type="mousemove"; break; 15 | case "touchend": type="mouseup"; break; 16 | default: return; 17 | } 18 | 19 | var simulatedEvent = document.createEvent("MouseEvent"); 20 | simulatedEvent.initMouseEvent( 21 | type, true, true, window, 1, 22 | first.screenX, first.screenY, 23 | first.clientX, first.clientY, false, 24 | false, false, false, 0/*left*/, null 25 | ); 26 | if(touches.length < 2) { 27 | first.target.dispatchEvent(simulatedEvent); 28 | //event.preventDefault(); 29 | } 30 | } 31 | 32 | })(); 33 | -------------------------------------------------------------------------------- /src/js/loading.js: -------------------------------------------------------------------------------- 1 | (function(){ 2 | const canvasContent = localStorage.getItem("md-canvasContent"); 3 | const isDark = localStorage.getItem("md-darkmode"); 4 | if (!isDark && isDark !== null) document.body.classList.add("inverted"); 5 | if (!canvasContent) return; 6 | const parser = new DOMParser(); 7 | const doc = parser.parseFromString(canvasContent, "image/svg+xml"); 8 | const workarea = document.getElementById("workarea"); 9 | workarea.appendChild(doc.documentElement); 10 | const svgCanvas = document.getElementById("svgcanvas"); 11 | const canvasTitle = localStorage.getItem("md-canvasTitle"); 12 | svgCanvas.setAttribute("title", canvasTitle ? "Loading " + canvasTitle : "Loading Drawing"); 13 | const svg = workarea.querySelector("svg"); 14 | })(); -------------------------------------------------------------------------------- /src/js/modalbackup.js: -------------------------------------------------------------------------------- 1 | 2 | var orig_source = false; 3 | 4 | $("button.cancel, .overlay").on("click", cancelOverlays); 5 | $("#tool_source").on("click", viewSource); 6 | $("#tool_source_save").on("click", saveSource); 7 | 8 | function viewSource(e, forSaving){ 9 | editor.menu.flash($('#view_menu')); 10 | $('#save_output_btns').toggle(!!forSaving); 11 | $('#tool_source_back').toggle(!forSaving); 12 | orig_source = svgCanvas.getSvgString(); 13 | $('#svg_source_textarea').val(orig_source); 14 | $('#svg_source_editor').fadeIn(); 15 | $('#svg_source_textarea').focus().select(); 16 | }; 17 | 18 | function saveSource(){ 19 | var saveChanges = function() { 20 | svgCanvas.clearSelection(); 21 | $('#svg_source_editor').hide(); 22 | $('#svg_source_textarea').blur(); 23 | editor.zoom.multiply(1); 24 | editor.rulers.update(); 25 | editor.paintBox.fill.prep(); 26 | editor.paintBox.stroke.prep(); 27 | } 28 | 29 | if (!svgCanvas.setSvgString($('#svg_source_textarea').val())) { 30 | $.confirm("There were parsing errors in your SVG source.\nRevert back to original SVG source?", function(ok) { 31 | if(!ok) return false; 32 | saveChanges(); 33 | }); 34 | } else { 35 | saveChanges(); 36 | } 37 | }; 38 | 39 | function cancelOverlays() { 40 | $('#dialog_box').hide(); 41 | 42 | if (orig_source && orig_source !== $('#svg_source_textarea').val()) { 43 | $.confirm("Ignore changes made to SVG source?", function(ok) { 44 | if(ok) { 45 | $('#svg_source_editor').hide(); 46 | $('#svg_source_textarea').blur(); 47 | }; 48 | }); 49 | } else { 50 | $('#svg_source_editor').hide(); 51 | $('#svg_source_textarea').blur(); 52 | } 53 | }; 54 | 55 | function isVisible(){ 56 | return $('#svg_source_editor').is(":visible"); 57 | } 58 | 59 | this.cancelOverlays = cancelOverlays; 60 | this.isVisible = isVisible; 61 | this.viewSource = viewSource; 62 | this.saveSource = saveSource; -------------------------------------------------------------------------------- /src/js/modals.js: -------------------------------------------------------------------------------- 1 | // globals 2 | const svgCanvas = new $.SvgCanvas(document.getElementById("svgcanvas")); 3 | const editor = new MD.Editor(); 4 | const state = new State(); 5 | 6 | editor.modal = { 7 | about: new MD.Modal({ 8 | html: ` 9 |

    About this application

    10 |

    Method Draw is a simple open source vector drawing application. Method Draw was forked from SVG-Edit several years ago with the goal of improving and modernizing the interface.

    11 |

    At this time (2021), the author (Mark MacKay) is working on improving stability and improving the codebase, which contains a lot of legacy practices. The goal is to create a vector editor suitable for simple graphic design tasks.

    12 | ` 13 | }), 14 | source: new MD.Modal({ 15 | html: ` 16 |
    17 |
    18 |
    19 |
    20 | 21 |
    22 |
    23 | 24 | 25 |
    26 |
    27 |
    `, 28 | js: function(el){ 29 | el.children[0].classList.add("modal-item-source"); 30 | el.querySelector("#tool_source_save").addEventListener("click", function(){ 31 | var saveChanges = function() { 32 | svgCanvas.clearSelection(); 33 | $('#svg_source_textarea').blur(); 34 | editor.zoom.multiply(1); 35 | editor.rulers.update(); 36 | editor.paintBox.fill.prep(); 37 | editor.paintBox.stroke.prep(); 38 | editor.modal.source.close(); 39 | } 40 | 41 | if (!svgCanvas.setSvgString($('#svg_source_textarea').val())) { 42 | $.confirm("There were parsing errors in your SVG source.\nRevert back to original SVG source?", function(ok) { 43 | if(!ok) return false; 44 | saveChanges(); 45 | }); 46 | } else { 47 | saveChanges(); 48 | } 49 | }) 50 | el.querySelector("#tool_source_cancel").addEventListener("click", function(){ 51 | editor.modal.source.close(); 52 | }); 53 | } 54 | }), 55 | configure: new MD.Modal({ 56 | html: ` 57 |

    Configuration

    58 |
    59 | 60 |
    61 | `, 62 | js: function(el){ 63 | const input = el.querySelector("#configuration button.warning"); 64 | input.addEventListener("click", function(){ 65 | state.clean(); 66 | }) 67 | } 68 | }), 69 | donate: new MD.Modal({ 70 | html: ` 71 |

    Donate

    72 |

    73 | Method Draw relies on your generous donations for continued development. 74 | Donate now if you find this application useful. 75 |

    ` 76 | }), 77 | shortcuts: new MD.Modal({ 78 | html: ` 79 |

    Shortcuts

    80 |
    `, 81 | js: function(el){ 82 | el.children[0].classList.add("modal-item-wide"); 83 | } 84 | }) 85 | }; -------------------------------------------------------------------------------- /src/js/paste.js: -------------------------------------------------------------------------------- 1 | // TODO as is paste is currently not working 2 | document.onpaste = function(event){ 3 | var items = (event.clipboardData || event.originalEvent.clipboardData).items; 4 | for (index in items) { 5 | var item = items[index]; 6 | if (item.kind === 'file') { 7 | var blob = item.getAsFile(); 8 | var reader = new FileReader(); 9 | reader.onload = function(event){}; // remove? 10 | reader.readAsDataURL(blob); 11 | } 12 | } 13 | } -------------------------------------------------------------------------------- /src/js/selectedChanged.js: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/js/shapelib/dialog_balloon.json: -------------------------------------------------------------------------------- 1 | {"data": { 2 | "1": "m0.99786,35.96579l0,0c0,-19.31077 15.28761,-34.96524 34.14583,-34.96524l15.52084,0l0,0l74.50001,0l139.68748,0c9.05606,0 17.74118,3.68382 24.14478,10.24108c6.40356,6.55726 10.00107,15.45081 10.00107,24.72416l0,87.41311l0,0l0,52.44785l0,0c0,19.31078 -15.2876,34.96524 -34.14584,34.96524l-139.68748,0l-97.32507,88.90848l22.82506,-88.90848l-15.52084,0c-18.85822,0 -34.14583,-15.65446 -34.14583,-34.96524l0,0l0,-52.44785l0,0z", 3 | "4": "m1,1l49.66667,0l0,0l74.5,0l173.83334,0l0,115.8889l0,0l0,49.66666l0,33.11111l-173.83334,0l-123.68433,97.37498l49.18433,-97.37498l-49.66667,0l0,-33.11111l0,-49.66666l0,0z", 4 | "5": "m3.88165,296.34811l58.64952,-105.30074l0,0c-62.13446,-31.76456 -79.86445,-91.6022 -40.96117,-138.24044c38.90255,-46.63797 121.70818,-64.81269 191.29914,-41.98796c69.59094,22.8246 103.19446,79.17835 77.63046,130.19172c-25.56265,51.01335 -101.92546,79.99094 -176.41714,66.94487l-110.20081,88.39255z", 5 | "6": "m4.33333,266.6662c0,-1.854 2.23757,-3.35571 5,-3.35571c2.76243,0 5,1.50171 5,3.35571c0,1.85394 -2.23757,3.35565 -5,3.35565c-2.76243,0 -5,-1.50171 -5,-3.35565zm10.25,-24.11072c0,-4.6351 5.59392,-8.38943 12.50001,-8.38943c6.90608,0 12.5,3.75433 12.5,8.38943c0,4.63489 -5.59392,8.38928 -12.5,8.38928c-6.90609,0 -12.50001,-3.75433 -12.50001,-8.38928zm23.75001,-36.55524c0,-12.81482 19.46685,-23.19473 43.50002,-23.19473c24.0331,0 43.49996,10.37991 43.49996,23.19473c0,12.81473 -19.46686,23.19455 -43.49996,23.19455c-24.03317,0 -43.50002,-10.37982 -43.50002,-23.19455zm-37.33334,-104.99994c0,-55.2486 66.67956,-100 149,-100c82.32047,0 149,44.7514 149,100c0,55.24866 -66.67953,100 -149,100c-82.32044,0 -149,-44.75134 -149,-100z", 6 | "scream": "m299.67374,132.67729l-35.72574,1.97192l-9.55817,48.04506l-31.60561,-11.61551l-45.83566,36.86661l-17.45096,-21.51509l-146.98414,92.00807l81.6677,-102.60858l-67.83573,-13.33963l21.22697,-19.84731l-46.57336,-36.42733l33.47025,-8.80944l-10.52427,-47.94958l35.08694,5.02536l28.86619,-44.2482l25.5638,17.26465l59.09183,-26.49832l7.92432,24.02253l70.55626,-0.33542l-12.23108,23.15343l59.61954,25.93398l-28.50317,14.93327l29.75409,43.96953z", 7 | "thought": "m12,1c-6.094,0 -11,4.906 -11,11l0,147c0,6.09399 4.906,11 11,11l49.15625,0c-2.03143,2.32526 -3.15625,4.84886 -3.15625,7.5c0,11.32597 20.36188,20.5 45.5,20.5c25.13812,0 45.5,-9.17403 45.5,-20.5c0,-2.65114 -1.12482,-5.17474 -3.15625,-7.5l142.15625,0c6.09399,0 11,-4.90601 11,-11l0,-147c0,-6.094 -4.90601,-11 -11,-11l-276,0zm54,199c-13.81215,0 -25,5.37016 -25,12c0,6.62984 11.18785,12 25,12c13.81216,0 25,-5.37016 25,-12c0,-6.62984 -11.18784,-12 -25,-12zm-25,30c-7.73481,0 -14,4.02762 -14,9c0,4.97238 6.26519,9 14,9c7.73481,0 14,-4.02762 14,-9c0,-4.97238 -6.26519,-9 -14,-9zm-24,22c-4.97238,0 -9,2.23756 -9,5c0,2.76242 4.02762,5 9,5c4.97238,0 9,-2.23758 9,-5c0,-2.76244 -4.02762,-5 -9,-5z", 8 | "raph_chat": "m149.40149,33.90986c-80.3511,0 -145.50004,43.31243 -145.50004,96.74474c0,30.56227 21.38675,57.79297 54.68462,75.51593c-4.73782,15.71271 -14.19292,33.11575 -32.46717,48.50804c0,0 42.65602,-2.82466 72.78463,-33.39108c1.7345,0.42561 3.52987,0.74596 5.28078,1.13896c-1.64549,-4.91217 -2.61422,-10.01866 -2.61422,-15.33182c0,-36.04973 40.11905,-64.27756 91.35863,-64.27756c36.80342,0 67.77124,14.61842 82.34897,36.18755c12.40991,-14.23764 19.62378,-30.72852 19.62378,-48.3623c0,-53.42003 -65.14893,-96.73246 -145.49997,-96.73246zm124.25104,173.19301c0,-29.63838 -36.13483,-53.68361 -80.73599,-53.68361c-44.57683,0 -80.73203,24.04523 -80.73203,53.68361c0,29.65428 36.1552,53.68753 80.73203,53.68753c9.87251,0 19.27516,-1.23618 28.01714,-3.39209c16.71373,16.96906 40.38206,18.52942 40.38206,18.52942c-10.13583,-8.54349 -15.3721,-18.19742 -18.00662,-26.92308c18.46475,-9.82388 30.34341,-24.93292 30.34341,-41.90178z", 9 | "raph_bubble": "m151.46498,38.88166c-77.59646,0 -140.49998,47.18048 -140.49998,105.37873c0,19.88803 7.43651,38.46727 20.23268,54.33186l-20.23268,44.35922l56.33016,-14.46355c23.47343,13.21898 52.55724,21.14758 84.16982,21.14758c77.59654,0 140.50002,-47.18053 140.50002,-105.37512c0,-58.19824 -62.90347,-105.37873 -140.50002,-105.37873z", 10 | "raph_codetalk": "m151.43494,41.81969c-77.0443,0 -139.49994,46.84459 -139.49994,104.6212c0,19.74286 7.38358,38.19347 20.08679,53.95245l-20.08679,44.03983l55.92923,-14.34583c23.30623,13.10641 52.18307,20.98224 83.5707,20.98224c77.04425,0 139.50006,-46.84476 139.50006,-104.62869s-62.45581,-104.6212 -139.50006,-104.6212zm-22.87875,144.79852l-23.29687,23.27774l-64.06922,-64.0787l64.05966,-64.09371l23.30643,23.33098l-40.76296,40.76273l40.76296,40.80096zm70.45883,23.24741l-23.28729,-23.30817l40.77419,-40.75514l-40.77419,-40.77059l23.28729,-23.30818l64.06931,64.03304l-64.06931,64.10904z", 11 | "raph_talkq": "m151.95413,37.83469c-77.87229,0 -140.99913,47.34782 -140.99913,105.75328c0,19.94707 7.46298,38.60376 20.30468,54.52449l-20.30468,44.51276l56.5302,-14.49918c23.55888,13.25453 52.74399,21.2072 84.46893,21.2072c77.87204,0 141.00085,-47.3481 141.00085,-105.74527s-63.12881,-105.75328 -141.00085,-105.75328zm8.74214,165.54159l-19.83006,0l0,-19.02583l19.83006,0l0,19.02583zm-0.96628,-33.04105l-17.89746,0l-1.77126,-81.41087l21.28053,0l-1.61182,81.41087z", 12 | "raph_talke": "m151.46997,37.82469c-78.14881,0 -141.49997,47.50852 -141.49997,106.12126c0,20.02579 7.4895,38.74088 20.37584,54.71835l-20.37584,44.67868l56.73113,-14.55873c23.6405,13.30168 52.9314,21.29042 84.76884,21.29042c78.149,0 141.50003,-47.51628 141.50003,-106.12872s-63.35103,-106.12126 -141.50003,-106.12126zm9.92621,166.12944l-19.90253,0l0,-19.09268l19.90253,0l0,19.09268zm0,-39.48112l0,6.31543l-19.90253,0l0,-7.77286c0,-23.45735 26.69424,-27.17406 26.69424,-43.83023c0,-7.60336 -6.79172,-13.43297 -15.69612,-13.43297c-9.21872,0 -17.31352,6.79367 -17.31352,6.79367l-11.32195,-14.0807c0,0 11.15999,-11.65153 30.41292,-11.65153c18.29469,0 35.27467,11.32766 35.27467,30.41285c0.0097,26.71156 -28.14772,29.78053 -28.14772,47.24634z" 13 | } 14 | } -------------------------------------------------------------------------------- /src/js/shapelib/flowchart.json: -------------------------------------------------------------------------------- 1 | {"data": { 2 | "manual_input": "m1,103.64394l298,-30.9037l0,154.51852l-298,0z", 3 | "callout_left_right": "m1,150.0006l58.10869,-58.1087l0,29.05434l46.81141,0l0,-87.16304l87.1598,0l0,87.16304l46.8114,0l0,-29.05434l58.1087,58.1087l-58.1087,58.10869l0,-29.05435l-46.8114,0l0,87.16306l-87.1598,0l0,-87.16306l-46.81141,0l0,29.05435l-58.10869,-58.10869z", 4 | "card": "m1,60.5l59.5,-59.5l238.5,0l0,298l-298,0l0,-238.5z", 5 | "collate": "m0,1l299,0l-149.5,149l149.5,149l-299.00031,0l149.50031,-149l-149.5,-149z", 6 | "terminal": "m48.94167,99.12235l202.11729,0l0,0c26.47794,0 47.9425,22.7794 47.9425,50.8792c0,28.09979 -21.46457,50.87918 -47.9425,50.87918l-202.11729,0l0,0c-26.47791,0 -47.9425,-22.77939 -47.9425,-50.87918c0,-28.09981 21.46459,-50.8792 47.9425,-50.8792z", 7 | "connector_offpage": "m0.99775,0.99775l297.99984,0l0,238.39982l-149.00002,59.60002l-148.99999,-59.60002l0.00017,-238.39982z", 8 | "data_stored": "m50.83397,0.99813l249.16667,0c-27.52219,0 -49.83333,66.78392 -49.83333,149.16604c0,82.38213 22.31114,149.16603 49.83333,149.16603l-249.16667,0l0,0c-27.52219,0 -49.83333,-66.78391 -49.83333,-149.16603c0,-82.38212 22.31114,-149.16604 49.83333,-149.16604z", 9 | "data": "m1.00038,249.33351l59.60001,-198.66668l238.40001,0l-59.60001,198.66668z", 10 | "decision": "m0.99837,149.99953l148.79352,-102.86476l148.79387,102.86476l-148.79387,102.86476l-148.79352,-102.86476z", 11 | "delay": "m1,1l149,0l0,0c82.29044,0 149,66.70957 149,149c0,82.29044 -66.70956,149 -149,149l-149,0z", 12 | "display": "m1,149.99924l49.66672,-97.42307l198.66612,0c27.43034,0 49.66716,43.61774 49.66716,97.42307c0,53.80476 -22.23682,97.42308 -49.66716,97.42308l-198.66612,0l-49.66672,-97.42308z", 13 | "document": "m1.00064,1.00098l298,0l0,242.19891c-149,0 -149,92.28223 -298,39.84915z", 14 | "or_junction": "m0.99865,149.9991l0,0c0,-82.29043 66.70957,-149 149.00001,-149l0,0c39.51724,0 77.41597,15.69817 105.3589,43.64109c27.94292,27.94292 43.64107,65.84166 43.64107,105.35891l0,0c0,82.29041 -66.70956,148.99998 -148.99997,148.99998l0,0c-82.29044,0 -149.00001,-66.70958 -149.00001,-148.99998zm149.00001,-149l0,297.99998m-149.00001,-148.99998l297.99998,0", 15 | "preparation": "m1.00063,150.00006l59.58485,-82.24058l178.75446,0l59.58505,82.24058l-59.58505,82.24086l-178.75446,0l-59.58485,-82.24086z", "sequential_data_storage": "m150,299l0,0c-82.29043,0 -149,-66.70955 -149,-149l0,0c0,-82.29043 66.70957,-149 149,-149l0,0c39.51726,0 77.41599,15.69817 105.3589,43.64108c27.94292,27.94293 43.6411,65.84165 43.6411,105.35892l0,0c0,39.51726 -15.69818,77.41599 -43.6411,105.3589l43.6411,0l0,43.6411z", 16 | "sort": "m-0.0038,150.00102l299.00334,0m-299.00334,-0.00002l149.50209,-150.00059l149.50131,150.00059l-149.50131,150.00018l-149.50209,-150.00018z", 17 | "punched_tape": "m1.00047,30.80047l0,0c0,16.45808 33.35479,29.8 74.50001,29.8c41.1452,0 74.49998,-13.34192 74.49998,-29.8l0,0c0,-16.45809 33.3548,-29.8 74.50002,-29.8c41.14522,0 74.49998,13.34192 74.49998,29.8l0,238.4c0,-16.45808 -33.35477,-29.80002 -74.49998,-29.80002c-41.14522,0 -74.50002,13.34193 -74.50002,29.80002c0,16.45807 -33.35478,29.79999 -74.49998,29.79999c-41.14522,0 -74.50001,-13.34192 -74.50001,-29.79999z", 18 | "wave": "m1,37.20809c99.33355,-125.42461 198.66708,125.4246 298.00061,0l0,225.76426c-99.33353,125.42462 -198.66706,-125.42459 -298.00061,0z" 19 | } 20 | } -------------------------------------------------------------------------------- /src/js/shapelib/math.json: -------------------------------------------------------------------------------- 1 | {"data": { 2 | "divide": "m150,0.99785l0,0c25.17819,0 45.58916,20.41097 45.58916,45.58916c0,25.17821 -20.41096,45.58916 -45.58916,45.58916c-25.17822,0 -45.58916,-20.41093 -45.58916,-45.58916c0,-25.1782 20.41093,-45.58916 45.58916,-45.58916zm0,296.25203c-25.17822,0 -45.58916,-20.41095 -45.58916,-45.58917c0,-25.17819 20.41093,-45.58916 45.58916,-45.58916c25.17819,0 45.58916,20.41096 45.58916,45.58916c0,25.17822 -20.41096,45.58917 -45.58916,45.58917zm-134.06754,-193.71518l268.13507,0l0,91.17833l-268.13507,0z", 3 | "minus": "m0.99887,102.39503l297.49445,0l0,95.2112l-297.49445,0z", 4 | "not_equal": "m40.81188,62.2131l103.7978,0l22.27972,-61.2131l65.67503,23.90375l-13.5795,37.30935l40.20317,0l0,69.88993l-65.64099,0l-12.71893,34.94495l78.35992,0l0,69.88991l-103.79779,0l-22.27972,61.21309l-65.67503,-23.90378l13.57949,-37.30933l-40.20319,0l0,-69.88991l65.64102,0l12.71894,-34.94498l-78.35995,0z", 5 | "times": "m1.00089,73.36786l72.36697,-72.36697l76.87431,76.87368l76.87431,-76.87368l72.36765,72.36697l-76.87433,76.87431l76.87433,76.87431l-72.36765,72.36765l-76.87431,-76.87433l-76.87431,76.87433l-72.36697,-72.36765l76.87368,-76.87431l-76.87368,-76.87431z", 6 | "plus": "m1.00211,102.40185l101.39974,0l0,-101.39975l95.45412,0l0,101.39975l101.3997,0l0,95.45412l-101.3997,0l0,101.3997l-95.45412,0l0,-101.3997l-101.39974,0z" 7 | } 8 | } -------------------------------------------------------------------------------- /src/js/start.js: -------------------------------------------------------------------------------- 1 | editor.keyboard = new MD.Keyboard(); 2 | editor.menu = new MD.Menu(); 3 | editor.toolbar = new MD.Toolbar(); 4 | editor.rulers = new MD.Rulers(); 5 | editor.canvas = new MD.Canvas(); 6 | editor.text = new MD.Text(); 7 | editor.panel = new MD.Panel(); 8 | editor.zoom = new MD.Zoom(); 9 | editor.paintBox = { 10 | fill: new MD.PaintBox('#fill_color', 'fill'), 11 | stroke: new MD.PaintBox('#stroke_color', 'stroke'), 12 | canvas: new MD.PaintBox('#canvas_color', 'canvas') 13 | }; 14 | editor.palette = new MD.Palette(); 15 | editor.pan = new MD.Pan(); 16 | 17 | editor.import = new MD.Import(); 18 | editor.contextMenu = new MD.ContextMenu(); 19 | editor.darkmode = new MD.Darkmode(); 20 | editor.title = new MD.Title(); 21 | 22 | // bind the selected event to our function that handles updates to the UI 23 | svgCanvas.bind("selected", editor.selectedChanged); 24 | svgCanvas.bind("transition", editor.elementTransition); 25 | svgCanvas.bind("changed", editor.elementChanged); 26 | svgCanvas.bind("exported", editor.exportHandler); 27 | svgCanvas.bind("zoomed", editor.zoom.changed); 28 | svgCanvas.bind("contextset", editor.contextChanged); 29 | svgCanvas.bind("extension_added", editor.extensionAdded); 30 | svgCanvas.textActions.setInputElem($("#text")[0]); 31 | const shapeLib = svgCanvas.addExtension.apply(this, ["shapes", MD.Shapelib]); 32 | const eyedropper = svgCanvas.addExtension.apply(this, ["eyedropper", MD.Eyedropper]); 33 | state.set("canvasId", t("Untitled")); 34 | state.set("canvasMode", state.get("canvasMode")); 35 | 36 | // load from param 37 | if (!window.location.search.includes("?load=")) { 38 | svgCanvas.setSvgString(state.get("canvasContent")); 39 | } 40 | else { 41 | 42 | const error = function(err) { 43 | console.log(err); 44 | svgCanvas.setSvgString(state.get("canvasContent")); 45 | } 46 | 47 | const url = utils.findGetParameter("load"); 48 | fetch(url) 49 | .then(r => r.text()) 50 | .then(text => { 51 | if (text.includes("Error response")) return error("Error response"); 52 | svgCanvas.setSvgString(text); 53 | }) 54 | .catch(error); 55 | } 56 | 57 | state.set("canvasTitle", svgCanvas.getDocumentTitle()); 58 | 59 | //editor.paintBox.fill.setPaint(state.get("canvasFill")); 60 | //editor.paintBox.stroke.setPaint(state.get("canvasStroke")); 61 | //editor.paintBox.canvas.setPaint(state.get("canvasBackground")); 62 | 63 | document.body.classList.remove("loading"); 64 | document.getElementById("svgcanvas").removeAttribute("title"); -------------------------------------------------------------------------------- /src/js/state.js: -------------------------------------------------------------------------------- 1 | function State(){ 2 | 3 | const _self = this; 4 | const tenThousandThings = dao.map(thing => thing.name); 5 | const saveableKeys = dao.filter(thing => thing.save).map(thing => thing.name); 6 | const ID = window.location.pathname; 7 | 8 | this.data = _loadData(); 9 | 10 | this.set = (key, val) => { 11 | key = key.split("-")[0] || key; 12 | if (tenThousandThings.indexOf(key) === -1) return console.warn( key + " not implemented"); 13 | const archetype = dao.find(thing => thing.name === key); 14 | val = _self.data[_getKey(key)] = archetype.clean(val); 15 | if (~saveableKeys.indexOf(key)) _save(_getKey(key), val); 16 | _self[key](val); 17 | } 18 | 19 | this.get = (key) => { 20 | return _self.data[_getKey(key)]; 21 | } 22 | 23 | this.refresh = () => { dao.forEach(thing => this[thing.name]( this.get(thing.name) ) ); } 24 | 25 | // canvas data 26 | this.canvasId = (id) => {/* noop */} 27 | this.canvasMode = (mode) => { editor.toolbar.setMode(mode) } 28 | this.canvasTitle = (str) => { editor.canvas.rename(str) } 29 | this.canvasSize = (size) => { editor.canvas.resize(...size) } 30 | this.canvasContent = (svgString) => { /* noop */ } 31 | this.canvasRulers = (bool) => { /* noop */ } 32 | this.canvasFill = (paint) => { /* noop */ } 33 | this.canvasStroke = (paint) => { /* noop */ } 34 | this.canvasBackground = (paint) => { /* noop */ } 35 | this.darkmode = (isDark) => { editor.darkmode.set(isDark) } 36 | 37 | this.clean = (warn = true) => { 38 | if (warn) { 39 | const confirmed = confirm("Clears all editor configuration and canvas, are you sure?"); 40 | if (!confirmed) return; 41 | } 42 | 43 | Object.keys(localStorage).forEach(key => localStorage.removeItem(key)); 44 | window.location.reload(); 45 | } 46 | 47 | // INNER UTILS 48 | 49 | function _save(key, val) { 50 | // basic checks 51 | if (val === undefined || val === null) throw "wont save nuthin, " + key + " " + val; 52 | const isObject = dao.find(thing => thing.name === key).type === "object"; 53 | localStorage.setItem("md" + "-" + key, isObject ? JSON.stringify(val) : val.toString()); 54 | } 55 | 56 | function _getKey(name) { 57 | //const key = name.indexOf("canvas") !== -1 58 | // ? name + "-" + ID 59 | // : name + "-0"; // system 60 | return name; 61 | } 62 | 63 | function _loadData() { 64 | 65 | const data = dao.map(thing => { 66 | return { 67 | [_getKey(thing.name)]: _getValue(_getKey(thing.name), utils.findGetParameter(thing.name) || thing.default) 68 | } 69 | }); 70 | return Object.assign({}, ...data); 71 | }; 72 | 73 | function _getValue(key, def) { 74 | const item = localStorage.getItem("md-" + key) || def; 75 | const archetype = dao.find(thing => thing.name === key.split("-")[0]); 76 | if (archetype) return archetype.clean(item); 77 | else throw "Unkown archetype"; 78 | } 79 | 80 | } -------------------------------------------------------------------------------- /src/js/translate.js: -------------------------------------------------------------------------------- 1 | function t(key){ 2 | 3 | const lang = typeof state === "undefined" ? "en" : state.get("language"); 4 | 5 | const dict = { 6 | "Untitled": "Sin Título", 7 | } 8 | 9 | const keys = Object.keys(dict); 10 | const values = Object.values(dict); 11 | const originWord = keys.find(word => key === word); 12 | const destinationWord = values.find(word => key === word); 13 | 14 | if (!originWord && !destinationWord) { 15 | console.log( "translation missing: " + key); 16 | return key; 17 | } 18 | 19 | const translation = lang === "en" ? key : dict[key]// english input; 20 | 21 | return translation 22 | 23 | } 24 | 25 | function translateDom(lang){ 26 | const els = dom.qsa("[data-translate]"); 27 | els.forEach(el => { 28 | const translation = el.getAttribute("data-translate"); 29 | const source = el.innerHTML; 30 | el.innerHTML = translation; 31 | el.setAttribute("data-translate", source); 32 | }); 33 | } -------------------------------------------------------------------------------- /src/shapelib/dialog_balloon.json: -------------------------------------------------------------------------------- 1 | {"data": { 2 | "1": "m0.99786,35.96579l0,0c0,-19.31077 15.28761,-34.96524 34.14583,-34.96524l15.52084,0l0,0l74.50001,0l139.68748,0c9.05606,0 17.74118,3.68382 24.14478,10.24108c6.40356,6.55726 10.00107,15.45081 10.00107,24.72416l0,87.41311l0,0l0,52.44785l0,0c0,19.31078 -15.2876,34.96524 -34.14584,34.96524l-139.68748,0l-97.32507,88.90848l22.82506,-88.90848l-15.52084,0c-18.85822,0 -34.14583,-15.65446 -34.14583,-34.96524l0,0l0,-52.44785l0,0z", 3 | "4": "m1,1l49.66667,0l0,0l74.5,0l173.83334,0l0,115.8889l0,0l0,49.66666l0,33.11111l-173.83334,0l-123.68433,97.37498l49.18433,-97.37498l-49.66667,0l0,-33.11111l0,-49.66666l0,0z", 4 | "5": "m3.88165,296.34811l58.64952,-105.30074l0,0c-62.13446,-31.76456 -79.86445,-91.6022 -40.96117,-138.24044c38.90255,-46.63797 121.70818,-64.81269 191.29914,-41.98796c69.59094,22.8246 103.19446,79.17835 77.63046,130.19172c-25.56265,51.01335 -101.92546,79.99094 -176.41714,66.94487l-110.20081,88.39255z", 5 | "6": "m4.33333,266.6662c0,-1.854 2.23757,-3.35571 5,-3.35571c2.76243,0 5,1.50171 5,3.35571c0,1.85394 -2.23757,3.35565 -5,3.35565c-2.76243,0 -5,-1.50171 -5,-3.35565zm10.25,-24.11072c0,-4.6351 5.59392,-8.38943 12.50001,-8.38943c6.90608,0 12.5,3.75433 12.5,8.38943c0,4.63489 -5.59392,8.38928 -12.5,8.38928c-6.90609,0 -12.50001,-3.75433 -12.50001,-8.38928zm23.75001,-36.55524c0,-12.81482 19.46685,-23.19473 43.50002,-23.19473c24.0331,0 43.49996,10.37991 43.49996,23.19473c0,12.81473 -19.46686,23.19455 -43.49996,23.19455c-24.03317,0 -43.50002,-10.37982 -43.50002,-23.19455zm-37.33334,-104.99994c0,-55.2486 66.67956,-100 149,-100c82.32047,0 149,44.7514 149,100c0,55.24866 -66.67953,100 -149,100c-82.32044,0 -149,-44.75134 -149,-100z", 6 | "scream": "m299.67374,132.67729l-35.72574,1.97192l-9.55817,48.04506l-31.60561,-11.61551l-45.83566,36.86661l-17.45096,-21.51509l-146.98414,92.00807l81.6677,-102.60858l-67.83573,-13.33963l21.22697,-19.84731l-46.57336,-36.42733l33.47025,-8.80944l-10.52427,-47.94958l35.08694,5.02536l28.86619,-44.2482l25.5638,17.26465l59.09183,-26.49832l7.92432,24.02253l70.55626,-0.33542l-12.23108,23.15343l59.61954,25.93398l-28.50317,14.93327l29.75409,43.96953z", 7 | "thought": "m12,1c-6.094,0 -11,4.906 -11,11l0,147c0,6.09399 4.906,11 11,11l49.15625,0c-2.03143,2.32526 -3.15625,4.84886 -3.15625,7.5c0,11.32597 20.36188,20.5 45.5,20.5c25.13812,0 45.5,-9.17403 45.5,-20.5c0,-2.65114 -1.12482,-5.17474 -3.15625,-7.5l142.15625,0c6.09399,0 11,-4.90601 11,-11l0,-147c0,-6.094 -4.90601,-11 -11,-11l-276,0zm54,199c-13.81215,0 -25,5.37016 -25,12c0,6.62984 11.18785,12 25,12c13.81216,0 25,-5.37016 25,-12c0,-6.62984 -11.18784,-12 -25,-12zm-25,30c-7.73481,0 -14,4.02762 -14,9c0,4.97238 6.26519,9 14,9c7.73481,0 14,-4.02762 14,-9c0,-4.97238 -6.26519,-9 -14,-9zm-24,22c-4.97238,0 -9,2.23756 -9,5c0,2.76242 4.02762,5 9,5c4.97238,0 9,-2.23758 9,-5c0,-2.76244 -4.02762,-5 -9,-5z", 8 | "raph_chat": "m149.40149,33.90986c-80.3511,0 -145.50004,43.31243 -145.50004,96.74474c0,30.56227 21.38675,57.79297 54.68462,75.51593c-4.73782,15.71271 -14.19292,33.11575 -32.46717,48.50804c0,0 42.65602,-2.82466 72.78463,-33.39108c1.7345,0.42561 3.52987,0.74596 5.28078,1.13896c-1.64549,-4.91217 -2.61422,-10.01866 -2.61422,-15.33182c0,-36.04973 40.11905,-64.27756 91.35863,-64.27756c36.80342,0 67.77124,14.61842 82.34897,36.18755c12.40991,-14.23764 19.62378,-30.72852 19.62378,-48.3623c0,-53.42003 -65.14893,-96.73246 -145.49997,-96.73246zm124.25104,173.19301c0,-29.63838 -36.13483,-53.68361 -80.73599,-53.68361c-44.57683,0 -80.73203,24.04523 -80.73203,53.68361c0,29.65428 36.1552,53.68753 80.73203,53.68753c9.87251,0 19.27516,-1.23618 28.01714,-3.39209c16.71373,16.96906 40.38206,18.52942 40.38206,18.52942c-10.13583,-8.54349 -15.3721,-18.19742 -18.00662,-26.92308c18.46475,-9.82388 30.34341,-24.93292 30.34341,-41.90178z", 9 | "raph_bubble": "m151.46498,38.88166c-77.59646,0 -140.49998,47.18048 -140.49998,105.37873c0,19.88803 7.43651,38.46727 20.23268,54.33186l-20.23268,44.35922l56.33016,-14.46355c23.47343,13.21898 52.55724,21.14758 84.16982,21.14758c77.59654,0 140.50002,-47.18053 140.50002,-105.37512c0,-58.19824 -62.90347,-105.37873 -140.50002,-105.37873z", 10 | "raph_codetalk": "m151.43494,41.81969c-77.0443,0 -139.49994,46.84459 -139.49994,104.6212c0,19.74286 7.38358,38.19347 20.08679,53.95245l-20.08679,44.03983l55.92923,-14.34583c23.30623,13.10641 52.18307,20.98224 83.5707,20.98224c77.04425,0 139.50006,-46.84476 139.50006,-104.62869s-62.45581,-104.6212 -139.50006,-104.6212zm-22.87875,144.79852l-23.29687,23.27774l-64.06922,-64.0787l64.05966,-64.09371l23.30643,23.33098l-40.76296,40.76273l40.76296,40.80096zm70.45883,23.24741l-23.28729,-23.30817l40.77419,-40.75514l-40.77419,-40.77059l23.28729,-23.30818l64.06931,64.03304l-64.06931,64.10904z", 11 | "raph_talkq": "m151.95413,37.83469c-77.87229,0 -140.99913,47.34782 -140.99913,105.75328c0,19.94707 7.46298,38.60376 20.30468,54.52449l-20.30468,44.51276l56.5302,-14.49918c23.55888,13.25453 52.74399,21.2072 84.46893,21.2072c77.87204,0 141.00085,-47.3481 141.00085,-105.74527s-63.12881,-105.75328 -141.00085,-105.75328zm8.74214,165.54159l-19.83006,0l0,-19.02583l19.83006,0l0,19.02583zm-0.96628,-33.04105l-17.89746,0l-1.77126,-81.41087l21.28053,0l-1.61182,81.41087z", 12 | "raph_talke": "m151.46997,37.82469c-78.14881,0 -141.49997,47.50852 -141.49997,106.12126c0,20.02579 7.4895,38.74088 20.37584,54.71835l-20.37584,44.67868l56.73113,-14.55873c23.6405,13.30168 52.9314,21.29042 84.76884,21.29042c78.149,0 141.50003,-47.51628 141.50003,-106.12872s-63.35103,-106.12126 -141.50003,-106.12126zm9.92621,166.12944l-19.90253,0l0,-19.09268l19.90253,0l0,19.09268zm0,-39.48112l0,6.31543l-19.90253,0l0,-7.77286c0,-23.45735 26.69424,-27.17406 26.69424,-43.83023c0,-7.60336 -6.79172,-13.43297 -15.69612,-13.43297c-9.21872,0 -17.31352,6.79367 -17.31352,6.79367l-11.32195,-14.0807c0,0 11.15999,-11.65153 30.41292,-11.65153c18.29469,0 35.27467,11.32766 35.27467,30.41285c0.0097,26.71156 -28.14772,29.78053 -28.14772,47.24634z" 13 | } 14 | } -------------------------------------------------------------------------------- /src/shapelib/flowchart.json: -------------------------------------------------------------------------------- 1 | {"data": { 2 | "manual_input": "m1,103.64394l298,-30.9037l0,154.51852l-298,0z", 3 | "callout_left_right": "m1,150.0006l58.10869,-58.1087l0,29.05434l46.81141,0l0,-87.16304l87.1598,0l0,87.16304l46.8114,0l0,-29.05434l58.1087,58.1087l-58.1087,58.10869l0,-29.05435l-46.8114,0l0,87.16306l-87.1598,0l0,-87.16306l-46.81141,0l0,29.05435l-58.10869,-58.10869z", 4 | "card": "m1,60.5l59.5,-59.5l238.5,0l0,298l-298,0l0,-238.5z", 5 | "collate": "m0,1l299,0l-149.5,149l149.5,149l-299.00031,0l149.50031,-149l-149.5,-149z", 6 | "terminal": "m48.94167,99.12235l202.11729,0l0,0c26.47794,0 47.9425,22.7794 47.9425,50.8792c0,28.09979 -21.46457,50.87918 -47.9425,50.87918l-202.11729,0l0,0c-26.47791,0 -47.9425,-22.77939 -47.9425,-50.87918c0,-28.09981 21.46459,-50.8792 47.9425,-50.8792z", 7 | "connector_offpage": "m0.99775,0.99775l297.99984,0l0,238.39982l-149.00002,59.60002l-148.99999,-59.60002l0.00017,-238.39982z", 8 | "data_stored": "m50.83397,0.99813l249.16667,0c-27.52219,0 -49.83333,66.78392 -49.83333,149.16604c0,82.38213 22.31114,149.16603 49.83333,149.16603l-249.16667,0l0,0c-27.52219,0 -49.83333,-66.78391 -49.83333,-149.16603c0,-82.38212 22.31114,-149.16604 49.83333,-149.16604z", 9 | "data": "m1.00038,249.33351l59.60001,-198.66668l238.40001,0l-59.60001,198.66668z", 10 | "decision": "m0.99837,149.99953l148.79352,-102.86476l148.79387,102.86476l-148.79387,102.86476l-148.79352,-102.86476z", 11 | "delay": "m1,1l149,0l0,0c82.29044,0 149,66.70957 149,149c0,82.29044 -66.70956,149 -149,149l-149,0z", 12 | "display": "m1,149.99924l49.66672,-97.42307l198.66612,0c27.43034,0 49.66716,43.61774 49.66716,97.42307c0,53.80476 -22.23682,97.42308 -49.66716,97.42308l-198.66612,0l-49.66672,-97.42308z", 13 | "document": "m1.00064,1.00098l298,0l0,242.19891c-149,0 -149,92.28223 -298,39.84915z", 14 | "or_junction": "m0.99865,149.9991l0,0c0,-82.29043 66.70957,-149 149.00001,-149l0,0c39.51724,0 77.41597,15.69817 105.3589,43.64109c27.94292,27.94292 43.64107,65.84166 43.64107,105.35891l0,0c0,82.29041 -66.70956,148.99998 -148.99997,148.99998l0,0c-82.29044,0 -149.00001,-66.70958 -149.00001,-148.99998zm149.00001,-149l0,297.99998m-149.00001,-148.99998l297.99998,0", 15 | "preparation": "m1.00063,150.00006l59.58485,-82.24058l178.75446,0l59.58505,82.24058l-59.58505,82.24086l-178.75446,0l-59.58485,-82.24086z", "sequential_data_storage": "m150,299l0,0c-82.29043,0 -149,-66.70955 -149,-149l0,0c0,-82.29043 66.70957,-149 149,-149l0,0c39.51726,0 77.41599,15.69817 105.3589,43.64108c27.94292,27.94293 43.6411,65.84165 43.6411,105.35892l0,0c0,39.51726 -15.69818,77.41599 -43.6411,105.3589l43.6411,0l0,43.6411z", 16 | "sort": "m-0.0038,150.00102l299.00334,0m-299.00334,-0.00002l149.50209,-150.00059l149.50131,150.00059l-149.50131,150.00018l-149.50209,-150.00018z", 17 | "punched_tape": "m1.00047,30.80047l0,0c0,16.45808 33.35479,29.8 74.50001,29.8c41.1452,0 74.49998,-13.34192 74.49998,-29.8l0,0c0,-16.45809 33.3548,-29.8 74.50002,-29.8c41.14522,0 74.49998,13.34192 74.49998,29.8l0,238.4c0,-16.45808 -33.35477,-29.80002 -74.49998,-29.80002c-41.14522,0 -74.50002,13.34193 -74.50002,29.80002c0,16.45807 -33.35478,29.79999 -74.49998,29.79999c-41.14522,0 -74.50001,-13.34192 -74.50001,-29.79999z", 18 | "wave": "m1,37.20809c99.33355,-125.42461 198.66708,125.4246 298.00061,0l0,225.76426c-99.33353,125.42462 -198.66706,-125.42459 -298.00061,0z" 19 | } 20 | } -------------------------------------------------------------------------------- /src/shapelib/math.json: -------------------------------------------------------------------------------- 1 | {"data": { 2 | "divide": "m150,0.99785l0,0c25.17819,0 45.58916,20.41097 45.58916,45.58916c0,25.17821 -20.41096,45.58916 -45.58916,45.58916c-25.17822,0 -45.58916,-20.41093 -45.58916,-45.58916c0,-25.1782 20.41093,-45.58916 45.58916,-45.58916zm0,296.25203c-25.17822,0 -45.58916,-20.41095 -45.58916,-45.58917c0,-25.17819 20.41093,-45.58916 45.58916,-45.58916c25.17819,0 45.58916,20.41096 45.58916,45.58916c0,25.17822 -20.41096,45.58917 -45.58916,45.58917zm-134.06754,-193.71518l268.13507,0l0,91.17833l-268.13507,0z", 3 | "minus": "m0.99887,102.39503l297.49445,0l0,95.2112l-297.49445,0z", 4 | "not_equal": "m40.81188,62.2131l103.7978,0l22.27972,-61.2131l65.67503,23.90375l-13.5795,37.30935l40.20317,0l0,69.88993l-65.64099,0l-12.71893,34.94495l78.35992,0l0,69.88991l-103.79779,0l-22.27972,61.21309l-65.67503,-23.90378l13.57949,-37.30933l-40.20319,0l0,-69.88991l65.64102,0l12.71894,-34.94498l-78.35995,0z", 5 | "times": "m1.00089,73.36786l72.36697,-72.36697l76.87431,76.87368l76.87431,-76.87368l72.36765,72.36697l-76.87433,76.87431l76.87433,76.87431l-72.36765,72.36765l-76.87431,-76.87433l-76.87431,76.87433l-72.36697,-72.36765l76.87368,-76.87431l-76.87368,-76.87431z", 6 | "plus": "m1.00211,102.40185l101.39974,0l0,-101.39975l95.45412,0l0,101.39975l101.3997,0l0,95.45412l-101.3997,0l0,101.3997l-95.45412,0l0,-101.3997l-101.39974,0z" 7 | } 8 | } -------------------------------------------------------------------------------- /src/shapelib/raphael.txt: -------------------------------------------------------------------------------- 1 | All Raphaël icons were retrieved from here: 2 | http://raphaeljs.com/icons/ 3 | 4 | And fall under the MIT license: 5 | 6 | Copyright © 2008 Dmitry Baranovskiy 7 | 8 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 11 | 12 | The software is provided “as is”, without warranty of any kind, express or implied, including but not limited to the warranties of merchantability, fitness for a particular purpose and noninfringement. In no event shall the authors or copyright holders be liable for any claim, damages or other liability, whether in an action of contract, tort or otherwise, arising from, out of or in connection with the software or the use or other dealings in the software. -------------------------------------------------------------------------------- /src/site.webmanifest: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Blank Page", 3 | "short_name": "Blank Page", 4 | "start_url": "/", 5 | "icons": [ 6 | { 7 | "src": "/android-chrome-192x192.png", 8 | "sizes": "192x192", 9 | "type": "image/png" 10 | } 11 | ], 12 | "theme_color": "#fafafa", 13 | "display": "fullscreen", 14 | "background_color": "#fafafa" 15 | } 16 | -------------------------------------------------------------------------------- /test/all_tests.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | All SVG-edit Tests 5 | 6 | 7 |

    All SVG-edit Tests

    8 |

    This file frames all SVG-edit test pages. This should only include tests known to work. These tests are known to pass 100% in the following: Firefox 3.6, Chrome 7, IE9 Preview 6 (1.9.8006.6000), Opera 10.63. If a test is broken in this page, it is possible that YOU broke it. Please do not submit code that breaks any of these tests.

    9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 28 | -------------------------------------------------------------------------------- /test/contextmenu_test.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 11 | 12 | 13 | 74 | 75 | 76 |

    Unit Tests for contextmenu.js

    77 |

    78 |

    79 |
      80 |
    81 | 82 | 83 | 84 | -------------------------------------------------------------------------------- /test/math_test.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 106 | 107 | 108 |

    Unit Tests for math.js

    109 |

    110 |

    111 |
      112 |
    113 | 114 | 115 | -------------------------------------------------------------------------------- /test/path_test.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 19 | 20 | 21 |

    Unit Tests for path.js

    22 |

    23 |

    24 |
      25 |
    26 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /test/qunit/qunit.css: -------------------------------------------------------------------------------- 1 | /** Font Family and Sizes */ 2 | 3 | #qunit-tests, #qunit-header, #qunit-banner, #qunit-testrunner-toolbar, #qunit-userAgent, #qunit-testresult { 4 | font-family: "Helvetica Neue Light", "HelveticaNeue-Light", "Helvetica Neue", Calibri, Helvetica, Arial; 5 | } 6 | 7 | #qunit-testrunner-toolbar, #qunit-userAgent, #qunit-testresult, #qunit-tests li { font-size: small; } 8 | #qunit-tests { font-size: smaller; } 9 | 10 | 11 | /** Resets */ 12 | 13 | #qunit-tests, #qunit-tests ol, #qunit-header, #qunit-banner, #qunit-userAgent, #qunit-testresult { 14 | margin: 0; 15 | padding: 0; 16 | } 17 | 18 | 19 | /** Header */ 20 | 21 | #qunit-header { 22 | padding: 0.5em 0 0.5em 1em; 23 | 24 | color: #8699a4; 25 | background-color: #0d3349; 26 | 27 | font-size: 1.5em; 28 | line-height: 1em; 29 | font-weight: normal; 30 | 31 | border-radius: 15px 15px 0 0; 32 | -moz-border-radius: 15px 15px 0 0; 33 | -webkit-border-top-right-radius: 15px; 34 | -webkit-border-top-left-radius: 15px; 35 | } 36 | 37 | #qunit-header a { 38 | text-decoration: none; 39 | color: #c2ccd1; 40 | } 41 | 42 | #qunit-header a:hover, 43 | #qunit-header a:focus { 44 | color: #fff; 45 | } 46 | 47 | #qunit-banner { 48 | height: 5px; 49 | } 50 | 51 | #qunit-testrunner-toolbar { 52 | padding: 0.5em 0 0.5em 2em; 53 | color: #5E740B; 54 | background-color: #eee; 55 | } 56 | 57 | #qunit-userAgent { 58 | padding: 0.5em 0 0.5em 2.5em; 59 | background-color: #2b81af; 60 | color: #fff; 61 | text-shadow: rgba(0, 0, 0, 0.5) 2px 2px 1px; 62 | } 63 | 64 | 65 | /** Tests: Pass/Fail */ 66 | 67 | #qunit-tests { 68 | list-style-position: inside; 69 | } 70 | 71 | #qunit-tests li { 72 | padding: 0.4em 0.5em 0.4em 2.5em; 73 | border-bottom: 1px solid #fff; 74 | list-style-position: inside; 75 | } 76 | 77 | #qunit-tests li strong { 78 | cursor: pointer; 79 | } 80 | 81 | #qunit-tests ol { 82 | margin-top: 0.5em; 83 | padding: 0.5em; 84 | 85 | background-color: #fff; 86 | 87 | border-radius: 15px; 88 | -moz-border-radius: 15px; 89 | -webkit-border-radius: 15px; 90 | 91 | box-shadow: inset 0px 2px 13px #999; 92 | -moz-box-shadow: inset 0px 2px 13px #999; 93 | -webkit-box-shadow: inset 0px 2px 13px #999; 94 | } 95 | 96 | #qunit-tests table { 97 | border-collapse: collapse; 98 | margin-top: .2em; 99 | } 100 | 101 | #qunit-tests th { 102 | text-align: right; 103 | vertical-align: top; 104 | padding: 0 .5em 0 0; 105 | } 106 | 107 | #qunit-tests td { 108 | vertical-align: top; 109 | } 110 | 111 | #qunit-tests pre { 112 | margin: 0; 113 | white-space: pre-wrap; 114 | word-wrap: break-word; 115 | } 116 | 117 | #qunit-tests del { 118 | background-color: #e0f2be; 119 | color: #374e0c; 120 | text-decoration: none; 121 | } 122 | 123 | #qunit-tests ins { 124 | background-color: #ffcaca; 125 | color: #500; 126 | text-decoration: none; 127 | } 128 | 129 | /*** Test Counts */ 130 | 131 | #qunit-tests b.counts { color: black; } 132 | #qunit-tests b.passed { color: #5E740B; } 133 | #qunit-tests b.failed { color: #710909; } 134 | 135 | #qunit-tests li li { 136 | margin: 0.5em; 137 | padding: 0.4em 0.5em 0.4em 0.5em; 138 | background-color: #fff; 139 | border-bottom: none; 140 | list-style-position: inside; 141 | } 142 | 143 | /*** Passing Styles */ 144 | 145 | #qunit-tests li li.pass { 146 | color: #5E740B; 147 | background-color: #fff; 148 | border-left: 26px solid #C6E746; 149 | } 150 | 151 | #qunit-tests .pass { color: #528CE0; background-color: #D2E0E6; } 152 | #qunit-tests .pass .test-name { color: #366097; } 153 | 154 | #qunit-tests .pass .test-actual, 155 | #qunit-tests .pass .test-expected { color: #999999; } 156 | 157 | #qunit-banner.qunit-pass { background-color: #C6E746; } 158 | 159 | /*** Failing Styles */ 160 | 161 | #qunit-tests li li.fail { 162 | color: #710909; 163 | background-color: #fff; 164 | border-left: 26px solid #EE5757; 165 | } 166 | 167 | #qunit-tests .fail { color: #000000; background-color: #EE5757; } 168 | #qunit-tests .fail .test-name, 169 | #qunit-tests .fail .module-name { color: #000000; } 170 | 171 | #qunit-tests .fail .test-actual { color: #EE5757; } 172 | #qunit-tests .fail .test-expected { color: green; } 173 | 174 | #qunit-banner.qunit-fail { background-color: #EE5757; } 175 | 176 | 177 | /** Footer */ 178 | 179 | #qunit-testresult { 180 | padding: 0.5em 0.5em 0.5em 2.5em; 181 | 182 | color: #2b81af; 183 | background-color: #D2E0E6; 184 | 185 | border-radius: 0 0 15px 15px; 186 | -moz-border-radius: 0 0 15px 15px; 187 | -webkit-border-bottom-right-radius: 15px; 188 | -webkit-border-bottom-left-radius: 15px; 189 | } 190 | 191 | /** Fixture */ 192 | 193 | #qunit-fixture { 194 | position: absolute; 195 | top: -10000px; 196 | left: -10000px; 197 | } 198 | -------------------------------------------------------------------------------- /test/select_test.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 145 | 146 | 147 |

    Unit Tests for select.js

    148 |

    149 |

    150 |
      151 |
    152 |
    153 | 154 | 155 | -------------------------------------------------------------------------------- /test/svgutils_test.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 126 | 127 | 128 |

    Unit Tests for svgutils.js

    129 |

    130 |

    131 |
      132 |
    133 | 134 | 135 | -------------------------------------------------------------------------------- /test/units_test.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 79 | 80 | 81 |

    Unit Tests for units.js

    82 |

    83 |

    84 |
      85 |
    86 | 88 |
    89 | 90 | 91 |
    92 | 93 | 94 | 95 | 96 | --------------------------------------------------------------------------------