├── .replit ├── LICENSE ├── README.md ├── android-chrome-192x192.png ├── android-chrome-512x512.png ├── apple-touch-icon.png ├── css ├── Lato-Light.ttf └── style.css ├── demos └── recycling_TouchDrawer.json ├── favicon-16x16.png ├── favicon-32x32.png ├── favicon.ico ├── header.png ├── header.svg ├── index.html ├── js ├── app.js └── browsersupport.js ├── libraries ├── SVGAnimFrames │ └── SVGAnimFrames.js ├── alertifyjs │ ├── alertify.core.css │ ├── alertify.default.css │ └── alertify.js ├── bowser │ └── bowser.min.js ├── fabricjs │ ├── eraser_brush.mixin.js │ ├── fabric.js │ ├── fabric.min.js │ ├── lasso.mixin.js │ └── lassoerase.mixin.js ├── gifshot │ ├── gifshot.js │ └── gifshot.min.js ├── jquery │ ├── jquery-migrate-1.2.1.min.js │ └── jquery.js ├── jszip │ ├── Blob.js │ ├── FileSaver.js │ ├── jszip-utils.js │ └── jszip.min.js ├── normalize │ └── normalize.css ├── panzoom │ ├── panzoom.js │ ├── panzoom.min.js │ └── panzoom.mod.js ├── pickr │ ├── pickr.es5.min.js │ ├── pickr.min.js │ └── themes │ │ ├── classic.min.css │ │ ├── monolith.min.css │ │ └── nano.min.css ├── saveSvgAsPng │ └── saveSvgAsPng.js ├── sortablejs │ ├── Sortable.js │ └── jquery-sortable.js └── sweetalert2 │ ├── sweetalert2.min.css │ └── sweetalert2.min.js ├── logo.svg ├── screenshot.png ├── site.webmanifest └── svgs ├── add.svg ├── add2.svg ├── alignbottom.svg ├── aligncenter.svg ├── alignleft.svg ├── alignmiddle.svg ├── alignright.svg ├── aligntop.svg ├── blur.svg ├── brightness.svg ├── broom.svg ├── brush.svg ├── cog.svg ├── color.svg ├── contrast.svg ├── donate.svg ├── duplicate.svg ├── ellipse.svg ├── eraser.svg ├── eraser2.svg ├── eraser3.svg ├── eye.svg ├── eyedropper.svg ├── eyeslash.svg ├── fill.svg ├── filters.svg ├── filters2.svg ├── fliph.svg ├── flipv.svg ├── graduation-cap.svg ├── group.svg ├── hue.svg ├── image.svg ├── imagefilters.svg ├── invert.svg ├── invert2.svg ├── lasso.svg ├── lastframe.svg ├── layers.svg ├── layers1.svg ├── layers2.svg ├── layers3.svg ├── layers4.svg ├── leftarrow.svg ├── line.svg ├── line2.svg ├── nextframe.svg ├── panda.svg ├── pause.svg ├── pen.svg ├── pencil.svg ├── play.svg ├── polygon.svg ├── rect.svg ├── redo.svg ├── resetsize.svg ├── rightarrow.svg ├── rotateccw.svg ├── rotatecw.svg ├── saturation.svg ├── savecode.svg ├── savecode2.svg ├── saveimage.svg ├── saveimage2.svg ├── select.svg ├── selectall.svg ├── selectall2.svg ├── sendbackward.svg ├── sendforward.svg ├── splatter.svg ├── spray.svg ├── stop.svg ├── times.svg ├── trash.svg ├── triangle.svg ├── undo.svg ├── ungroup.svg ├── youtube.svg ├── zoom-no.svg └── zoom.svg /.replit: -------------------------------------------------------------------------------- 1 | language = "html" 2 | run = " " -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Michael Schwartz 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | TouchDrawer 2 | =================== 3 | 4 | Just a free and open source vector drawing tool for mobile. Try it today at [michaelsboost.com/TouchDrawer](https://michaelsboost.github.io/TouchDrawer) 5 | 6 | ![](https://raw.githubusercontent.com/michaelsboost/TouchDrawer/gh-pages/screenshot.png) 7 | 8 | Version 9 | ------------- 10 | 11 | 1.000-release 12 | 13 | Shortcut Keys 14 | ------------- 15 | 16 | 1. (Ctrl+Shift+Z) Redo (Cmd+Shift+Z) 17 | 2. (Ctrl+Z) Undo (Cmd+Z) 18 | 3. (Ctrl+A) Select All (Cmd+A) 19 | 4. (Ctrl+G) Group (Cmd+G) 20 | 5. (Ctrl+Shift+G) Ungroup (Cmd+Shift+G) 21 | 6. (Ctrl+X) Cut (Cmd+X) 22 | 7. (Ctrl+C) Copy (Cmd+C) 23 | 8. (Ctrl+V) Paste (Cmd+V) 24 | 9. (Ctrl+S) Save (Cmd+S) 25 | 10. (Ctrl+E) Export Image Sequence 26 | 11. (Ctrl+N) New Project (Cmd+N) and even (Alt+N) 27 | 12. (DEL) - Delete selected object 28 | 13. (Alt++) - Add frame 29 | 14. (Alt+-) - Delete last frame 30 | 15. (Alt+X) - Clear canvas 31 | 16. (Spacebar) - Start/Stop Animation 32 | 17. (1) - Toggle Zoom/Pan 33 | 18. (2) - Toggle Color Picker 34 | 19. (3) - Toggle Eyedropper 35 | 20. (4) - Toggle Select 36 | 21. (5) - Toggle Pencil 37 | 22. (6) - Toggle Brush 38 | 23. (7) - Toggle Eraser 39 | 24. (8) - Toggle Lasso 40 | 25. (9) - Toggle LassoErase 41 | 26. (Shift+1) - Toggle Rect 42 | 27. (Shift+2) - Toggle Ellipse 43 | 28. (Shift+3) - Toggle Line 44 | 29. (Shift+4) - Toggle Triangle 45 | 30. (Shift+5) - Toggle Splatter 46 | 31. (Shift+6) - Toggle Spray 47 | 32. (Shift+7) - Toggle Fill 48 | 33. (Shift+8) - Toggle Filters 49 | 34. (0) - Apply Fill As Canvas Background Color 50 | 35. (I) - Open Application Info 51 | 52 | To Do: 53 | ------------- 54 | * Allow user to change layers (roughLayer being in the back, paintLayer above that then, highlightsLayer and lastly inkLayer) 55 | * Filters for selected objects 56 | * Within select tool add a subtool to edit Paths (Bezier Curves), Polygons, Lines and Polylines 57 | 58 | License 59 | ------------- 60 | 61 | MIT 62 | 63 | Tech 64 | ------------- 65 | 66 | TouchDrawer uses a number of open source projects to work properly: 67 | 68 | * [Normalize](https://github.com/necolas/normalize.css) - CSS reset library 69 | * [jQuery](http://jquery.com/) - duh 70 | * [Pickr](https://simonwep.github.io/pickr/) - The color picker 71 | * [AlertifyJS](http://alertifyjs.com/) - For the awesome and stylish notification logs 72 | * [SweetAlert](https://sweetalert.js.org/guides/) - For the awesome and stylish alert dialogs 73 | * [panzoom](https://github.com/anvaka/panzoom/) - Used to pan and zoom the canvas 74 | * [SVGAnimFrames](https://michaelsboost.com/SVGAnimFrames/) - Used to render your animation 75 | * [JSZip](https://stuk.github.io/jszip/) - package zip files in javascript 76 | * [Gifshot](https://github.com/yahoo/gifshot) - Used to make the gif animation 77 | * [FabricJS](http://fabricjs.com/) - The technology that makes the drawing possible 78 | 79 | Development 80 | ------------- 81 | 82 | Want to contribute? Great! 83 | 84 | You can submit a pull request or simply share the project :) 85 | 86 | *As of March 11th, 2022 TouchDrawer is no longer an active project. All updates as of that date and on are solely contributor based implementations.* 87 | 88 | Of course TouchDrawer is free and open source, so you can always fork the project and have fun :) 89 | 90 | [![ko-fi](https://az743702.vo.msecnd.net/cdn/kofi2.png?v=0)](https://ko-fi.com/michaelsboost) 91 | 92 | If TouchDrawer was at all helpful for you. You can show your appreciation a few ways... 93 | 94 | 1) Check out my Graphic Design Course: https://michaelsboost.com/graphicdesign 95 | 2) Registering on my store as a customer: https://michaelsboost.com/store 96 | 3) Buying me a coffee! http://ko-fi.com/michaelsboost 97 | 4) Purchasing one of my t-shirts: https://michaelsboost.com/gear 98 | 5) Purchasing any of my art: https://deviantart.com/michaelsboost/prints 99 | 6) Donating via PayPal: https://michaelsboost.com/donate 100 | 7) Donating via SquareCash: https://cash.me/$michaelsboost -------------------------------------------------------------------------------- /android-chrome-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/michaelsboost/TouchDrawer/4e78060cd184e2af47376fbb05bbaedf3bc31443/android-chrome-192x192.png -------------------------------------------------------------------------------- /android-chrome-512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/michaelsboost/TouchDrawer/4e78060cd184e2af47376fbb05bbaedf3bc31443/android-chrome-512x512.png -------------------------------------------------------------------------------- /apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/michaelsboost/TouchDrawer/4e78060cd184e2af47376fbb05bbaedf3bc31443/apple-touch-icon.png -------------------------------------------------------------------------------- /css/Lato-Light.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/michaelsboost/TouchDrawer/4e78060cd184e2af47376fbb05bbaedf3bc31443/css/Lato-Light.ttf -------------------------------------------------------------------------------- /favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/michaelsboost/TouchDrawer/4e78060cd184e2af47376fbb05bbaedf3bc31443/favicon-16x16.png -------------------------------------------------------------------------------- /favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/michaelsboost/TouchDrawer/4e78060cd184e2af47376fbb05bbaedf3bc31443/favicon-32x32.png -------------------------------------------------------------------------------- /favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/michaelsboost/TouchDrawer/4e78060cd184e2af47376fbb05bbaedf3bc31443/favicon.ico -------------------------------------------------------------------------------- /header.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/michaelsboost/TouchDrawer/4e78060cd184e2af47376fbb05bbaedf3bc31443/header.png -------------------------------------------------------------------------------- /header.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /js/browsersupport.js: -------------------------------------------------------------------------------- 1 | /* 2 | Version: 1.000-release 3 | TouchDrawer, copyright (c) by Michael Schwartz 4 | Distributed under an MIT license: https://github.com/michaelsboost/TouchDrawer/blob/gh-pages/LICENSE 5 | 6 | This is TouchDrawer (https://michaelsboost.github.io/TouchDrawer/), Just a free and open source vector drawing tool for mobile. 7 | */ 8 | 9 | // Detect browser support onload 10 | function unsupportedBrowser() { 11 | alertify.error("Error: You are using an unsupported browser!"); 12 | setTimeout(function() { 13 | alertify.log('We recommend using the most recent version of Google Chrome'); 14 | }, 2000); 15 | } 16 | if (bowser.msie && bowser.version <= 6) { 17 | // hello ie 18 | unsupportedBrowser(); 19 | } else if (bowser.firefox) { 20 | // hello firefox 21 | unsupportedBrowser(); 22 | } else if (bowser.chrome) { 23 | // hello chrome 24 | } else if (bowser.safari) { 25 | // hello safari 26 | unsupportedBrowser(); 27 | } else if(bowser.iphone || bowser.android) { 28 | // hello mobile 29 | unsupportedBrowser(); 30 | } -------------------------------------------------------------------------------- /libraries/SVGAnimFrames/SVGAnimFrames.js: -------------------------------------------------------------------------------- 1 | /* 2 | Version: 0.0.3-alpha 3 | SVGAnimFrames, copyright (c) by Michael Schwartz 4 | Distributed under an MIT license: https://github.com/michaelsboost/SVGAnimFrames/blob/gh-pages/LICENSE 5 | 6 | This is SVGAnimFrames (https://michaelsboost.github.io/SVGAnimFrames/), SVG Frame By Frame Animation 7 | */ 8 | 9 | // call SVG Frame by Frame animation 10 | // SVGAnimFrames("#bounce svg", "> g > g", "repeat", "40", "0"); 11 | 12 | function SVGAnimFrames(elm, tobefound, repeat, frametime, delay) { 13 | var counter = 0; 14 | 15 | // grab animation frames 16 | var detectFrame = parseInt(document.querySelectorAll(elm + ' ' + tobefound).length); 17 | var totalFrames = parseInt(document.querySelectorAll(elm + ' ' + tobefound).length); 18 | 19 | // kill animation 20 | function killAnim() { 21 | counter = 0; 22 | detectFrame = 0; 23 | clearInterval(window.intervalID); 24 | } 25 | 26 | // restart timer 27 | function restartSVGAnim() { 28 | killAnim(); 29 | intervalID = setInterval(animateSVGFrames, frametime); 30 | } 31 | 32 | // SVG Frame by Frame animation 33 | function animateSVGFrames() { 34 | // frame counter 35 | var detectFrame = counter++; 36 | 37 | // remove the vector-effect attribute 38 | for (var i = 0; i < document.querySelectorAll(elm + " > g > g *").length; i++) { 39 | document.querySelectorAll(elm + " *")[i].removeAttribute("vector-effect"); 40 | } 41 | 42 | // only show active frame 43 | for (var i = 0; i < totalFrames; i++) { 44 | if (counter > totalFrames) { 45 | return false; 46 | } 47 | document.querySelectorAll(elm + ' ' + tobefound)[i].style.display = "none"; 48 | document.querySelectorAll(elm + ' ' + tobefound)[detectFrame].style.display = "block"; 49 | } 50 | 51 | // detect end of animation 52 | if (repeat === "no-repeat") { 53 | // if user states no-repeat 54 | if (counter === totalFrames) { 55 | // end of animation 56 | for (var i = 0; i < totalFrames; i++) { 57 | if (counter > totalFrames) { 58 | clearInterval(window.intervalID); 59 | counter = 0; 60 | var detectFrame = totalFrames; 61 | return false; 62 | } 63 | document.querySelectorAll(elm + ' ' + tobefound)[i].style.display = "none"; 64 | document.querySelectorAll(elm + ' ' + tobefound)[detectFrame].style.display = "block"; 65 | } 66 | } 67 | } else { 68 | // if user states repeat or other 69 | if (counter === totalFrames) { 70 | // end of animation 71 | setTimeout(function() { 72 | restartSVGAnim(); 73 | }, delay); 74 | } else if (detectFrame >= totalFrames) { 75 | // restart animation 76 | setTimeout(function() { 77 | restartSVGAnim(); 78 | }, delay); 79 | } 80 | } 81 | } 82 | 83 | // initiate SVG Frame by Frame animation 84 | window.intervalID = setInterval(animateSVGFrames, frametime); 85 | return false; 86 | }; -------------------------------------------------------------------------------- /libraries/alertifyjs/alertify.core.css: -------------------------------------------------------------------------------- 1 | .alertify, 2 | .alertify-show, 3 | .alertify-log { 4 | -webkit-transition: all 500ms cubic-bezier(0.175, 0.885, 0.320, 1.275); 5 | -moz-transition: all 500ms cubic-bezier(0.175, 0.885, 0.320, 1.275); 6 | -ms-transition: all 500ms cubic-bezier(0.175, 0.885, 0.320, 1.275); 7 | -o-transition: all 500ms cubic-bezier(0.175, 0.885, 0.320, 1.275); 8 | transition: all 500ms cubic-bezier(0.175, 0.885, 0.320, 1.275); /* easeOutBack */ 9 | } 10 | .alertify-hide { 11 | -webkit-transition: all 250ms cubic-bezier(0.600, -0.280, 0.735, 0.045); 12 | -moz-transition: all 250ms cubic-bezier(0.600, -0.280, 0.735, 0.045); 13 | -ms-transition: all 250ms cubic-bezier(0.600, -0.280, 0.735, 0.045); 14 | -o-transition: all 250ms cubic-bezier(0.600, -0.280, 0.735, 0.045); 15 | transition: all 250ms cubic-bezier(0.600, -0.280, 0.735, 0.045); /* easeInBack */ 16 | } 17 | .alertify-log-hide { 18 | -webkit-transition: all 500ms cubic-bezier(0.600, -0.280, 0.735, 0.045); 19 | -moz-transition: all 500ms cubic-bezier(0.600, -0.280, 0.735, 0.045); 20 | -ms-transition: all 500ms cubic-bezier(0.600, -0.280, 0.735, 0.045); 21 | -o-transition: all 500ms cubic-bezier(0.600, -0.280, 0.735, 0.045); 22 | transition: all 500ms cubic-bezier(0.600, -0.280, 0.735, 0.045); /* easeInBack */ 23 | } 24 | .alertify-cover { 25 | position: fixed; z-index: 99999; 26 | top: 0; right: 0; bottom: 0; left: 0; 27 | background-color:white; 28 | filter:alpha(opacity=0); 29 | opacity:0; 30 | } 31 | .alertify-cover-hidden { 32 | display: none; 33 | } 34 | .alertify { 35 | position: fixed; z-index: 99999; 36 | top: 50px; left: 50%; 37 | width: 550px; 38 | margin-left: -275px; 39 | opacity: 1; 40 | } 41 | .alertify-hidden { 42 | -webkit-transform: translate(0,-150px); 43 | -moz-transform: translate(0,-150px); 44 | -ms-transform: translate(0,-150px); 45 | -o-transform: translate(0,-150px); 46 | transform: translate(0,-150px); 47 | opacity: 0; 48 | display: none; 49 | } 50 | /* overwrite display: none; for everything except IE6-8 */ 51 | :root *> .alertify-hidden { 52 | display: block; 53 | visibility: hidden; 54 | } 55 | .alertify-logs { 56 | position: fixed; 57 | z-index: 5000; 58 | bottom: 10px; 59 | right: 10px; 60 | width: 300px; 61 | } 62 | .alertify-logs-hidden { 63 | display: none; 64 | } 65 | .alertify-log { 66 | display: block; 67 | margin-top: 10px; 68 | position: relative; 69 | right: -300px; 70 | opacity: 0; 71 | } 72 | .alertify-log-show { 73 | right: 0; 74 | opacity: 1; 75 | } 76 | .alertify-log-hide { 77 | -webkit-transform: translate(300px, 0); 78 | -moz-transform: translate(300px, 0); 79 | -ms-transform: translate(300px, 0); 80 | -o-transform: translate(300px, 0); 81 | transform: translate(300px, 0); 82 | opacity: 0; 83 | } 84 | .alertify-dialog { 85 | padding: 25px; 86 | } 87 | .alertify-resetFocus { 88 | border: 0; 89 | clip: rect(0 0 0 0); 90 | height: 1px; 91 | margin: -1px; 92 | overflow: hidden; 93 | padding: 0; 94 | position: absolute; 95 | width: 1px; 96 | } 97 | .alertify-inner { 98 | text-align: center; 99 | } 100 | .alertify-text { 101 | margin-bottom: 15px; 102 | width: 100%; 103 | -webkit-box-sizing: border-box; 104 | -moz-box-sizing: border-box; 105 | box-sizing: border-box; 106 | font-size: 100%; 107 | } 108 | .alertify-buttons { 109 | } 110 | .alertify-button, 111 | .alertify-button:hover, 112 | .alertify-button:active, 113 | .alertify-button:visited { 114 | background: none; 115 | text-decoration: none; 116 | border: none; 117 | /* line-height and font-size for input button */ 118 | line-height: 1.5; 119 | font-size: 100%; 120 | display: inline-block; 121 | cursor: pointer; 122 | margin-left: 5px; 123 | } 124 | 125 | @media only screen and (max-width: 680px) { 126 | .alertify, 127 | .alertify-logs { 128 | width: 90%; 129 | -webkit-box-sizing: border-box; 130 | -moz-box-sizing: border-box; 131 | box-sizing: border-box; 132 | } 133 | .alertify { 134 | left: 5%; 135 | margin: 0; 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /libraries/alertifyjs/alertify.default.css: -------------------------------------------------------------------------------- 1 | /** 2 | * Default Look and Feel 3 | */ 4 | .alertify, 5 | .alertify-log { 6 | font-family: sans-serif; 7 | } 8 | .alertify { 9 | background: #FFF; 10 | border: 10px solid #333; /* browsers that don't support rgba */ 11 | border: 10px solid rgba(0,0,0,.7); 12 | border-radius: 8px; 13 | box-shadow: 0 3px 3px rgba(0,0,0,.3); 14 | -webkit-background-clip: padding; /* Safari 4? Chrome 6? */ 15 | -moz-background-clip: padding; /* Firefox 3.6 */ 16 | background-clip: padding-box; /* Firefox 4, Safari 5, Opera 10, IE 9 */ 17 | } 18 | .alertify-text { 19 | border: 1px solid #CCC; 20 | padding: 10px; 21 | border-radius: 4px; 22 | } 23 | .alertify-button { 24 | border-radius: 4px; 25 | color: #FFF; 26 | font-weight: bold; 27 | padding: 6px 15px; 28 | text-decoration: none; 29 | text-shadow: 1px 1px 0 rgba(0,0,0,.5); 30 | box-shadow: inset 0 1px 0 0 rgba(255,255,255,.5); 31 | background-image: -webkit-linear-gradient(top, rgba(255,255,255,.3), rgba(255,255,255,0)); 32 | background-image: -moz-linear-gradient(top, rgba(255,255,255,.3), rgba(255,255,255,0)); 33 | background-image: -ms-linear-gradient(top, rgba(255,255,255,.3), rgba(255,255,255,0)); 34 | background-image: -o-linear-gradient(top, rgba(255,255,255,.3), rgba(255,255,255,0)); 35 | background-image: linear-gradient(top, rgba(255,255,255,.3), rgba(255,255,255,0)); 36 | } 37 | .alertify-button:hover, 38 | .alertify-button:focus { 39 | outline: none; 40 | background-image: -webkit-linear-gradient(top, rgba(0,0,0,.1), rgba(0,0,0,0)); 41 | background-image: -moz-linear-gradient(top, rgba(0,0,0,.1), rgba(0,0,0,0)); 42 | background-image: -ms-linear-gradient(top, rgba(0,0,0,.1), rgba(0,0,0,0)); 43 | background-image: -o-linear-gradient(top, rgba(0,0,0,.1), rgba(0,0,0,0)); 44 | background-image: linear-gradient(top, rgba(0,0,0,.1), rgba(0,0,0,0)); 45 | } 46 | .alertify-button:focus { 47 | box-shadow: 0 0 15px #2B72D5; 48 | } 49 | .alertify-button:active { 50 | position: relative; 51 | box-shadow: inset 0 2px 4px rgba(0,0,0,.15), 0 1px 2px rgba(0,0,0,.05); 52 | } 53 | .alertify-button-cancel, 54 | .alertify-button-cancel:hover, 55 | .alertify-button-cancel:focus { 56 | background-color: #FE1A00; 57 | border: 1px solid #D83526; 58 | } 59 | .alertify-button-ok, 60 | .alertify-button-ok:hover, 61 | .alertify-button-ok:focus { 62 | background-color: #5CB811; 63 | border: 1px solid #3B7808; 64 | } 65 | 66 | .alertify-log { 67 | background: #1F1F1F; 68 | background: rgba(0,0,0,.9); 69 | padding: 15px; 70 | border-radius: 4px; 71 | color: #FFF; 72 | text-shadow: -1px -1px 0 rgba(0,0,0,.5); 73 | } 74 | .alertify-log-error { 75 | background: #FE1A00; 76 | background: rgba(254,26,0,.9); 77 | } 78 | .alertify-log-success { 79 | background: #5CB811; 80 | background: rgba(92,184,17,.9); 81 | } -------------------------------------------------------------------------------- /libraries/bowser/bowser.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * Bowser - a browser detector 3 | * https://github.com/ded/bowser 4 | * MIT License | (c) Dustin Diaz 2015 5 | */ 6 | !function(e,t,n){typeof module!="undefined"&&module.exports?module.exports=n():typeof define=="function"&&define.amd?define(t,n):e[t]=n()}(this,"bowser",function(){function t(t){function n(e){var n=t.match(e);return n&&n.length>1&&n[1]||""}function r(e){var n=t.match(e);return n&&n.length>1&&n[2]||""}function C(e){switch(e){case"NT":return"NT";case"XP":return"XP";case"NT 5.0":return"2000";case"NT 5.1":return"XP";case"NT 5.2":return"2003";case"NT 6.0":return"Vista";case"NT 6.1":return"7";case"NT 6.2":return"8";case"NT 6.3":return"8.1";case"NT 10.0":return"10";default:return undefined}}var i=n(/(ipod|iphone|ipad)/i).toLowerCase(),o=/like android/i.test(t),u=!o&&/android/i.test(t),a=/nexus\s*[0-6]\s*/i.test(t),f=!a&&/nexus\s*[0-9]+/i.test(t),l=/CrOS/.test(t),c=/silk/i.test(t),h=/sailfish/i.test(t),p=/tizen/i.test(t),d=/(web|hpw)(o|0)s/i.test(t),v=/windows phone/i.test(t),m=/SamsungBrowser/i.test(t),g=!v&&/windows/i.test(t),y=!i&&!c&&/macintosh/i.test(t),b=!u&&!h&&!p&&!d&&/linux/i.test(t),w=r(/edg([ea]|ios)\/(\d+(\.\d+)?)/i),E=n(/version\/(\d+(\.\d+)?)/i),S=/tablet/i.test(t)&&!/tablet pc/i.test(t),x=!S&&/[^-]mobi/i.test(t),T=/xbox/i.test(t),N;/opera/i.test(t)?N={name:"Opera",opera:e,version:E||n(/(?:opera|opr|opios)[\s\/](\d+(\.\d+)?)/i)}:/opr\/|opios/i.test(t)?N={name:"Opera",opera:e,version:n(/(?:opr|opios)[\s\/](\d+(\.\d+)?)/i)||E}:/SamsungBrowser/i.test(t)?N={name:"Samsung Internet for Android",samsungBrowser:e,version:E||n(/(?:SamsungBrowser)[\s\/](\d+(\.\d+)?)/i)}:/Whale/i.test(t)?N={name:"NAVER Whale browser",whale:e,version:n(/(?:whale)[\s\/](\d+(?:\.\d+)+)/i)}:/MZBrowser/i.test(t)?N={name:"MZ Browser",mzbrowser:e,version:n(/(?:MZBrowser)[\s\/](\d+(?:\.\d+)+)/i)}:/coast/i.test(t)?N={name:"Opera Coast",coast:e,version:E||n(/(?:coast)[\s\/](\d+(\.\d+)?)/i)}:/focus/i.test(t)?N={name:"Focus",focus:e,version:n(/(?:focus)[\s\/](\d+(?:\.\d+)+)/i)}:/yabrowser/i.test(t)?N={name:"Yandex Browser",yandexbrowser:e,version:E||n(/(?:yabrowser)[\s\/](\d+(\.\d+)?)/i)}:/ucbrowser/i.test(t)?N={name:"UC Browser",ucbrowser:e,version:n(/(?:ucbrowser)[\s\/](\d+(?:\.\d+)+)/i)}:/mxios/i.test(t)?N={name:"Maxthon",maxthon:e,version:n(/(?:mxios)[\s\/](\d+(?:\.\d+)+)/i)}:/epiphany/i.test(t)?N={name:"Epiphany",epiphany:e,version:n(/(?:epiphany)[\s\/](\d+(?:\.\d+)+)/i)}:/puffin/i.test(t)?N={name:"Puffin",puffin:e,version:n(/(?:puffin)[\s\/](\d+(?:\.\d+)?)/i)}:/sleipnir/i.test(t)?N={name:"Sleipnir",sleipnir:e,version:n(/(?:sleipnir)[\s\/](\d+(?:\.\d+)+)/i)}:/k-meleon/i.test(t)?N={name:"K-Meleon",kMeleon:e,version:n(/(?:k-meleon)[\s\/](\d+(?:\.\d+)+)/i)}:v?(N={name:"Windows Phone",osname:"Windows Phone",windowsphone:e},w?(N.msedge=e,N.version=w):(N.msie=e,N.version=n(/iemobile\/(\d+(\.\d+)?)/i))):/msie|trident/i.test(t)?N={name:"Internet Explorer",msie:e,version:n(/(?:msie |rv:)(\d+(\.\d+)?)/i)}:l?N={name:"Chrome",osname:"Chrome OS",chromeos:e,chromeBook:e,chrome:e,version:n(/(?:chrome|crios|crmo)\/(\d+(\.\d+)?)/i)}:/edg([ea]|ios)/i.test(t)?N={name:"Microsoft Edge",msedge:e,version:w}:/vivaldi/i.test(t)?N={name:"Vivaldi",vivaldi:e,version:n(/vivaldi\/(\d+(\.\d+)?)/i)||E}:h?N={name:"Sailfish",osname:"Sailfish OS",sailfish:e,version:n(/sailfish\s?browser\/(\d+(\.\d+)?)/i)}:/seamonkey\//i.test(t)?N={name:"SeaMonkey",seamonkey:e,version:n(/seamonkey\/(\d+(\.\d+)?)/i)}:/firefox|iceweasel|fxios/i.test(t)?(N={name:"Firefox",firefox:e,version:n(/(?:firefox|iceweasel|fxios)[ \/](\d+(\.\d+)?)/i)},/\((mobile|tablet);[^\)]*rv:[\d\.]+\)/i.test(t)&&(N.firefoxos=e,N.osname="Firefox OS")):c?N={name:"Amazon Silk",silk:e,version:n(/silk\/(\d+(\.\d+)?)/i)}:/phantom/i.test(t)?N={name:"PhantomJS",phantom:e,version:n(/phantomjs\/(\d+(\.\d+)?)/i)}:/slimerjs/i.test(t)?N={name:"SlimerJS",slimer:e,version:n(/slimerjs\/(\d+(\.\d+)?)/i)}:/blackberry|\bbb\d+/i.test(t)||/rim\stablet/i.test(t)?N={name:"BlackBerry",osname:"BlackBerry OS",blackberry:e,version:E||n(/blackberry[\d]+\/(\d+(\.\d+)?)/i)}:d?(N={name:"WebOS",osname:"WebOS",webos:e,version:E||n(/w(?:eb)?osbrowser\/(\d+(\.\d+)?)/i)},/touchpad\//i.test(t)&&(N.touchpad=e)):/bada/i.test(t)?N={name:"Bada",osname:"Bada",bada:e,version:n(/dolfin\/(\d+(\.\d+)?)/i)}:p?N={name:"Tizen",osname:"Tizen",tizen:e,version:n(/(?:tizen\s?)?browser\/(\d+(\.\d+)?)/i)||E}:/qupzilla/i.test(t)?N={name:"QupZilla",qupzilla:e,version:n(/(?:qupzilla)[\s\/](\d+(?:\.\d+)+)/i)||E}:/chromium/i.test(t)?N={name:"Chromium",chromium:e,version:n(/(?:chromium)[\s\/](\d+(?:\.\d+)?)/i)||E}:/chrome|crios|crmo/i.test(t)?N={name:"Chrome",chrome:e,version:n(/(?:chrome|crios|crmo)\/(\d+(\.\d+)?)/i)}:u?N={name:"Android",version:E}:/safari|applewebkit/i.test(t)?(N={name:"Safari",safari:e},E&&(N.version=E)):i?(N={name:i=="iphone"?"iPhone":i=="ipad"?"iPad":"iPod"},E&&(N.version=E)):/googlebot/i.test(t)?N={name:"Googlebot",googlebot:e,version:n(/googlebot\/(\d+(\.\d+))/i)||E}:N={name:n(/^(.*)\/(.*) /),version:r(/^(.*)\/(.*) /)},!N.msedge&&/(apple)?webkit/i.test(t)?(/(apple)?webkit\/537\.36/i.test(t)?(N.name=N.name||"Blink",N.blink=e):(N.name=N.name||"Webkit",N.webkit=e),!N.version&&E&&(N.version=E)):!N.opera&&/gecko\//i.test(t)&&(N.name=N.name||"Gecko",N.gecko=e,N.version=N.version||n(/gecko\/(\d+(\.\d+)?)/i)),!N.windowsphone&&(u||N.silk)?(N.android=e,N.osname="Android"):!N.windowsphone&&i?(N[i]=e,N.ios=e,N.osname="iOS"):y?(N.mac=e,N.osname="macOS"):T?(N.xbox=e,N.osname="Xbox"):g?(N.windows=e,N.osname="Windows"):b&&(N.linux=e,N.osname="Linux");var k="";N.windows?k=C(n(/Windows ((NT|XP)( \d\d?.\d)?)/i)):N.windowsphone?k=n(/windows phone (?:os)?\s?(\d+(\.\d+)*)/i):N.mac?(k=n(/Mac OS X (\d+([_\.\s]\d+)*)/i),k=k.replace(/[_\s]/g,".")):i?(k=n(/os (\d+([_\s]\d+)*) like mac os x/i),k=k.replace(/[_\s]/g,".")):u?k=n(/android[ \/-](\d+(\.\d+)*)/i):N.webos?k=n(/(?:web|hpw)os\/(\d+(\.\d+)*)/i):N.blackberry?k=n(/rim\stablet\sos\s(\d+(\.\d+)*)/i):N.bada?k=n(/bada\/(\d+(\.\d+)*)/i):N.tizen&&(k=n(/tizen[\/\s](\d+(\.\d+)*)/i)),k&&(N.osversion=k);var L=!N.windows&&k.split(".")[0];if(S||f||i=="ipad"||u&&(L==3||L>=4&&!x)||N.silk)N.tablet=e;else if(x||i=="iphone"||i=="ipod"||u||a||N.blackberry||N.webos||N.bada)N.mobile=e;return N.msedge||N.msie&&N.version>=10||N.yandexbrowser&&N.version>=15||N.vivaldi&&N.version>=1||N.chrome&&N.version>=20||N.samsungBrowser&&N.version>=4||N.whale&&s([N.version,"1.0"])===1||N.mzbrowser&&s([N.version,"6.0"])===1||N.focus&&s([N.version,"1.0"])===1||N.firefox&&N.version>=20||N.safari&&N.version>=6||N.opera&&N.version>=10||N.ios&&N.osversion&&N.osversion.split(".")[0]>=6||N.blackberry&&N.version>=10.1||N.chromium&&N.version>=20?N.a=e:N.msie&&N.version<10||N.chrome&&N.version<20||N.firefox&&N.version<20||N.safari&&N.version<6||N.opera&&N.version<10||N.ios&&N.osversion&&N.osversion.split(".")[0]<6||N.chromium&&N.version<20?N.c=e:N.x=e,N}function r(e){return e.split(".").length}function i(e,t){var n=[],r;if(Array.prototype.map)return Array.prototype.map.call(e,t);for(r=0;r=0){if(n[0][t]>n[1][t])return 1;if(n[0][t]!==n[1][t])return-1;if(t===0)return 0}}function o(e,r,i){var o=n;typeof r=="string"&&(i=r,r=void 0),r===void 0&&(r=!1),i&&(o=t(i));var u=""+o.version;for(var a in e)if(e.hasOwnProperty(a)&&o[a]){if(typeof e[a]!="string")throw new Error("Browser version in the minVersion map should be a string: "+a+": "+String(e));return s([u,e[a]])<0}return r}function u(e,t,n){return!o(e,t,n)}var e=!0,n=t(typeof navigator!="undefined"?navigator.userAgent||"":"");return n.test=function(e){for(var t=0;t 1) { 85 | if (this.needsFullRender()) { 86 | // redraw curve 87 | // clear top canvas 88 | this.canvas.clearContext(this.canvas.contextTop); 89 | this._render(); 90 | } 91 | else { 92 | var points = this._points, length = points.length, ctx = this.canvas.contextTop; 93 | // draw the curve update 94 | this._saveAndTransform(ctx); 95 | if (this.oldEnd) { 96 | ctx.beginPath(); 97 | ctx.moveTo(this.oldEnd.x, this.oldEnd.y); 98 | } 99 | this.oldEnd = this._drawSegment(ctx, points[length - 2], points[length - 1], true); 100 | ctx.stroke(); 101 | ctx.fill(); 102 | ctx.restore(); 103 | } 104 | } 105 | }, 106 | 107 | /** 108 | * Invoked on mouse up 109 | */ 110 | onMouseUp: function(options) { 111 | if (!this.canvas._isMainEvent(options.e)) { 112 | return true; 113 | } 114 | this.drawStraightLine = false; 115 | this.oldEnd = undefined; 116 | this._finalizeAndAddPath(); 117 | return false; 118 | }, 119 | 120 | /** 121 | * @private 122 | * @param {Object} pointer Actual mouse position related to the canvas. 123 | */ 124 | _prepareForDrawing: function(pointer) { 125 | 126 | var p = new fabric.Point(pointer.x, pointer.y); 127 | 128 | this._reset(); 129 | this._addPoint(p); 130 | this.canvas.contextTop.moveTo(p.x, p.y); 131 | }, 132 | 133 | /** 134 | * @private 135 | * @param {fabric.Point} point Point to be added to points array 136 | */ 137 | _addPoint: function(point) { 138 | if (this._points.length > 1 && point.eq(this._points[this._points.length - 1])) { 139 | return false; 140 | } 141 | if (this.drawStraightLine && this._points.length > 1) { 142 | this._hasStraightLine = true; 143 | this._points.pop(); 144 | } 145 | this._points.push(point); 146 | return true; 147 | }, 148 | 149 | /** 150 | * Clear points array and set contextTop canvas style. 151 | * @private 152 | */ 153 | _reset: function() { 154 | this._points = []; 155 | this._setBrushStyles(); 156 | this._setShadow(); 157 | this._hasStraightLine = false; 158 | }, 159 | 160 | /** 161 | * @private 162 | * @param {Object} pointer Actual mouse position related to the canvas. 163 | */ 164 | _captureDrawingPath: function(pointer) { 165 | var pointerPoint = new fabric.Point(pointer.x, pointer.y); 166 | return this._addPoint(pointerPoint); 167 | }, 168 | 169 | /** 170 | * Draw a smooth path on the topCanvas using quadraticCurveTo 171 | * @private 172 | */ 173 | _render: function() { 174 | var ctx = this.canvas.contextTop, i, len, 175 | p1 = this._points[0], 176 | p2 = this._points[1]; 177 | 178 | this._saveAndTransform(ctx); 179 | ctx.beginPath(); 180 | //if we only have 2 points in the path and they are the same 181 | //it means that the user only clicked the canvas without moving the mouse 182 | //then we should be drawing a dot. A path isn't drawn between two identical dots 183 | //that's why we set them apart a bit 184 | if (this._points.length === 2 && p1.x === p2.x && p1.y === p2.y) { 185 | var width = this.width / 1000; 186 | p1 = new fabric.Point(p1.x, p1.y); 187 | p2 = new fabric.Point(p2.x, p2.y); 188 | p1.x -= width; 189 | p2.x += width; 190 | } 191 | ctx.moveTo(p1.x, p1.y); 192 | 193 | for (i = 1, len = this._points.length; i < len; i++) { 194 | // we pick the point between pi + 1 & pi + 2 as the 195 | // end point and p1 as our control point. 196 | this._drawSegment(ctx, p1, p2); 197 | p1 = this._points[i]; 198 | p2 = this._points[i + 1]; 199 | } 200 | // Draw last line as a straight line while 201 | // we wait for the next point to be able to calculate 202 | // the bezier control point 203 | ctx.lineTo(p1.x, p1.y); 204 | ctx.stroke(); 205 | ctx.fill(); 206 | ctx.restore(); 207 | }, 208 | 209 | /** 210 | * Converts points to SVG path 211 | * @param {Array} points Array of points 212 | * @return {(string|number)[][]} SVG path commands 213 | */ 214 | convertPointsToSVGPath: function (points) { 215 | var correction = this.width / 1000; 216 | return fabric.util.getSmoothPathFromPoints(points, correction); 217 | }, 218 | 219 | /** 220 | * @private 221 | * @param {(string|number)[][]} pathData SVG path commands 222 | * @returns {boolean} 223 | */ 224 | _isEmptySVGPath: function (pathData) { 225 | var pathString = fabric.util.joinPath(pathData); 226 | return pathString === 'M 0 0 Q 0 0 0 0 L 0 0'; 227 | }, 228 | 229 | /** 230 | * Creates fabric.Path object to add on canvas 231 | * @param {(string|number)[][]} pathData Path data 232 | * @return {fabric.Path} Path to add on canvas 233 | */ 234 | createPath: function(pathData) { 235 | var path = new fabric.Path(pathData, { 236 | fill: this.color, 237 | stroke: null, 238 | strokeWidth: 1, 239 | strokeLineCap: this.strokeLineCap, 240 | strokeMiterLimit: this.strokeMiterLimit, 241 | strokeLineJoin: this.strokeLineJoin, 242 | strokeDashArray: this.strokeDashArray, 243 | }); 244 | if (this.shadow) { 245 | this.shadow.affectStroke = true; 246 | path.shadow = new fabric.Shadow(this.shadow); 247 | } 248 | 249 | return path; 250 | }, 251 | 252 | /** 253 | * Decimate points array with the decimate value 254 | */ 255 | decimatePoints: function(points, distance) { 256 | if (points.length <= 2) { 257 | return points; 258 | } 259 | var zoom = this.canvas.getZoom(), adjustedDistance = Math.pow(distance / zoom, 2), 260 | i, l = points.length - 1, lastPoint = points[0], newPoints = [lastPoint], 261 | cDistance; 262 | for (i = 1; i < l - 1; i++) { 263 | cDistance = Math.pow(lastPoint.x - points[i].x, 2) + Math.pow(lastPoint.y - points[i].y, 2); 264 | if (cDistance >= adjustedDistance) { 265 | lastPoint = points[i]; 266 | newPoints.push(lastPoint); 267 | } 268 | } 269 | /** 270 | * Add the last point from the original line to the end of the array. 271 | * This ensures decimate doesn't delete the last point on the line, and ensures the line is > 1 point. 272 | */ 273 | newPoints.push(points[l]); 274 | return newPoints; 275 | }, 276 | 277 | /** 278 | * On mouseup after drawing the path on contextTop canvas 279 | * we use the points captured to create an new fabric path object 280 | * and add it to the fabric canvas. 281 | */ 282 | _finalizeAndAddPath: function() { 283 | var ctx = this.canvas.contextTop; 284 | ctx.closePath(); 285 | if (this.decimate) { 286 | this._points = this.decimatePoints(this._points, this.decimate); 287 | } 288 | var pathData = this.convertPointsToSVGPath(this._points); 289 | if (this._isEmptySVGPath(pathData)) { 290 | // do not create 0 width/height paths, as they are 291 | // rendered inconsistently across browsers 292 | // Firefox 4, for example, renders a dot, 293 | // whereas Chrome 10 renders nothing 294 | this.canvas.requestRenderAll(); 295 | return; 296 | } 297 | 298 | var path = this.createPath(pathData); 299 | this.canvas.clearContext(this.canvas.contextTop); 300 | this.canvas.fire('before:path:created', { path: path }); 301 | this.canvas.add(path); 302 | this.canvas.requestRenderAll(); 303 | path.setCoords(); 304 | this._resetShadow(); 305 | 306 | 307 | // fire event 'path' created 308 | this.canvas.fire('path:created', { path: path }); 309 | } 310 | }); 311 | })(); -------------------------------------------------------------------------------- /libraries/jquery/jquery-migrate-1.2.1.min.js: -------------------------------------------------------------------------------- 1 | /*! jQuery Migrate v1.2.1 | (c) 2005, 2013 jQuery Foundation, Inc. and other contributors | jquery.org/license */ 2 | jQuery.migrateMute===void 0&&(jQuery.migrateMute=!0),function(e,t,n){function r(n){var r=t.console;i[n]||(i[n]=!0,e.migrateWarnings.push(n),r&&r.warn&&!e.migrateMute&&(r.warn("JQMIGRATE: "+n),e.migrateTrace&&r.trace&&r.trace()))}function a(t,a,i,o){if(Object.defineProperty)try{return Object.defineProperty(t,a,{configurable:!0,enumerable:!0,get:function(){return r(o),i},set:function(e){r(o),i=e}}),n}catch(s){}e._definePropertyBroken=!0,t[a]=i}var i={};e.migrateWarnings=[],!e.migrateMute&&t.console&&t.console.log&&t.console.log("JQMIGRATE: Logging is active"),e.migrateTrace===n&&(e.migrateTrace=!0),e.migrateReset=function(){i={},e.migrateWarnings.length=0},"BackCompat"===document.compatMode&&r("jQuery is not compatible with Quirks Mode");var o=e("",{size:1}).attr("size")&&e.attrFn,s=e.attr,u=e.attrHooks.value&&e.attrHooks.value.get||function(){return null},c=e.attrHooks.value&&e.attrHooks.value.set||function(){return n},l=/^(?:input|button)$/i,d=/^[238]$/,p=/^(?:autofocus|autoplay|async|checked|controls|defer|disabled|hidden|loop|multiple|open|readonly|required|scoped|selected)$/i,f=/^(?:checked|selected)$/i;a(e,"attrFn",o||{},"jQuery.attrFn is deprecated"),e.attr=function(t,a,i,u){var c=a.toLowerCase(),g=t&&t.nodeType;return u&&(4>s.length&&r("jQuery.fn.attr( props, pass ) is deprecated"),t&&!d.test(g)&&(o?a in o:e.isFunction(e.fn[a])))?e(t)[a](i):("type"===a&&i!==n&&l.test(t.nodeName)&&t.parentNode&&r("Can't change the 'type' of an input or button in IE 6/7/8"),!e.attrHooks[c]&&p.test(c)&&(e.attrHooks[c]={get:function(t,r){var a,i=e.prop(t,r);return i===!0||"boolean"!=typeof i&&(a=t.getAttributeNode(r))&&a.nodeValue!==!1?r.toLowerCase():n},set:function(t,n,r){var a;return n===!1?e.removeAttr(t,r):(a=e.propFix[r]||r,a in t&&(t[a]=!0),t.setAttribute(r,r.toLowerCase())),r}},f.test(c)&&r("jQuery.fn.attr('"+c+"') may use property instead of attribute")),s.call(e,t,a,i))},e.attrHooks.value={get:function(e,t){var n=(e.nodeName||"").toLowerCase();return"button"===n?u.apply(this,arguments):("input"!==n&&"option"!==n&&r("jQuery.fn.attr('value') no longer gets properties"),t in e?e.value:null)},set:function(e,t){var a=(e.nodeName||"").toLowerCase();return"button"===a?c.apply(this,arguments):("input"!==a&&"option"!==a&&r("jQuery.fn.attr('value', val) no longer sets properties"),e.value=t,n)}};var g,h,v=e.fn.init,m=e.parseJSON,y=/^([^<]*)(<[\w\W]+>)([^>]*)$/;e.fn.init=function(t,n,a){var i;return t&&"string"==typeof t&&!e.isPlainObject(n)&&(i=y.exec(e.trim(t)))&&i[0]&&("<"!==t.charAt(0)&&r("$(html) HTML strings must start with '<' character"),i[3]&&r("$(html) HTML text after last tag is ignored"),"#"===i[0].charAt(0)&&(r("HTML string cannot start with a '#' character"),e.error("JQMIGRATE: Invalid selector string (XSS)")),n&&n.context&&(n=n.context),e.parseHTML)?v.call(this,e.parseHTML(i[2],n,!0),n,a):v.apply(this,arguments)},e.fn.init.prototype=e.fn,e.parseJSON=function(e){return e||null===e?m.apply(this,arguments):(r("jQuery.parseJSON requires a valid JSON string"),null)},e.uaMatch=function(e){e=e.toLowerCase();var t=/(chrome)[ \/]([\w.]+)/.exec(e)||/(webkit)[ \/]([\w.]+)/.exec(e)||/(opera)(?:.*version|)[ \/]([\w.]+)/.exec(e)||/(msie) ([\w.]+)/.exec(e)||0>e.indexOf("compatible")&&/(mozilla)(?:.*? rv:([\w.]+)|)/.exec(e)||[];return{browser:t[1]||"",version:t[2]||"0"}},e.browser||(g=e.uaMatch(navigator.userAgent),h={},g.browser&&(h[g.browser]=!0,h.version=g.version),h.chrome?h.webkit=!0:h.webkit&&(h.safari=!0),e.browser=h),a(e,"browser",e.browser,"jQuery.browser is deprecated"),e.sub=function(){function t(e,n){return new t.fn.init(e,n)}e.extend(!0,t,this),t.superclass=this,t.fn=t.prototype=this(),t.fn.constructor=t,t.sub=this.sub,t.fn.init=function(r,a){return a&&a instanceof e&&!(a instanceof t)&&(a=t(a)),e.fn.init.call(this,r,a,n)},t.fn.init.prototype=t.fn;var n=t(document);return r("jQuery.sub() is deprecated"),t},e.ajaxSetup({converters:{"text json":e.parseJSON}});var b=e.fn.data;e.fn.data=function(t){var a,i,o=this[0];return!o||"events"!==t||1!==arguments.length||(a=e.data(o,t),i=e._data(o,t),a!==n&&a!==i||i===n)?b.apply(this,arguments):(r("Use of jQuery.fn.data('events') is deprecated"),i)};var j=/\/(java|ecma)script/i,w=e.fn.andSelf||e.fn.addBack;e.fn.andSelf=function(){return r("jQuery.fn.andSelf() replaced by jQuery.fn.addBack()"),w.apply(this,arguments)},e.clean||(e.clean=function(t,a,i,o){a=a||document,a=!a.nodeType&&a[0]||a,a=a.ownerDocument||a,r("jQuery.clean() is deprecated");var s,u,c,l,d=[];if(e.merge(d,e.buildFragment(t,a).childNodes),i)for(c=function(e){return!e.type||j.test(e.type)?o?o.push(e.parentNode?e.parentNode.removeChild(e):e):i.appendChild(e):n},s=0;null!=(u=d[s]);s++)e.nodeName(u,"script")&&c(u)||(i.appendChild(u),u.getElementsByTagName!==n&&(l=e.grep(e.merge([],u.getElementsByTagName("script")),c),d.splice.apply(d,[s+1,0].concat(l)),s+=l.length));return d});var Q=e.event.add,x=e.event.remove,k=e.event.trigger,N=e.fn.toggle,T=e.fn.live,M=e.fn.die,S="ajaxStart|ajaxStop|ajaxSend|ajaxComplete|ajaxError|ajaxSuccess",C=RegExp("\\b(?:"+S+")\\b"),H=/(?:^|\s)hover(\.\S+|)\b/,A=function(t){return"string"!=typeof t||e.event.special.hover?t:(H.test(t)&&r("'hover' pseudo-event is deprecated, use 'mouseenter mouseleave'"),t&&t.replace(H,"mouseenter$1 mouseleave$1"))};e.event.props&&"attrChange"!==e.event.props[0]&&e.event.props.unshift("attrChange","attrName","relatedNode","srcElement"),e.event.dispatch&&a(e.event,"handle",e.event.dispatch,"jQuery.event.handle is undocumented and deprecated"),e.event.add=function(e,t,n,a,i){e!==document&&C.test(t)&&r("AJAX events should be attached to document: "+t),Q.call(this,e,A(t||""),n,a,i)},e.event.remove=function(e,t,n,r,a){x.call(this,e,A(t)||"",n,r,a)},e.fn.error=function(){var e=Array.prototype.slice.call(arguments,0);return r("jQuery.fn.error() is deprecated"),e.splice(0,0,"error"),arguments.length?this.bind.apply(this,e):(this.triggerHandler.apply(this,e),this)},e.fn.toggle=function(t,n){if(!e.isFunction(t)||!e.isFunction(n))return N.apply(this,arguments);r("jQuery.fn.toggle(handler, handler...) is deprecated");var a=arguments,i=t.guid||e.guid++,o=0,s=function(n){var r=(e._data(this,"lastToggle"+t.guid)||0)%o;return e._data(this,"lastToggle"+t.guid,r+1),n.preventDefault(),a[r].apply(this,arguments)||!1};for(s.guid=i;a.length>o;)a[o++].guid=i;return this.click(s)},e.fn.live=function(t,n,a){return r("jQuery.fn.live() is deprecated"),T?T.apply(this,arguments):(e(this.context).on(t,this.selector,n,a),this)},e.fn.die=function(t,n){return r("jQuery.fn.die() is deprecated"),M?M.apply(this,arguments):(e(this.context).off(t,this.selector||"**",n),this)},e.event.trigger=function(e,t,n,a){return n||C.test(e)||r("Global events are undocumented and deprecated"),k.call(this,e,t,n||document,a)},e.each(S.split("|"),function(t,n){e.event.special[n]={setup:function(){var t=this;return t!==document&&(e.event.add(document,n+"."+e.guid,function(){e.event.trigger(n,null,t,!0)}),e._data(this,n,e.guid++)),!1},teardown:function(){return this!==document&&e.event.remove(document,n+"."+e._data(this,n)),!1}}})}(jQuery,window); -------------------------------------------------------------------------------- /libraries/jszip/Blob.js: -------------------------------------------------------------------------------- 1 | /* Blob.js 2 | * A Blob implementation. 3 | * 2014-07-24 4 | * 5 | * By Eli Grey, http://eligrey.com 6 | * By Devin Samarin, https://github.com/dsamarin 7 | * License: X11/MIT 8 | * See https://github.com/eligrey/Blob.js/blob/master/LICENSE.md 9 | */ 10 | 11 | /*global self, unescape */ 12 | /*jslint bitwise: true, regexp: true, confusion: true, es5: true, vars: true, white: true, 13 | plusplus: true */ 14 | 15 | /*! @source http://purl.eligrey.com/github/Blob.js/blob/master/Blob.js */ 16 | 17 | (function (view) { 18 | "use strict"; 19 | 20 | view.URL = view.URL || view.webkitURL; 21 | 22 | if (view.Blob && view.URL) { 23 | try { 24 | new Blob; 25 | return; 26 | } catch (e) {} 27 | } 28 | 29 | // Internally we use a BlobBuilder implementation to base Blob off of 30 | // in order to support older browsers that only have BlobBuilder 31 | var BlobBuilder = view.BlobBuilder || view.WebKitBlobBuilder || view.MozBlobBuilder || (function(view) { 32 | var 33 | get_class = function(object) { 34 | return Object.prototype.toString.call(object).match(/^\[object\s(.*)\]$/)[1]; 35 | } 36 | , FakeBlobBuilder = function BlobBuilder() { 37 | this.data = []; 38 | } 39 | , FakeBlob = function Blob(data, type, encoding) { 40 | this.data = data; 41 | this.size = data.length; 42 | this.type = type; 43 | this.encoding = encoding; 44 | } 45 | , FBB_proto = FakeBlobBuilder.prototype 46 | , FB_proto = FakeBlob.prototype 47 | , FileReaderSync = view.FileReaderSync 48 | , FileException = function(type) { 49 | this.code = this[this.name = type]; 50 | } 51 | , file_ex_codes = ( 52 | "NOT_FOUND_ERR SECURITY_ERR ABORT_ERR NOT_READABLE_ERR ENCODING_ERR " 53 | + "NO_MODIFICATION_ALLOWED_ERR INVALID_STATE_ERR SYNTAX_ERR" 54 | ).split(" ") 55 | , file_ex_code = file_ex_codes.length 56 | , real_URL = view.URL || view.webkitURL || view 57 | , real_create_object_URL = real_URL.createObjectURL 58 | , real_revoke_object_URL = real_URL.revokeObjectURL 59 | , URL = real_URL 60 | , btoa = view.btoa 61 | , atob = view.atob 62 | 63 | , ArrayBuffer = view.ArrayBuffer 64 | , Uint8Array = view.Uint8Array 65 | 66 | , origin = /^[\w-]+:\/*\[?[\w\.:-]+\]?(?::[0-9]+)?/ 67 | ; 68 | FakeBlob.fake = FB_proto.fake = true; 69 | while (file_ex_code--) { 70 | FileException.prototype[file_ex_codes[file_ex_code]] = file_ex_code + 1; 71 | } 72 | // Polyfill URL 73 | if (!real_URL.createObjectURL) { 74 | URL = view.URL = function(uri) { 75 | var 76 | uri_info = document.createElementNS("http://www.w3.org/1999/xhtml", "a") 77 | , uri_origin 78 | ; 79 | uri_info.href = uri; 80 | if (!("origin" in uri_info)) { 81 | if (uri_info.protocol.toLowerCase() === "data:") { 82 | uri_info.origin = null; 83 | } else { 84 | uri_origin = uri.match(origin); 85 | uri_info.origin = uri_origin && uri_origin[1]; 86 | } 87 | } 88 | return uri_info; 89 | }; 90 | } 91 | URL.createObjectURL = function(blob) { 92 | var 93 | type = blob.type 94 | , data_URI_header 95 | ; 96 | if (type === null) { 97 | type = "application/octet-stream"; 98 | } 99 | if (blob instanceof FakeBlob) { 100 | data_URI_header = "data:" + type; 101 | if (blob.encoding === "base64") { 102 | return data_URI_header + ";base64," + blob.data; 103 | } else if (blob.encoding === "URI") { 104 | return data_URI_header + "," + decodeURIComponent(blob.data); 105 | } if (btoa) { 106 | return data_URI_header + ";base64," + btoa(blob.data); 107 | } else { 108 | return data_URI_header + "," + encodeURIComponent(blob.data); 109 | } 110 | } else if (real_create_object_URL) { 111 | return real_create_object_URL.call(real_URL, blob); 112 | } 113 | }; 114 | URL.revokeObjectURL = function(object_URL) { 115 | if (object_URL.substring(0, 5) !== "data:" && real_revoke_object_URL) { 116 | real_revoke_object_URL.call(real_URL, object_URL); 117 | } 118 | }; 119 | FBB_proto.append = function(data/*, endings*/) { 120 | var bb = this.data; 121 | // decode data to a binary string 122 | if (Uint8Array && (data instanceof ArrayBuffer || data instanceof Uint8Array)) { 123 | var 124 | str = "" 125 | , buf = new Uint8Array(data) 126 | , i = 0 127 | , buf_len = buf.length 128 | ; 129 | for (; i < buf_len; i++) { 130 | str += String.fromCharCode(buf[i]); 131 | } 132 | bb.push(str); 133 | } else if (get_class(data) === "Blob" || get_class(data) === "File") { 134 | if (FileReaderSync) { 135 | var fr = new FileReaderSync; 136 | bb.push(fr.readAsBinaryString(data)); 137 | } else { 138 | // async FileReader won't work as BlobBuilder is sync 139 | throw new FileException("NOT_READABLE_ERR"); 140 | } 141 | } else if (data instanceof FakeBlob) { 142 | if (data.encoding === "base64" && atob) { 143 | bb.push(atob(data.data)); 144 | } else if (data.encoding === "URI") { 145 | bb.push(decodeURIComponent(data.data)); 146 | } else if (data.encoding === "raw") { 147 | bb.push(data.data); 148 | } 149 | } else { 150 | if (typeof data !== "string") { 151 | data += ""; // convert unsupported types to strings 152 | } 153 | // decode UTF-16 to binary string 154 | bb.push(unescape(encodeURIComponent(data))); 155 | } 156 | }; 157 | FBB_proto.getBlob = function(type) { 158 | if (!arguments.length) { 159 | type = null; 160 | } 161 | return new FakeBlob(this.data.join(""), type, "raw"); 162 | }; 163 | FBB_proto.toString = function() { 164 | return "[object BlobBuilder]"; 165 | }; 166 | FB_proto.slice = function(start, end, type) { 167 | var args = arguments.length; 168 | if (args < 3) { 169 | type = null; 170 | } 171 | return new FakeBlob( 172 | this.data.slice(start, args > 1 ? end : this.data.length) 173 | , type 174 | , this.encoding 175 | ); 176 | }; 177 | FB_proto.toString = function() { 178 | return "[object Blob]"; 179 | }; 180 | FB_proto.close = function() { 181 | this.size = 0; 182 | delete this.data; 183 | }; 184 | return FakeBlobBuilder; 185 | }(view)); 186 | 187 | view.Blob = function(blobParts, options) { 188 | var type = options ? (options.type || "") : ""; 189 | var builder = new BlobBuilder(); 190 | if (blobParts) { 191 | for (var i = 0, len = blobParts.length; i < len; i++) { 192 | if (Uint8Array && blobParts[i] instanceof Uint8Array) { 193 | builder.append(blobParts[i].buffer); 194 | } 195 | else { 196 | builder.append(blobParts[i]); 197 | } 198 | } 199 | } 200 | var blob = builder.getBlob(type); 201 | if (!blob.slice && blob.webkitSlice) { 202 | blob.slice = blob.webkitSlice; 203 | } 204 | return blob; 205 | }; 206 | 207 | var getPrototypeOf = Object.getPrototypeOf || function(object) { 208 | return object.__proto__; 209 | }; 210 | view.Blob.prototype = getPrototypeOf(new view.Blob()); 211 | }(typeof self !== "undefined" && self || typeof window !== "undefined" && window || this.content || this)); 212 | -------------------------------------------------------------------------------- /libraries/jszip/FileSaver.js: -------------------------------------------------------------------------------- 1 | /*! FileSaver.js 2 | * A saveAs() FileSaver implementation. 3 | * 2014-01-24 4 | * 5 | * By Eli Grey, http://eligrey.com 6 | * License: X11/MIT 7 | * See LICENSE.md 8 | */ 9 | 10 | /*global self */ 11 | /*jslint bitwise: true, indent: 4, laxbreak: true, laxcomma: true, smarttabs: true, plusplus: true */ 12 | 13 | /*! @source http://purl.eligrey.com/github/FileSaver.js/blob/master/FileSaver.js */ 14 | 15 | var saveAs = saveAs 16 | // IE 10+ (native saveAs) 17 | || (typeof navigator !== "undefined" && 18 | navigator.msSaveOrOpenBlob && navigator.msSaveOrOpenBlob.bind(navigator)) 19 | // Everyone else 20 | || (function(view) { 21 | "use strict"; 22 | // IE <10 is explicitly unsupported 23 | if (typeof navigator !== "undefined" && 24 | /MSIE [1-9]\./.test(navigator.userAgent)) { 25 | return; 26 | } 27 | var 28 | doc = view.document 29 | // only get URL when necessary in case BlobBuilder.js hasn't overridden it yet 30 | , get_URL = function() { 31 | return view.URL || view.webkitURL || view; 32 | } 33 | , URL = view.URL || view.webkitURL || view 34 | , save_link = doc.createElementNS("http://www.w3.org/1999/xhtml", "a") 35 | , can_use_save_link = !view.externalHost && "download" in save_link 36 | , click = function(node) { 37 | var event = doc.createEvent("MouseEvents"); 38 | event.initMouseEvent( 39 | "click", true, false, view, 0, 0, 0, 0, 0 40 | , false, false, false, false, 0, null 41 | ); 42 | node.dispatchEvent(event); 43 | } 44 | , webkit_req_fs = view.webkitRequestFileSystem 45 | , req_fs = view.requestFileSystem || webkit_req_fs || view.mozRequestFileSystem 46 | , throw_outside = function(ex) { 47 | (view.setImmediate || view.setTimeout)(function() { 48 | throw ex; 49 | }, 0); 50 | } 51 | , force_saveable_type = "application/octet-stream" 52 | , fs_min_size = 0 53 | , deletion_queue = [] 54 | , process_deletion_queue = function() { 55 | var i = deletion_queue.length; 56 | while (i--) { 57 | var file = deletion_queue[i]; 58 | if (typeof file === "string") { // file is an object URL 59 | URL.revokeObjectURL(file); 60 | } else { // file is a File 61 | file.remove(); 62 | } 63 | } 64 | deletion_queue.length = 0; // clear queue 65 | } 66 | , dispatch = function(filesaver, event_types, event) { 67 | event_types = [].concat(event_types); 68 | var i = event_types.length; 69 | while (i--) { 70 | var listener = filesaver["on" + event_types[i]]; 71 | if (typeof listener === "function") { 72 | try { 73 | listener.call(filesaver, event || filesaver); 74 | } catch (ex) { 75 | throw_outside(ex); 76 | } 77 | } 78 | } 79 | } 80 | , FileSaver = function(blob, name) { 81 | // First try a.download, then web filesystem, then object URLs 82 | var 83 | filesaver = this 84 | , type = blob.type 85 | , blob_changed = false 86 | , object_url 87 | , target_view 88 | , get_object_url = function() { 89 | var object_url = get_URL().createObjectURL(blob); 90 | deletion_queue.push(object_url); 91 | return object_url; 92 | } 93 | , dispatch_all = function() { 94 | dispatch(filesaver, "writestart progress write writeend".split(" ")); 95 | } 96 | // on any filesys errors revert to saving with object URLs 97 | , fs_error = function() { 98 | // don't create more object URLs than needed 99 | if (blob_changed || !object_url) { 100 | object_url = get_object_url(blob); 101 | } 102 | if (target_view) { 103 | target_view.location.href = object_url; 104 | } else { 105 | window.open(object_url, "_blank"); 106 | } 107 | filesaver.readyState = filesaver.DONE; 108 | dispatch_all(); 109 | } 110 | , abortable = function(func) { 111 | return function() { 112 | if (filesaver.readyState !== filesaver.DONE) { 113 | return func.apply(this, arguments); 114 | } 115 | }; 116 | } 117 | , create_if_not_found = {create: true, exclusive: false} 118 | , slice 119 | ; 120 | filesaver.readyState = filesaver.INIT; 121 | if (!name) { 122 | name = "download"; 123 | } 124 | if (can_use_save_link) { 125 | object_url = get_object_url(blob); 126 | // FF for Android has a nasty garbage collection mechanism 127 | // that turns all objects that are not pure javascript into 'deadObject' 128 | // this means `doc` and `save_link` are unusable and need to be recreated 129 | // `view` is usable though: 130 | doc = view.document; 131 | save_link = doc.createElementNS("http://www.w3.org/1999/xhtml", "a"); 132 | save_link.href = object_url; 133 | save_link.download = name; 134 | var event = doc.createEvent("MouseEvents"); 135 | event.initMouseEvent( 136 | "click", true, false, view, 0, 0, 0, 0, 0 137 | , false, false, false, false, 0, null 138 | ); 139 | save_link.dispatchEvent(event); 140 | filesaver.readyState = filesaver.DONE; 141 | dispatch_all(); 142 | return; 143 | } 144 | // Object and web filesystem URLs have a problem saving in Google Chrome when 145 | // viewed in a tab, so I force save with application/octet-stream 146 | // http://code.google.com/p/chromium/issues/detail?id=91158 147 | if (view.chrome && type && type !== force_saveable_type) { 148 | slice = blob.slice || blob.webkitSlice; 149 | blob = slice.call(blob, 0, blob.size, force_saveable_type); 150 | blob_changed = true; 151 | } 152 | // Since I can't be sure that the guessed media type will trigger a download 153 | // in WebKit, I append .download to the filename. 154 | // https://bugs.webkit.org/show_bug.cgi?id=65440 155 | if (webkit_req_fs && name !== "download") { 156 | name += ".download"; 157 | } 158 | if (type === force_saveable_type || webkit_req_fs) { 159 | target_view = view; 160 | } 161 | if (!req_fs) { 162 | fs_error(); 163 | return; 164 | } 165 | fs_min_size += blob.size; 166 | req_fs(view.TEMPORARY, fs_min_size, abortable(function(fs) { 167 | fs.root.getDirectory("saved", create_if_not_found, abortable(function(dir) { 168 | var save = function() { 169 | dir.getFile(name, create_if_not_found, abortable(function(file) { 170 | file.createWriter(abortable(function(writer) { 171 | writer.onwriteend = function(event) { 172 | target_view.location.href = file.toURL(); 173 | deletion_queue.push(file); 174 | filesaver.readyState = filesaver.DONE; 175 | dispatch(filesaver, "writeend", event); 176 | }; 177 | writer.onerror = function() { 178 | var error = writer.error; 179 | if (error.code !== error.ABORT_ERR) { 180 | fs_error(); 181 | } 182 | }; 183 | "writestart progress write abort".split(" ").forEach(function(event) { 184 | writer["on" + event] = filesaver["on" + event]; 185 | }); 186 | writer.write(blob); 187 | filesaver.abort = function() { 188 | writer.abort(); 189 | filesaver.readyState = filesaver.DONE; 190 | }; 191 | filesaver.readyState = filesaver.WRITING; 192 | }), fs_error); 193 | }), fs_error); 194 | }; 195 | dir.getFile(name, {create: false}, abortable(function(file) { 196 | // delete file if it already exists 197 | file.remove(); 198 | save(); 199 | }), abortable(function(ex) { 200 | if (ex.code === ex.NOT_FOUND_ERR) { 201 | save(); 202 | } else { 203 | fs_error(); 204 | } 205 | })); 206 | }), fs_error); 207 | }), fs_error); 208 | } 209 | , FS_proto = FileSaver.prototype 210 | , saveAs = function(blob, name) { 211 | return new FileSaver(blob, name); 212 | } 213 | ; 214 | FS_proto.abort = function() { 215 | var filesaver = this; 216 | filesaver.readyState = filesaver.DONE; 217 | dispatch(filesaver, "abort"); 218 | }; 219 | FS_proto.readyState = FS_proto.INIT = 0; 220 | FS_proto.WRITING = 1; 221 | FS_proto.DONE = 2; 222 | 223 | FS_proto.error = 224 | FS_proto.onwritestart = 225 | FS_proto.onprogress = 226 | FS_proto.onwrite = 227 | FS_proto.onabort = 228 | FS_proto.onerror = 229 | FS_proto.onwriteend = 230 | null; 231 | 232 | view.addEventListener("unload", process_deletion_queue, false); 233 | saveAs.unload = function() { 234 | process_deletion_queue(); 235 | view.removeEventListener("unload", process_deletion_queue, false); 236 | }; 237 | return saveAs; 238 | }( 239 | typeof self !== "undefined" && self 240 | || typeof window !== "undefined" && window 241 | || this.content 242 | )); 243 | // `self` is undefined in Firefox for Android content script context 244 | // while `this` is nsIContentFrameMessageManager 245 | // with an attribute `content` that corresponds to the window 246 | 247 | if (typeof module !== "undefined") module.exports = saveAs; 248 | -------------------------------------------------------------------------------- /libraries/jszip/jszip-utils.js: -------------------------------------------------------------------------------- 1 | /*! 2 | 3 | JSZipUtils - A collection of cross-browser utilities to go along with JSZip. 4 | 5 | 6 | (c) 2014 Stuart Knightley, David Duponchel 7 | Dual licenced under the MIT license or GPLv3. See https://raw.github.com/Stuk/jszip-utils/master/LICENSE.markdown. 8 | 9 | */ 10 | !function(e){"object"==typeof exports?module.exports=e():"function"==typeof define&&define.amd?define(e):"undefined"!=typeof window?window.JSZipUtils=e():"undefined"!=typeof global?global.JSZipUtils=e():"undefined"!=typeof self&&(self.JSZipUtils=e())}(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);throw new Error("Cannot find module '"+o+"'")}var f=n[o]={exports:{}};t[o][0].call(f.exports,function(e){var n=t[o][1][e];return s(n?n:e)},f,f.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o