├── .gitignore ├── screenshot.png ├── src ├── images │ ├── cloud.png │ ├── grid.png │ ├── tshirt.png │ ├── new-window.png │ ├── toolbar │ │ ├── arrow.png │ │ ├── bold.png │ │ ├── line.png │ │ ├── redo.png │ │ ├── undo.png │ │ ├── circle.png │ │ ├── effects.png │ │ ├── italics.png │ │ ├── shapes.png │ │ ├── fill-color.png │ │ ├── line-color.png │ │ ├── rectangle.png │ │ ├── send-back.png │ │ ├── underline.png │ │ ├── bring-forward.png │ │ ├── bring-front.png │ │ ├── insert-text.png │ │ ├── send-backward.png │ │ └── rounded-rectangle.png │ └── sidebar │ │ ├── artwork.png │ │ └── export.png ├── js │ ├── config │ │ ├── development.js │ │ └── production.js │ ├── editor.js │ ├── lib │ │ ├── ie10-viewport-bug-workaround.min.js │ │ ├── blob.min.js │ │ ├── filesaver.min.js │ │ ├── jquery.ui.position.min.js │ │ ├── jquery.tooltipster.min.js │ │ └── jquery.contextMenu.min.js │ └── app │ │ ├── state.js │ │ ├── importExport.js │ │ ├── drawing.js │ │ ├── text.js │ │ ├── fetchApi.js │ │ ├── page.js │ │ ├── fabricUtils.js │ │ └── handlers.js ├── scss │ ├── editor.scss │ ├── _page.scss │ ├── _sidebar.scss │ └── _toolbar.scss ├── css │ └── lib │ │ ├── jquery.contextMenu.min.css │ │ ├── normalize.css │ │ ├── tooltipster.css │ │ └── spectrum.css └── html │ └── index.html ├── .jshintrc ├── README.md ├── LICENSE ├── package.json └── gulpfile.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .DS_Store 3 | build/ 4 | src/js/app/config.js 5 | -------------------------------------------------------------------------------- /screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danielktaylor/fabric-js-editor/HEAD/screenshot.png -------------------------------------------------------------------------------- /src/images/cloud.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danielktaylor/fabric-js-editor/HEAD/src/images/cloud.png -------------------------------------------------------------------------------- /src/images/grid.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danielktaylor/fabric-js-editor/HEAD/src/images/grid.png -------------------------------------------------------------------------------- /src/images/tshirt.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danielktaylor/fabric-js-editor/HEAD/src/images/tshirt.png -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "browserify": true, 3 | "jquery": true, 4 | "predef": [ 5 | "fabric" 6 | ] 7 | } 8 | -------------------------------------------------------------------------------- /src/images/new-window.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danielktaylor/fabric-js-editor/HEAD/src/images/new-window.png -------------------------------------------------------------------------------- /src/images/toolbar/arrow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danielktaylor/fabric-js-editor/HEAD/src/images/toolbar/arrow.png -------------------------------------------------------------------------------- /src/images/toolbar/bold.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danielktaylor/fabric-js-editor/HEAD/src/images/toolbar/bold.png -------------------------------------------------------------------------------- /src/images/toolbar/line.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danielktaylor/fabric-js-editor/HEAD/src/images/toolbar/line.png -------------------------------------------------------------------------------- /src/images/toolbar/redo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danielktaylor/fabric-js-editor/HEAD/src/images/toolbar/redo.png -------------------------------------------------------------------------------- /src/images/toolbar/undo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danielktaylor/fabric-js-editor/HEAD/src/images/toolbar/undo.png -------------------------------------------------------------------------------- /src/images/sidebar/artwork.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danielktaylor/fabric-js-editor/HEAD/src/images/sidebar/artwork.png -------------------------------------------------------------------------------- /src/images/sidebar/export.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danielktaylor/fabric-js-editor/HEAD/src/images/sidebar/export.png -------------------------------------------------------------------------------- /src/images/toolbar/circle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danielktaylor/fabric-js-editor/HEAD/src/images/toolbar/circle.png -------------------------------------------------------------------------------- /src/images/toolbar/effects.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danielktaylor/fabric-js-editor/HEAD/src/images/toolbar/effects.png -------------------------------------------------------------------------------- /src/images/toolbar/italics.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danielktaylor/fabric-js-editor/HEAD/src/images/toolbar/italics.png -------------------------------------------------------------------------------- /src/images/toolbar/shapes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danielktaylor/fabric-js-editor/HEAD/src/images/toolbar/shapes.png -------------------------------------------------------------------------------- /src/images/toolbar/fill-color.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danielktaylor/fabric-js-editor/HEAD/src/images/toolbar/fill-color.png -------------------------------------------------------------------------------- /src/images/toolbar/line-color.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danielktaylor/fabric-js-editor/HEAD/src/images/toolbar/line-color.png -------------------------------------------------------------------------------- /src/images/toolbar/rectangle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danielktaylor/fabric-js-editor/HEAD/src/images/toolbar/rectangle.png -------------------------------------------------------------------------------- /src/images/toolbar/send-back.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danielktaylor/fabric-js-editor/HEAD/src/images/toolbar/send-back.png -------------------------------------------------------------------------------- /src/images/toolbar/underline.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danielktaylor/fabric-js-editor/HEAD/src/images/toolbar/underline.png -------------------------------------------------------------------------------- /src/images/toolbar/bring-forward.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danielktaylor/fabric-js-editor/HEAD/src/images/toolbar/bring-forward.png -------------------------------------------------------------------------------- /src/images/toolbar/bring-front.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danielktaylor/fabric-js-editor/HEAD/src/images/toolbar/bring-front.png -------------------------------------------------------------------------------- /src/images/toolbar/insert-text.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danielktaylor/fabric-js-editor/HEAD/src/images/toolbar/insert-text.png -------------------------------------------------------------------------------- /src/images/toolbar/send-backward.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danielktaylor/fabric-js-editor/HEAD/src/images/toolbar/send-backward.png -------------------------------------------------------------------------------- /src/js/config/development.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | module.exports = { 4 | icons: { 5 | host: "//localhost:5000" 6 | } 7 | }; 8 | -------------------------------------------------------------------------------- /src/js/config/production.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | module.exports = { 4 | icons: { 5 | host: "//data.daringlogos.com" 6 | } 7 | }; 8 | -------------------------------------------------------------------------------- /src/images/toolbar/rounded-rectangle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danielktaylor/fabric-js-editor/HEAD/src/images/toolbar/rounded-rectangle.png -------------------------------------------------------------------------------- /src/js/editor.js: -------------------------------------------------------------------------------- 1 | global.jQuery = require('jquery'); 2 | global.$ = global.jQuery; 3 | 4 | $(function() { 5 | "use strict"; 6 | global.canvas = new fabric.Canvas('c'); 7 | new (require('./app/handlers.js'))(); 8 | }); 9 | -------------------------------------------------------------------------------- /src/js/lib/ie10-viewport-bug-workaround.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * IE10 viewport hack for Surface/desktop Windows 8 bug 3 | * Copyright 2014-2015 Twitter, Inc. 4 | * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) 5 | */ 6 | (function(){if(navigator.userAgent.match(/IEMobile\/10\.0/)){var a=document.createElement("style");a.appendChild(document.createTextNode("@-ms-viewport{width:auto!important}"));document.querySelector("head").appendChild(a)}})(); -------------------------------------------------------------------------------- /src/scss/editor.scss: -------------------------------------------------------------------------------- 1 | /* --- toolbar colors --- */ 2 | $toolbar-height: 36px; 3 | $toolbar-background: #F2F2F2; 4 | $toolbar-border: #BDBDBD; 5 | $toolbar-button-height: 26px; 6 | 7 | /* --- sidebar --- */ 8 | $sidebar-width: 80px; 9 | $sidebar-color: #37375A; 10 | $sidebar-color-hover: lighten($sidebar-color, 12%); 11 | $sidebar-color-highlight: #E74C3C; 12 | 13 | /* --- preview + download buttons */ 14 | $download-button-color: #E74C3C; 15 | $preview-button-color: #16A085; 16 | 17 | @import 'page'; 18 | @import 'sidebar'; 19 | @import 'toolbar'; 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Fabric.js Editor 2 | 3 | A 100% web-based vector image editor that uses the HTML5 canvas element and [Fabric.js](http://fabricjs.com/) 4 | 5 | ## Running the code 6 | 7 | npm install 8 | gulp dev 9 | 10 | If you run `gulp` without any arguments, you can see a list of the other tasks. 11 | 12 | ## Screenshots 13 | 14 | ![Screenshot](screenshot.png) 15 | 16 | ## TODO's 17 | 18 | * Switch to [gToolbars.js](https://github.com/danielktaylor/gToolbars.js), which is based on this project, for the toolbars 19 | * Strip out some remaining code that talks to the (no longer existing) backend server 20 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Daniel Taylor 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 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "fabric-js-editor", 3 | "version": "0.0.2", 4 | "description": "> npm install gulp > gulp", 5 | "private": true, 6 | "main": "gulpfile.js", 7 | "dependencies": { 8 | "filesaver.js": "^0.2.0", 9 | "jquery": "^3.3.1", 10 | "jszip": "^2.6.1", 11 | "jszip-utils": "0.0.2", 12 | "spectrum-colorpicker": "^1.8.0" 13 | }, 14 | "devDependencies": { 15 | "browserify": "^12.0.2", 16 | "del": "^2.2.0", 17 | "gulp": "^3.9.0", 18 | "gulp-clean": "^0.3.1", 19 | "gulp-concat": "^2.6.0", 20 | "gulp-if": "^2.0.0", 21 | "gulp-jshint": "^2.0.0", 22 | "gulp-rename": "^1.2.2", 23 | "gulp-sass": "^2.1.1", 24 | "gulp-task-listing": "^1.0.1", 25 | "gulp-uglify": "^1.5.4", 26 | "gulp-uglifycss": "^1.0.5", 27 | "gulp-webserver": "^0.9.1", 28 | "jshint": "^2.9.1-rc1", 29 | "jshint-stylish": "^2.1.0", 30 | "merge-stream": "^1.0.0", 31 | "run-sequence": "^1.1.5", 32 | "vinyl-buffer": "^1.0.0", 33 | "vinyl-source-stream": "^1.1.0" 34 | }, 35 | "scripts": { 36 | "test": "echo \"Error: no test specified\" && exit 1" 37 | }, 38 | "author": "", 39 | "license": "ISC" 40 | } 41 | -------------------------------------------------------------------------------- /src/js/app/state.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | var states = []; 4 | var currentState = 0; 5 | var restoring = false; 6 | var getState, setState; 7 | 8 | function pushState() { 9 | if (restoring) { 10 | return; 11 | } 12 | 13 | var state = getState(); 14 | if (state === states[currentState]) { 15 | // nothing has changed 16 | return; 17 | } 18 | 19 | // Cap the number of states 20 | if (currentState > 99) { 21 | currentState--; 22 | states.shift(); 23 | } 24 | 25 | if (currentState < states.length - 1) { 26 | // Forking stack 27 | var remove = (states.length - 1) - currentState; 28 | states = states.slice(0, states.length - remove); 29 | } 30 | 31 | currentState++; 32 | states.push(state); 33 | } 34 | 35 | function restore(state) { 36 | restoring = true; 37 | setState(state); 38 | restoring = false; 39 | } 40 | 41 | function undo() { 42 | if (currentState > 0) { 43 | currentState--; 44 | restore(states[currentState]); 45 | } 46 | } 47 | 48 | function redo() { 49 | if (currentState < (states.length - 1)) { 50 | currentState++; 51 | restore(states[currentState]); 52 | } 53 | } 54 | 55 | /* ----- exports ----- */ 56 | 57 | function StateModule(_getState, _setState) { 58 | if (!(this instanceof StateModule)) return new StateModule(); 59 | getState = _getState; 60 | setState = _setState; 61 | states.push(getState()); 62 | } 63 | 64 | StateModule.prototype.save = pushState; 65 | StateModule.prototype.undo = undo; 66 | StateModule.prototype.redo = redo; 67 | 68 | module.exports = StateModule; 69 | -------------------------------------------------------------------------------- /src/js/app/importExport.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | var config = require('./config.js'); 4 | 5 | function importFile(zipData, handler) { 6 | var zip = new (require('jszip'))(); 7 | zip.load(zipData); 8 | var content = zip.file("design").asText(); 9 | handler(content); 10 | } 11 | 12 | function loadRemoteFile(url, handler) { 13 | var zipUtils = require('jszip-utils'); 14 | zipUtils.getBinaryContent(url, function(err, data) { 15 | if (err) { 16 | handler(null); 17 | return; 18 | } 19 | importFile(data, handler); 20 | }); 21 | } 22 | 23 | function exportFile(textData, filename) { 24 | var filesaver = require('filesaver.js'); 25 | var zip = new (require('jszip'))(); 26 | zip.file("design", textData); 27 | var content = zip.generate({type: "blob", compression: "DEFLATE"}); 28 | filesaver.saveAs(content, filename); 29 | } 30 | 31 | function shareFile(textData, callback) { 32 | var zip = new (require('jszip'))(); 33 | zip.file("design", textData); 34 | var content = zip.generate({type: "blob", compression: "DEFLATE"}); 35 | 36 | var fd = new FormData(); 37 | fd.append('zip', content); 38 | 39 | var endpoint = config.icons.host; 40 | $.ajax({ 41 | type: 'POST', 42 | async: false, // For popup blocker 43 | url: endpoint + '/save', 44 | data: fd, 45 | processData: false, 46 | contentType: false 47 | }).done(function(data) { 48 | callback(data); 49 | }); 50 | } 51 | 52 | /* ----- exports ----- */ 53 | 54 | function ImportExportModule(_getState, _setState) { 55 | if (!(this instanceof ImportExportModule)) return new ImportExportModule(); 56 | } 57 | 58 | ImportExportModule.prototype.importFile = importFile; 59 | ImportExportModule.prototype.exportFile = exportFile; 60 | ImportExportModule.prototype.loadRemoteFile = loadRemoteFile; 61 | ImportExportModule.prototype.shareFile = shareFile; 62 | 63 | module.exports = ImportExportModule; 64 | -------------------------------------------------------------------------------- /src/scss/_page.scss: -------------------------------------------------------------------------------- 1 | @mixin hide { 2 | visibility: hidden; 3 | display: none; 4 | } 5 | 6 | html { 7 | height: 100%; 8 | } 9 | 10 | body { 11 | font-family: 'Roboto','Helvetica','Arial',sans-serif; 12 | background-color: #EEEEEE; 13 | } 14 | 15 | .noselect { 16 | -webkit-touch-callout: none; 17 | -webkit-user-select: none; 18 | -khtml-user-select: none; 19 | -moz-user-select: none; 20 | -ms-user-select: none; 21 | user-select: none; 22 | } 23 | 24 | .noshow { 25 | @include hide; 26 | } 27 | 28 | #c { 29 | position: relative; 30 | border: 1px solid #BBBBBB; 31 | box-shadow: 2px 2px 2px #d7d7d7; 32 | } 33 | 34 | #canvas-container { 35 | position: relative; 36 | left: 0px; 37 | top: 0px; 38 | margin-right: 100px; 39 | background:url("../images/grid.png") repeat; 40 | } 41 | 42 | .container { 43 | position: relative; 44 | padding: 0 0 0 $sidebar-width; 45 | } 46 | 47 | #content { 48 | width: 100%; 49 | height: 100%; 50 | background: #EEEEEE; 51 | } 52 | 53 | #preview { 54 | position: fixed; 55 | top: 0px; 56 | left: 0px; 57 | background: rgba(0, 0, 0, 0.55); 58 | z-index: 20; 59 | width: 100%; 60 | height: 100%; 61 | text-align: center; 62 | } 63 | 64 | #preview-image { 65 | width: 50%; 66 | padding-top: 20px; 67 | } 68 | 69 | #dynamic-preview { 70 | position: fixed; 71 | z-index: 25; 72 | top: 0px; 73 | left: 0px; 74 | } 75 | 76 | #loading-spinner { 77 | position:absolute; 78 | bottom: 32px; 79 | left: 170px; 80 | z-index: 95; 81 | } 82 | 83 | .floating-button { 84 | width: 140px; 85 | z-index: 5; 86 | bottom: 30px; 87 | 88 | > img { 89 | position: relative; 90 | top: -3px; 91 | left: -3px; 92 | } 93 | } 94 | 95 | #preview-button { 96 | position: absolute; 97 | color: white; 98 | background-color: $preview-button-color; 99 | right: 260px; 100 | &:hover { 101 | background-color: lighten($preview-button-color, 10%); 102 | } 103 | } 104 | 105 | #download-button { 106 | position: absolute; 107 | color: white; 108 | background-color: $download-button-color; 109 | right: 67px; 110 | &:hover { 111 | background-color: lighten($download-button-color, 10%); 112 | } 113 | } 114 | 115 | .mdl-layout__drawer-button { 116 | @include hide; 117 | } 118 | 119 | .mdl-layout__container { 120 | left: 0px; 121 | } 122 | -------------------------------------------------------------------------------- /src/js/lib/blob.min.js: -------------------------------------------------------------------------------- 1 | /*! @source http://purl.eligrey.com/github/Blob.js/blob/master/Blob.js */ 2 | (function(a){a.URL=a.URL||a.webkitURL;if(a.Blob&&a.URL){try{new Blob;return}catch(d){}}var c=a.BlobBuilder||a.WebKitBlobBuilder||a.MozBlobBuilder||(function(p){var g=function(z){return Object.prototype.toString.call(z).match(/^\[object\s(.*)\]$/)[1]},y=function m(){this.data=[]},w=function i(B,z,A){this.data=B;this.size=B.length;this.type=z;this.encoding=A},q=y.prototype,v=w.prototype,s=p.FileReaderSync,e=function(z){this.code=this[this.name=z]},r=("NOT_FOUND_ERR SECURITY_ERR ABORT_ERR NOT_READABLE_ERR ENCODING_ERR NO_MODIFICATION_ALLOWED_ERR INVALID_STATE_ERR SYNTAX_ERR").split(" "),u=r.length,l=p.URL||p.webkitURL||p,t=l.createObjectURL,f=l.revokeObjectURL,k=l,o=p.btoa,j=p.atob,h=p.ArrayBuffer,n=p.Uint8Array,x=/^[\w-]+:\/*\[?[\w\.:-]+\]?(?::[0-9]+)?/;w.fake=v.fake=true;while(u--){e.prototype[r[u]]=u+1}if(!l.createObjectURL){k=p.URL=function(A){var z=document.createElementNS("http://www.w3.org/1999/xhtml","a"),B;z.href=A;if(!("origin" in z)){if(z.protocol.toLowerCase()==="data:"){z.origin=null}else{B=A.match(x);z.origin=B&&B[1]}}return z}}k.createObjectURL=function(A){var B=A.type,z;if(B===null){B="application/octet-stream"}if(A instanceof w){z="data:"+B;if(A.encoding==="base64"){return z+";base64,"+A.data}else{if(A.encoding==="URI"){return z+","+decodeURIComponent(A.data)}}if(o){return z+";base64,"+o(A.data)}else{return z+","+encodeURIComponent(A.data)}}else{if(t){return t.call(l,A)}}};k.revokeObjectURL=function(z){if(z.substring(0,5)!=="data:"&&f){f.call(l,z)}};q.append=function(D){var F=this.data;if(n&&(D instanceof h||D instanceof n)){var E="",A=new n(D),B=0,C=A.length;for(;B1?z:this.data.length),B,this.encoding)};v.toString=function(){return"[object Blob]"};v.close=function(){this.size=0;delete this.data};return y}(a));a.Blob=function(j,h){var l=h?(h.type||""):"";var g=new c();if(j){for(var k=0,e=j.length;klabel>input,.context-menu-item>label>textarea{-webkit-user-select:text;-moz-user-select:text;-ms-user-select:text;user-select:text}.context-menu-item.context-menu-hover{color:#fff;cursor:pointer;background-color:#2980b9}.context-menu-item.context-menu-disabled{color:#626262;background-color:#fff}.context-menu-input.context-menu-hover,.context-menu-item.context-menu-disabled.context-menu-hover{cursor:default;background-color:#eee}.context-menu-submenu:after{position:absolute;top:50%;right:8px;z-index:1;width:0;height:0;content:'';border-color:transparent transparent transparent #2f2f2f;border-style:solid;border-width:4px 0 4px 4px;-webkit-transform:translateY(-50%);-ms-transform:translateY(-50%);-o-transform:translateY(-50%);transform:translateY(-50%)}.context-menu-item.context-menu-input{padding:5px 10px}.context-menu-input>label>*{vertical-align:top}.context-menu-input>label>input[type=checkbox],.context-menu-input>label>input[type=radio]{position:relative;top:3px}.context-menu-input>label,.context-menu-input>label>input[type=text],.context-menu-input>label>select,.context-menu-input>label>textarea{display:block;width:100%;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.context-menu-input>label>textarea{height:100px}.context-menu-item>.context-menu-list{top:5px;right:-5px;display:none}.context-menu-item.context-menu-visible>.context-menu-list{display:block}.context-menu-accesskey{text-decoration:underline} 16 | /*# sourceMappingURL=jquery.contextMenu.min.css.map */ 17 | -------------------------------------------------------------------------------- /src/scss/_sidebar.scss: -------------------------------------------------------------------------------- 1 | #sidebar { 2 | position: absolute; 3 | top: 0px; 4 | bottom: 0px; 5 | left: 0px; 6 | right: 0px; 7 | width: $sidebar-width; 8 | background-color: $sidebar-color; 9 | padding-top: 80px; 10 | z-index: 10; 11 | font-weight: 400; 12 | } 13 | 14 | .sidebar-item { 15 | text-align: center; 16 | border: 0px; 17 | padding-top: 20px; 18 | padding-bottom: 15px; 19 | cursor: pointer; 20 | font-size: 13px; 21 | line-height: 18px; 22 | color: white; 23 | border-left: 3px solid $sidebar-color; 24 | } 25 | 26 | .sidebar-item-hover { 27 | background-color: $sidebar-color-hover; 28 | border-left: 3px solid $sidebar-color-hover; 29 | } 30 | 31 | .sidebar-item-selected { 32 | border-left: 3px solid $sidebar-color-highlight; 33 | background-color: $sidebar-color-hover; 34 | } 35 | 36 | .sidebar-img { 37 | width: 40%; 38 | padding-bottom: 10px; 39 | } 40 | 41 | .slideout-body { 42 | width: 90%; 43 | padding-left: 20px; 44 | overflow: hidden; 45 | } 46 | 47 | .mdl-layout-title { 48 | padding-bottom: 30px; 49 | } 50 | 51 | #drawer { 52 | overflow-y: scroll; 53 | } 54 | 55 | #drawer { 56 | width: 400px; 57 | left: -1 * $sidebar-width - 20px; 58 | } 59 | 60 | #drawer.is-visible { 61 | left: $sidebar-width; 62 | } 63 | 64 | /* --- Artwork Panel --- */ 65 | 66 | #artwork-panel { 67 | height: 100%; 68 | } 69 | 70 | #artwork-spinner { 71 | margin-top: 20px; 72 | margin-bottom: 20px; 73 | text-align: center; 74 | } 75 | 76 | #artwork-search-form #left { 77 | display: inline-block; 78 | width: 85%; 79 | height: 50px; 80 | margin-bottom: 20px; 81 | } 82 | 83 | #artwork-search-form #right { 84 | display: inline-block; 85 | vertical-align: bottom; 86 | height: 27px; 87 | margin-bottom: 20px; 88 | &:hover { 89 | cursor: pointer; 90 | } 91 | } 92 | 93 | #search-submit { 94 | color: #3F51B5; 95 | } 96 | 97 | .search-results { 98 | padding-top: 15px; 99 | width: 100%; 100 | 101 | //height: 100%; 102 | height: 400px; 103 | 104 | overflow-x: hidden; 105 | overflow-y: scroll; 106 | 107 | // Always show scrollbar 108 | &::-webkit-scrollbar { 109 | -webkit-appearance: none; 110 | &:vertical { 111 | width: 11px; 112 | } 113 | &:horizontal { 114 | height: 11px; 115 | } 116 | } 117 | &::-webkit-scrollbar-thumb { 118 | border-radius: 8px; 119 | border: 2px solid #FAFAFA; /* should match background, can't be transparent */ 120 | background-color: rgba(0, 0, 0, .5); 121 | } 122 | &::-webkit-scrollbar-track { 123 | background-color: #FAFAFA; 124 | border-radius: 8px; 125 | } 126 | 127 | } 128 | 129 | .preview-image { 130 | cursor: pointer; 131 | border: 1px solid #b1b1b1; 132 | width: 92px; 133 | height: 92px; 134 | margin: 7px; 135 | } 136 | 137 | #search-type-block { 138 | margin-left: 60px; 139 | margin-top: -10px; 140 | margin-bottom: 10px; 141 | 142 | #clipart-label { 143 | margin-left: 30px; 144 | } 145 | } 146 | 147 | .onload-tooltip { 148 | padding-top: 12px; 149 | padding-left: 15px; 150 | padding-right: 10px; 151 | font-size: 22px; 152 | line-height: 30px; 153 | color: #383838; 154 | } 155 | 156 | /* --- Export Panel --- */ 157 | 158 | .export-options { 159 | padding-right: 10px; 160 | margin-top: -20px; 161 | } 162 | 163 | .export-button { 164 | width: 150px; 165 | margin-bottom: 20px; 166 | background-color: #d1d1f4; 167 | } 168 | 169 | .export-button-set { 170 | text-align: center; 171 | margin-top: -5px; 172 | margin-left: -5px; 173 | } 174 | 175 | .file-options { 176 | margin-left: 45px; 177 | display: inline-block; 178 | vertical-align: top; 179 | } 180 | 181 | .export-import-buttons { 182 | display: inline-block; 183 | padding: 5px; 184 | } 185 | 186 | #file-picker-label { 187 | padding:0px; 188 | } 189 | 190 | #uploading-placeholder { 191 | text-align: center; 192 | background-color: #7e7e7e; 193 | margin-left: 2px; 194 | margin-right: 12px; 195 | height: 35px; 196 | color: #f0efef; 197 | font-size: 23px; 198 | line-height: 35px; 199 | } 200 | -------------------------------------------------------------------------------- /gulpfile.js: -------------------------------------------------------------------------------- 1 | /* ----- plugins ----- */ 2 | 3 | var gulp = require('gulp'); 4 | var browserify = require('browserify'); 5 | var clean = require('gulp-clean'); 6 | var merge = require('merge-stream'); 7 | var taskListing = require('gulp-task-listing'); 8 | var jshint = require('gulp-jshint'); 9 | var sass = require('gulp-sass'); 10 | var concat = require('gulp-concat'); 11 | var uglify = require('gulp-uglify'); 12 | var uglifycss = require('gulp-uglifycss'); 13 | var rename = require('gulp-rename'); 14 | var webserver = require('gulp-webserver'); 15 | var source = require('vinyl-source-stream'); 16 | var buffer = require('vinyl-buffer'); 17 | var gulpif = require('gulp-if'); 18 | var del = require('del'); 19 | var runSequence = require('run-sequence'); 20 | var processes = require('child_process'); 21 | 22 | 23 | /* ----- build tasks ----- */ 24 | 25 | var exec = require('child_process').exec; 26 | var isDebug; 27 | 28 | // Handle errors 29 | function errorHandler (error) { 30 | console.log(error); 31 | this.emit('end'); 32 | } 33 | 34 | // copy correct JS config file 35 | gulp.task('x-js-config', function() { 36 | if (isDebug === true) { 37 | gulp.src(['src/js/config/development.js']) 38 | .pipe(rename("config.js")) 39 | .pipe(gulp.dest('src/js/app/')); 40 | } else { 41 | gulp.src(['src/js/config/production.js']) 42 | .pipe(rename("config.js")) 43 | .pipe(gulp.dest('src/js/app/')); 44 | } 45 | }); 46 | 47 | // dev webserver 48 | gulp.task('x-webserver', function() { 49 | gulp.src('build/') 50 | .pipe(webserver({ 51 | livereload: true, 52 | directoryListing: false, 53 | open: true 54 | })); 55 | }); 56 | 57 | // watch files for changes 58 | gulp.task('x-watch', function() { 59 | gulp.watch('src/js/*.js', ['lint', 'x-browserify']); 60 | gulp.watch('src/js/app/*.js', ['lint', 'x-browserify']); 61 | gulp.watch('src/scss/*.scss', ['x-sass']); 62 | gulp.watch('src/js/config/*.js', ['x-js-config']); 63 | gulp.watch(['src/images/**/*', 64 | 'src/js/lib/*', 65 | 'src/css/lib/*', 66 | 'src/html/*' 67 | ], ['x-copy']); 68 | }); 69 | 70 | // compile JS 71 | gulp.task('x-browserify', function() { 72 | return browserify({ 73 | entries: 'src/js/editor.js', 74 | debug: isDebug 75 | }) 76 | .bundle() 77 | .on("error", errorHandler) 78 | .pipe(source('bundle.js')) 79 | .pipe(buffer()) 80 | .pipe(gulpif(!isDebug, uglify())) 81 | .pipe(gulp.dest('build/js')); 82 | }); 83 | 84 | // compile SASS 85 | gulp.task('x-sass', function() { 86 | return gulp.src('src/scss/*.scss') 87 | .pipe(sass() 88 | .on('error', sass.logError)) 89 | .pipe(gulp.dest('build/css')); 90 | }); 91 | 92 | // copy supporting files 93 | gulp.task('x-copy', function() { 94 | 95 | var images = gulp.src(['src/images/**/*']) 96 | .pipe(gulp.dest('build/images')); 97 | 98 | var css = gulp.src(['src/css/lib/*.css']) 99 | .pipe(gulp.dest('build/css/lib')); 100 | 101 | var js = gulp.src(['src/js/lib/*.js']) 102 | .pipe(gulp.dest('build/js/lib')); 103 | 104 | var html = gulp.src(['src/html/*']) 105 | .pipe(gulp.dest('build')); 106 | 107 | return merge(images, css, js, html); 108 | }); 109 | 110 | // minify assets 111 | gulp.task('x-minify', function() { 112 | return gulp.src('build/css/*.css') 113 | .pipe(uglifycss({ 114 | "max-line-len": 80 115 | })) 116 | .pipe(gulp.dest('build/css')); 117 | }); 118 | 119 | 120 | /* ----- user tasks ----- */ 121 | 122 | // cleanup the build directory 123 | gulp.task('clean', function() { 124 | return del(['./build/*']); 125 | }); 126 | 127 | // lint javascript 128 | gulp.task('lint', function() { 129 | return gulp.src(['src/js/*.js', 'src/js/app/*.js'], {base: 'src/js/'}) 130 | .pipe(jshint()) 131 | .on('error', errorHandler) 132 | .pipe(jshint.reporter('default')); 133 | }); 134 | 135 | // build production assets 136 | gulp.task('prod', function(done) { 137 | isDebug = false; 138 | return runSequence('clean', 139 | 'x-js-config', 140 | 'x-sass', 141 | 'x-browserify', 142 | 'x-minify', 143 | 'x-copy', 144 | done); 145 | }); 146 | 147 | // start dev server 148 | gulp.task('dev', [], function(done) { 149 | isDebug = true; 150 | return runSequence('clean', 151 | 'x-js-config', 152 | ['x-sass', 'x-browserify'], 153 | 'x-copy', 154 | ['lint', 'x-webserver', 'x-watch'], 155 | done); 156 | }); 157 | 158 | // default: list tasks 159 | gulp.task('default', taskListing.withFilters(null, 'x-')); 160 | -------------------------------------------------------------------------------- /src/js/lib/filesaver.min.js: -------------------------------------------------------------------------------- 1 | /*! @source http://purl.eligrey.com/github/FileSaver.js/blob/master/FileSaver.js */ 2 | var saveAs=saveAs||function(view){"use strict";if(typeof navigator!=="undefined"&&/MSIE [1-9]\./.test(navigator.userAgent)){return}var doc=view.document,get_URL=function(){return view.URL||view.webkitURL||view},save_link=doc.createElementNS("http://www.w3.org/1999/xhtml","a"),can_use_save_link="download"in save_link,click=function(node){var event=new MouseEvent("click");node.dispatchEvent(event)},is_safari=/Version\/[\d\.]+.*Safari/.test(navigator.userAgent),webkit_req_fs=view.webkitRequestFileSystem,req_fs=view.requestFileSystem||webkit_req_fs||view.mozRequestFileSystem,throw_outside=function(ex){(view.setImmediate||view.setTimeout)(function(){throw ex},0)},force_saveable_type="application/octet-stream",fs_min_size=0,arbitrary_revoke_timeout=500,revoke=function(file){var revoker=function(){if(typeof file==="string"){get_URL().revokeObjectURL(file)}else{file.remove()}};if(view.chrome){revoker()}else{setTimeout(revoker,arbitrary_revoke_timeout)}},dispatch=function(filesaver,event_types,event){event_types=[].concat(event_types);var i=event_types.length;while(i--){var listener=filesaver["on"+event_types[i]];if(typeof listener==="function"){try{listener.call(filesaver,event||filesaver)}catch(ex){throw_outside(ex)}}}},auto_bom=function(blob){if(/^\s*(?:text\/\S*|application\/xml|\S*\/\S*\+xml)\s*;.*charset\s*=\s*utf-8/i.test(blob.type)){return new Blob(["\ufeff",blob],{type:blob.type})}return blob},FileSaver=function(blob,name,no_auto_bom){if(!no_auto_bom){blob=auto_bom(blob)}var filesaver=this,type=blob.type,blob_changed=false,object_url,target_view,dispatch_all=function(){dispatch(filesaver,"writestart progress write writeend".split(" "))},fs_error=function(){if(target_view&&is_safari&&typeof FileReader!=="undefined"){var reader=new FileReader;reader.onloadend=function(){var base64Data=reader.result;target_view.location.href="data:attachment/file"+base64Data.slice(base64Data.search(/[,;]/));filesaver.readyState=filesaver.DONE;dispatch_all()};reader.readAsDataURL(blob);filesaver.readyState=filesaver.INIT;return}if(blob_changed||!object_url){object_url=get_URL().createObjectURL(blob)}if(target_view){target_view.location.href=object_url}else{var new_tab=view.open(object_url,"_blank");if(new_tab==undefined&&is_safari){view.location.href=object_url}}filesaver.readyState=filesaver.DONE;dispatch_all();revoke(object_url)},abortable=function(func){return function(){if(filesaver.readyState!==filesaver.DONE){return func.apply(this,arguments)}}},create_if_not_found={create:true,exclusive:false},slice;filesaver.readyState=filesaver.INIT;if(!name){name="download"}if(can_use_save_link){object_url=get_URL().createObjectURL(blob);setTimeout(function(){save_link.href=object_url;save_link.download=name;click(save_link);dispatch_all();revoke(object_url);filesaver.readyState=filesaver.DONE});return}if(view.chrome&&type&&type!==force_saveable_type){slice=blob.slice||blob.webkitSlice;blob=slice.call(blob,0,blob.size,force_saveable_type);blob_changed=true}if(webkit_req_fs&&name!=="download"){name+=".download"}if(type===force_saveable_type||webkit_req_fs){target_view=view}if(!req_fs){fs_error();return}fs_min_size+=blob.size;req_fs(view.TEMPORARY,fs_min_size,abortable(function(fs){fs.root.getDirectory("saved",create_if_not_found,abortable(function(dir){var save=function(){dir.getFile(name,create_if_not_found,abortable(function(file){file.createWriter(abortable(function(writer){writer.onwriteend=function(event){target_view.location.href=file.toURL();filesaver.readyState=filesaver.DONE;dispatch(filesaver,"writeend",event);revoke(file)};writer.onerror=function(){var error=writer.error;if(error.code!==error.ABORT_ERR){fs_error()}};"writestart progress write abort".split(" ").forEach(function(event){writer["on"+event]=filesaver["on"+event]});writer.write(blob);filesaver.abort=function(){writer.abort();filesaver.readyState=filesaver.DONE};filesaver.readyState=filesaver.WRITING}),fs_error)}),fs_error)};dir.getFile(name,{create:false},abortable(function(file){file.remove();save()}),abortable(function(ex){if(ex.code===ex.NOT_FOUND_ERR){save()}else{fs_error()}}))}),fs_error)}),fs_error)},FS_proto=FileSaver.prototype,saveAs=function(blob,name,no_auto_bom){return new FileSaver(blob,name,no_auto_bom)};if(typeof navigator!=="undefined"&&navigator.msSaveOrOpenBlob){return function(blob,name,no_auto_bom){if(!no_auto_bom){blob=auto_bom(blob)}return navigator.msSaveOrOpenBlob(blob,name||"download")}}FS_proto.abort=function(){var filesaver=this;filesaver.readyState=filesaver.DONE;dispatch(filesaver,"abort")};FS_proto.readyState=FS_proto.INIT=0;FS_proto.WRITING=1;FS_proto.DONE=2;FS_proto.error=FS_proto.onwritestart=FS_proto.onprogress=FS_proto.onwrite=FS_proto.onabort=FS_proto.onerror=FS_proto.onwriteend=null;return saveAs}(typeof self!=="undefined"&&self||typeof window!=="undefined"&&window||this.content);if(typeof module!=="undefined"&&module.exports){module.exports.saveAs=saveAs}else if(typeof define!=="undefined"&&define!==null&&define.amd!=null){define([],function(){return saveAs})} 3 | -------------------------------------------------------------------------------- /src/js/app/drawing.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | var canvas = global.canvas; 4 | var utils = new (require('./fabricUtils.js'))(); 5 | 6 | var drawnObj, isMouseDown; 7 | 8 | function disableDraw() { 9 | canvas.off('mouse:down'); 10 | canvas.off('mouse:move'); 11 | canvas.off('mouse:up'); 12 | 13 | canvas.selection = true; 14 | canvas.forEachObject(function(o) { 15 | o.selectable = true; 16 | }); 17 | } 18 | 19 | function drawObj(objType) { 20 | // Esc key handler 21 | $(document).on("keyup", escHandler); 22 | 23 | canvas.selection = false; 24 | canvas.forEachObject(function(o) { 25 | o.selectable = false; 26 | }); 27 | 28 | canvas.on('mouse:down', function(o){ 29 | // Unregister escape key handler 30 | $(document).off("keyup", escHandler); 31 | 32 | isMouseDown = true; 33 | var pointer = canvas.getPointer(o.e); 34 | 35 | if (objType === 'line') { 36 | var points = [ pointer.x, pointer.y, pointer.x, pointer.y ]; 37 | drawnObj = new fabric.Line(points, { 38 | strokeWidth: 5, 39 | fill: 'blue', 40 | stroke: 'blue', 41 | originX: 'center', 42 | originY: 'center' 43 | }); 44 | } else if (objType === 'square') { 45 | drawnObj = new fabric.Rect({ 46 | width: 0, 47 | height: 0, 48 | top: pointer.y, 49 | left: pointer.x, 50 | fill: 'green' 51 | }); 52 | } else if (objType === 'rounded-rect') { 53 | drawnObj = new fabric.Rect({ 54 | width: 0, 55 | height: 0, 56 | top: pointer.y, 57 | left: pointer.x, 58 | rx: 10, 59 | ry: 10, 60 | fill: 'red' 61 | }); 62 | } else if (objType === 'circle') { 63 | drawnObj = new fabric.Circle({ 64 | radius: 0, 65 | top: pointer.y, 66 | left: pointer.x, 67 | fill: 'yellow' 68 | }); 69 | } 70 | 71 | canvas.add(drawnObj); 72 | }); 73 | 74 | canvas.on('mouse:move', function(o){ 75 | if (!isMouseDown) return; 76 | var shift = o.e.shiftKey; 77 | var pointer = canvas.getPointer(o.e); 78 | 79 | if (objType === 'line') { 80 | if (shift) { 81 | // TODO rotate towards closest angle 82 | drawnObj.set({ x2: pointer.x, y2: pointer.y }); 83 | } else { 84 | drawnObj.set({ x2: pointer.x, y2: pointer.y }); 85 | } 86 | } else if (objType === 'square' || objType === 'rounded-rect') { 87 | var newWidth = (drawnObj.left - pointer.x) * -1; 88 | var newHeight = (drawnObj.top - pointer.y) * -1; 89 | drawnObj.set({width: newWidth, height: newHeight}); 90 | } else if (objType === 'circle') { 91 | var x = drawnObj.left - pointer.x; 92 | var y = drawnObj.top - pointer.y; 93 | var diff = Math.sqrt(x*x + y*y); 94 | drawnObj.set({radius: diff/2.3}); 95 | } 96 | 97 | canvas.renderAll(); 98 | }); 99 | 100 | canvas.on('mouse:up', function(o){ 101 | isMouseDown = false; 102 | 103 | // Fix upside-down square 104 | if (objType === 'square' || objType === 'rounded-rect') { 105 | if (drawnObj.width < 0) { 106 | var newLeft = drawnObj.left + drawnObj.width; 107 | var newWidth = Math.abs(drawnObj.width); 108 | drawnObj.set({left: newLeft, width: newWidth}); 109 | } 110 | 111 | if (drawnObj.height < 0) { 112 | var newTop = drawnObj.top + drawnObj.height; 113 | var newHeight = Math.abs(drawnObj.height); 114 | drawnObj.set({top: newTop, height: newHeight}); 115 | } 116 | } 117 | 118 | // Delete the object if it's tiny, otherwise select it 119 | if (drawnObj.height !== 0 || drawnObj.width !== 0) { 120 | canvas.defaultCursor = 'auto'; 121 | 122 | // Fix selection bug by selecting and deselecting all objects 123 | utils.selectAll(); 124 | canvas.discardActiveObject(); 125 | 126 | // Select the object 127 | canvas.setActiveObject(drawnObj).renderAll(); 128 | 129 | // Set per-pixel dragging rather than bounding-box dragging 130 | drawnObj.perPixelTargetFind = true; 131 | drawnObj.targetFindTolerance = 4; 132 | 133 | // Disable drawing 134 | disableDraw(); 135 | 136 | // Push the canvas state to history 137 | canvas.trigger( "object:statechange"); 138 | } else { 139 | canvas.remove(drawnObj); 140 | } 141 | }); 142 | 143 | } 144 | 145 | function cancelInsert() { 146 | canvas.defaultCursor = 'auto'; 147 | disableDraw(); 148 | $("#toolbar-text").removeClass("toolbar-item-active"); 149 | } 150 | 151 | // Cancel text insertion 152 | function escHandler(e) { 153 | if (e.keyCode == 27) { 154 | cancelInsert(); 155 | 156 | // Unregister escape key handler 157 | $(document).off("keyup", escHandler); 158 | } 159 | } 160 | 161 | /* ----- exports ----- */ 162 | 163 | function DrawingModule() { 164 | if (!(this instanceof DrawingModule)) return new DrawingModule(); 165 | // constructor 166 | } 167 | 168 | DrawingModule.prototype.drawObj = drawObj; 169 | DrawingModule.prototype.disableDraw = disableDraw; 170 | 171 | module.exports = DrawingModule; 172 | -------------------------------------------------------------------------------- /src/js/app/text.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | var canvas = global.canvas; 4 | var drawing = new (require('./drawing.js'))(); 5 | 6 | function insertText() { 7 | canvas.defaultCursor = 'crosshair'; 8 | 9 | // Esc key handler 10 | $(document).on("keyup", escHandler); 11 | 12 | canvas.selection = false; 13 | canvas.forEachObject(function(o) { 14 | o.selectable = false; 15 | }); 16 | 17 | canvas.on('mouse:down', function(o){ 18 | // Unregister escape key handler 19 | $(document).off("keyup", escHandler); 20 | 21 | drawing.disableDraw(); 22 | 23 | var pointer = canvas.getPointer(o.e); 24 | var text = new fabric.IText('', { 25 | fontFamily: 'arial', 26 | left: pointer.x, 27 | top: pointer.y 28 | }); 29 | 30 | text.on('editing:exited', function(o){ 31 | $("#toolbar-text").removeClass("toolbar-item-active "); 32 | if ($(this)[0].text.length === 0) { 33 | canvas.remove(text); 34 | } else { 35 | // Delete the event listener 36 | //text.__eventListeners["editing:exited"] = []; 37 | 38 | // Push the canvas state to history 39 | canvas.trigger("object:statechange"); 40 | } 41 | }); 42 | 43 | text.targetFindTolerance = 4; 44 | 45 | canvas.add(text); 46 | canvas.setActiveObject(text); 47 | canvas.defaultCursor = 'auto'; 48 | text.enterEditing(); 49 | }); 50 | } 51 | 52 | function cancelInsert() { 53 | canvas.defaultCursor = 'auto'; 54 | drawing.disableDraw(); 55 | $("#toolbar-text").removeClass("toolbar-item-active "); 56 | } 57 | 58 | // Cancel text insertion 59 | function escHandler(e) { 60 | if (e.keyCode == 27) { 61 | cancelInsert(); 62 | 63 | // Unregister escape key handler 64 | $(document).off("keyup", escHandler); 65 | } 66 | } 67 | 68 | // Return focus to itext if user was editing text 69 | function returnFocus() { 70 | var o = canvas.getActiveObject(); 71 | if (o === undefined || o === null || o.type !== "i-text") { 72 | return; 73 | } 74 | 75 | if (o.hiddenTextarea) { 76 | o.hiddenTextarea.focus(); 77 | } 78 | } 79 | 80 | // Set object style 81 | function setStyle(object, styleName, value) { 82 | object = object || canvas.getActiveObject(); 83 | object[styleName] = value; 84 | object.set({dirty: true}); 85 | } 86 | 87 | // Get object style 88 | function getStyle(object, styleName) { 89 | // Don't allow changing part of the text 90 | /* 91 | if (object.getSelectionStyles && object.isEditing) { 92 | return object.getSelectionStyles()[styleName]; 93 | } else { 94 | return object[styleName]; 95 | } 96 | */ 97 | object = object || canvas.getActiveObject(); 98 | return object[styleName]; 99 | } 100 | 101 | function isItalics(obj) { 102 | return (getStyle(obj, 'fontStyle') || '').indexOf('italic') > -1; 103 | } 104 | 105 | function toggleItalics() { 106 | var button = $("#toolbar-italics"); 107 | var obj = canvas.getActiveObject(); 108 | var italics = !isItalics(obj); 109 | setStyle(obj, 'fontStyle', italics ? 'italic' : 'normal'); 110 | 111 | if (italics) { 112 | button.addClass("toolbar-item-active"); 113 | } else { 114 | button.removeClass("toolbar-item-active"); 115 | } 116 | canvas.renderAll(); 117 | 118 | // Push the canvas state to history 119 | canvas.trigger("object:statechange"); 120 | } 121 | 122 | function isBold(obj) { 123 | return (getStyle(obj, 'fontWeight') || '').indexOf('bold') > -1; 124 | } 125 | 126 | function toggleBold() { 127 | var button = $("#toolbar-bold"); 128 | var obj = canvas.getActiveObject(); 129 | var bold = !isBold(obj); 130 | setStyle(obj, 'fontWeight', bold ? 'bold' : ''); 131 | 132 | if (bold) { 133 | button.addClass("toolbar-item-active"); 134 | } else { 135 | button.removeClass("toolbar-item-active"); 136 | } 137 | canvas.renderAll(); 138 | 139 | // Push the canvas state to history 140 | canvas.trigger("object:statechange"); 141 | } 142 | 143 | function isUnderline(obj) { 144 | return (getStyle(obj, 'underline') || ''); 145 | } 146 | 147 | function toggleUnderline() { 148 | var button = $("#toolbar-underline"); 149 | var obj = canvas.getActiveObject(); 150 | var underlined = !isUnderline(obj); 151 | setStyle(obj, 'underline',underlined); 152 | 153 | if (underlined) { 154 | button.addClass("toolbar-item-active"); 155 | } else { 156 | button.removeClass("toolbar-item-active"); 157 | } 158 | canvas.renderAll(); 159 | 160 | // Push the canvas state to history 161 | canvas.trigger("object:statechange"); 162 | } 163 | 164 | 165 | /* ----- exports ----- */ 166 | 167 | function TextModule() { 168 | if (!(this instanceof TextModule)) return new TextModule(); 169 | } 170 | 171 | TextModule.prototype.isBold = isBold; 172 | TextModule.prototype.isUnderline = isUnderline; 173 | TextModule.prototype.isItalics = isItalics; 174 | TextModule.prototype.toggleBold = toggleBold; 175 | TextModule.prototype.toggleUnderline = toggleUnderline; 176 | TextModule.prototype.toggleItalics = toggleItalics; 177 | TextModule.prototype.insertText = insertText; 178 | TextModule.prototype.cancelInsert = cancelInsert; 179 | TextModule.prototype.returnFocus = returnFocus; 180 | 181 | module.exports = TextModule; 182 | -------------------------------------------------------------------------------- /src/js/lib/jquery.ui.position.min.js: -------------------------------------------------------------------------------- 1 | /*! jQuery UI - v1.11.4 - 2015-03-13 2 | * http://jqueryui.com 3 | * Copyright jQuery Foundation and other contributors; Licensed MIT */ 4 | (function(t){"function"==typeof define&&define.amd?define(["jquery"],t):t(jQuery)})(function(t){return function(){function e(t,e,i){return[parseFloat(t[0])*(p.test(t[0])?e/100:1),parseFloat(t[1])*(p.test(t[1])?i/100:1)]}function i(e,i){return parseInt(t.css(e,i),10)||0}function s(e){var i=e[0];return 9===i.nodeType?{width:e.width(),height:e.height(),offset:{top:0,left:0}}:t.isWindow(i)?{width:e.width(),height:e.height(),offset:{top:e.scrollTop(),left:e.scrollLeft()}}:i.preventDefault?{width:0,height:0,offset:{top:i.pageY,left:i.pageX}}:{width:e.outerWidth(),height:e.outerHeight(),offset:e.offset()}}t.ui=t.ui||{};var n,o,a=Math.max,r=Math.abs,h=Math.round,l=/left|center|right/,c=/top|center|bottom/,u=/[\+\-]\d+(\.[\d]+)?%?/,d=/^\w+/,p=/%$/,f=t.fn.position;t.position={scrollbarWidth:function(){if(void 0!==n)return n;var e,i,s=t("
"),o=s.children()[0];return t("body").append(s),e=o.offsetWidth,s.css("overflow","scroll"),i=o.offsetWidth,e===i&&(i=s[0].clientWidth),s.remove(),n=e-i},getScrollInfo:function(e){var i=e.isWindow||e.isDocument?"":e.element.css("overflow-x"),s=e.isWindow||e.isDocument?"":e.element.css("overflow-y"),n="scroll"===i||"auto"===i&&e.widthi?"left":e>0?"right":"center",vertical:0>o?"top":s>0?"bottom":"middle"};u>g&&g>r(e+i)&&(h.horizontal="center"),d>m&&m>r(s+o)&&(h.vertical="middle"),h.important=a(r(e),r(i))>a(r(s),r(o))?"horizontal":"vertical",n.using.call(this,t,h)}),c.offset(t.extend(P,{using:l}))})},t.ui.position={fit:{left:function(t,e){var i,s=e.within,n=s.isWindow?s.scrollLeft:s.offset.left,o=s.width,r=t.left-e.collisionPosition.marginLeft,h=n-r,l=r+e.collisionWidth-o-n;e.collisionWidth>o?h>0&&0>=l?(i=t.left+h+e.collisionWidth-o-n,t.left+=h-i):t.left=l>0&&0>=h?n:h>l?n+o-e.collisionWidth:n:h>0?t.left+=h:l>0?t.left-=l:t.left=a(t.left-r,t.left)},top:function(t,e){var i,s=e.within,n=s.isWindow?s.scrollTop:s.offset.top,o=e.within.height,r=t.top-e.collisionPosition.marginTop,h=n-r,l=r+e.collisionHeight-o-n;e.collisionHeight>o?h>0&&0>=l?(i=t.top+h+e.collisionHeight-o-n,t.top+=h-i):t.top=l>0&&0>=h?n:h>l?n+o-e.collisionHeight:n:h>0?t.top+=h:l>0?t.top-=l:t.top=a(t.top-r,t.top)}},flip:{left:function(t,e){var i,s,n=e.within,o=n.offset.left+n.scrollLeft,a=n.width,h=n.isWindow?n.scrollLeft:n.offset.left,l=t.left-e.collisionPosition.marginLeft,c=l-h,u=l+e.collisionWidth-a-h,d="left"===e.my[0]?-e.elemWidth:"right"===e.my[0]?e.elemWidth:0,p="left"===e.at[0]?e.targetWidth:"right"===e.at[0]?-e.targetWidth:0,f=-2*e.offset[0];0>c?(i=t.left+d+p+f+e.collisionWidth-a-o,(0>i||r(c)>i)&&(t.left+=d+p+f)):u>0&&(s=t.left-e.collisionPosition.marginLeft+d+p+f-h,(s>0||u>r(s))&&(t.left+=d+p+f))},top:function(t,e){var i,s,n=e.within,o=n.offset.top+n.scrollTop,a=n.height,h=n.isWindow?n.scrollTop:n.offset.top,l=t.top-e.collisionPosition.marginTop,c=l-h,u=l+e.collisionHeight-a-h,d="top"===e.my[1],p=d?-e.elemHeight:"bottom"===e.my[1]?e.elemHeight:0,f="top"===e.at[1]?e.targetHeight:"bottom"===e.at[1]?-e.targetHeight:0,g=-2*e.offset[1];0>c?(s=t.top+p+f+g+e.collisionHeight-a-o,(0>s||r(c)>s)&&(t.top+=p+f+g)):u>0&&(i=t.top-e.collisionPosition.marginTop+p+f+g-h,(i>0||u>r(i))&&(t.top+=p+f+g))}},flipfit:{left:function(){t.ui.position.flip.left.apply(this,arguments),t.ui.position.fit.left.apply(this,arguments)},top:function(){t.ui.position.flip.top.apply(this,arguments),t.ui.position.fit.top.apply(this,arguments)}}},function(){var e,i,s,n,a,r=document.getElementsByTagName("body")[0],h=document.createElement("div");e=document.createElement(r?"div":"body"),s={visibility:"hidden",width:0,height:0,border:0,margin:0,background:"none"},r&&t.extend(s,{position:"absolute",left:"-1000px",top:"-1000px"});for(a in s)e.style[a]=s[a];e.appendChild(h),i=r||document.documentElement,i.insertBefore(e,i.firstChild),h.style.cssText="position: absolute; left: 10.7432222px;",n=t(h).offset().left,o=n>10&&11>n,e.innerHTML="",i.removeChild(e)}()}(),t.ui.position}); -------------------------------------------------------------------------------- /src/scss/_toolbar.scss: -------------------------------------------------------------------------------- 1 | #toolbar { 2 | padding-left: 30px; 3 | padding-right: 30px; 4 | 5 | width: 100%; 6 | height: $toolbar-height; 7 | 8 | border-top: 1px solid $toolbar-border; 9 | border-bottom: 1px solid $toolbar-border; 10 | background-color: $toolbar-background; 11 | overflow: hidden; 12 | z-index: 5; 13 | } 14 | 15 | /* --- Toolbar Icons --- */ 16 | 17 | #toolbar-undo { 18 | background-image: url("../images/toolbar/undo.png"); 19 | } 20 | 21 | #toolbar-redo { 22 | background-image: url("../images/toolbar/redo.png"); 23 | } 24 | 25 | #toolbar-arrow { 26 | background-image: url("../images/toolbar/arrow.png"); 27 | } 28 | 29 | #toolbar-text { 30 | background-image: url("../images/toolbar/insert-text.png"); 31 | } 32 | 33 | #toolbar-shape { 34 | background-image: url("../images/toolbar/shapes.png"); 35 | } 36 | 37 | #toolbar-bold { 38 | background-image: url("../images/toolbar/bold.png"); 39 | } 40 | 41 | #toolbar-italics { 42 | background-image: url("../images/toolbar/italics.png"); 43 | } 44 | 45 | #toolbar-underline { 46 | background-image: url("../images/toolbar/underline.png"); 47 | } 48 | 49 | #toolbar-fill-color { 50 | background-image: url("../images/toolbar/fill-color.png"); 51 | } 52 | 53 | #toolbar-outline-color { 54 | background-image: url("../images/toolbar/line-color.png"); 55 | } 56 | 57 | #toolbar-effects { 58 | background-image: url("../images/toolbar/effects.png"); 59 | } 60 | 61 | #toolbar-arrange { 62 | width: 70px; 63 | line-height: 26px; 64 | padding-left: 5px; 65 | font-weight: 400; 66 | color: #585858; 67 | &:hover { 68 | cursor: default; 69 | } 70 | } 71 | 72 | #toolbar-font-family { 73 | width: 100px; 74 | line-height: 26px; 75 | padding-left: 5px; 76 | font-weight: 400; 77 | color: #585858; 78 | &:hover { 79 | cursor: default; 80 | } 81 | .submenu-item { 82 | margin-left: 10px; 83 | } 84 | } 85 | 86 | /* --- Toolbar --- */ 87 | 88 | .toolbar-separator { 89 | float: left; 90 | border-left: 1px solid $toolbar-border; 91 | width: 8px; 92 | margin-left: 8px; 93 | height: $toolbar-height; 94 | } 95 | 96 | .toolbar-item { 97 | float:left; 98 | width: 26px; 99 | height: $toolbar-button-height; 100 | margin-top: ($toolbar-height - $toolbar-button-height)/2; 101 | margin-bottom: ($toolbar-height - $toolbar-button-height)/2; 102 | background-size: 24px 24px; 103 | background-repeat: no-repeat; 104 | background-position: 1px 1px; 105 | border-radius: 2px; 106 | outline: none; 107 | border: 1px solid transparent; 108 | 109 | &:hover { 110 | border: 1px solid #CCCCCC; 111 | box-shadow: 1px 1px 1px #dadada; 112 | } 113 | 114 | &:active { 115 | border: 1px solid #CCCCCC; 116 | box-shadow: 1px 1px 2px #b7b7b7 inset; 117 | background-color: #e8e8e8; 118 | border: 1px solid #cecece; 119 | } 120 | } 121 | 122 | .toolbar-dropdown { 123 | width: 38px; 124 | } 125 | 126 | .toolbar-input { 127 | width: 48px; 128 | padding-left: 4px; 129 | } 130 | 131 | .toolbar-arrow { 132 | float: right; 133 | margin-top: 11px; 134 | margin-right: 3px; 135 | width: 0; 136 | height: 0; 137 | border-left: 4px solid transparent; 138 | border-right: 4px solid transparent; 139 | border-top: 4px solid #909090; 140 | } 141 | 142 | .toolbar-text-input { 143 | float: left; 144 | width: 28px; 145 | height: 18px; 146 | font-size: 13px; 147 | background-color: transparent; 148 | border: 1px solid transparent; 149 | outline: none; 150 | margin-top: 1px; 151 | 152 | &:focus { 153 | background-color: white; 154 | border: 1px solid #0096FD; 155 | } 156 | } 157 | 158 | .toolbar-item-active { 159 | border: 1px solid #CCCCCC; 160 | box-shadow: 1px 1px 2px #b1b1b1 inset; 161 | background-color: #e9e7e7; 162 | border: 1px solid #CCCCCC; 163 | 164 | &:hover { 165 | border: 1px solid #CCCCCC; 166 | box-shadow: 1px 1px 2px #b1b1b1 inset; 167 | } 168 | 169 | &:active { 170 | border: 1px solid #CCCCCC; 171 | box-shadow: 1px 1px 2px #b1b1b1 inset; 172 | } 173 | } 174 | 175 | /* --- toolbar submenu --- */ 176 | 177 | .toolbar-submenu { 178 | position: absolute; 179 | background-color: white; 180 | width: 205px; 181 | top: -100px; 182 | left: -100px; 183 | z-index: 5; 184 | border: 1px solid #b6b5b5; 185 | color: #5D5D5D; 186 | box-shadow: 1px 1px 2px #bebebe; 187 | max-height: 300px; 188 | overflow: hidden; 189 | } 190 | 191 | .scrolling { 192 | overflow-y: scroll; 193 | 194 | // Always show scrollbar 195 | &::-webkit-scrollbar { 196 | -webkit-appearance: none; 197 | &:vertical { 198 | width: 11px; 199 | } 200 | &:horizontal { 201 | height: 11px; 202 | } 203 | } 204 | &::-webkit-scrollbar-thumb { 205 | border-radius: 8px; 206 | border: 2px solid #FAFAFA; /* should match background, can't be transparent */ 207 | background-color: rgba(0, 0, 0, .5); 208 | } 209 | &::-webkit-scrollbar-track { 210 | background-color: #FAFAFA; 211 | border-radius: 8px; 212 | } 213 | } 214 | 215 | .submenu-item { 216 | width: 100%; 217 | height: 30px; 218 | border: 0px; 219 | font-size: 15px; 220 | line-height: 30px; 221 | border-bottom: 0px solid #979797; 222 | &:hover { 223 | background-color: #EAEAEA; 224 | cursor: pointer; 225 | } 226 | } 227 | 228 | .submenu-icon { 229 | width: 15px; 230 | height: 15px; 231 | padding-left: 12px; 232 | padding-right: 8px; 233 | padding-bottom: 3px; 234 | } 235 | 236 | /* --- effects submenu --- */ 237 | 238 | #effects-box { 239 | margin: 10px; 240 | 241 | .fx-options { 242 | display: none; 243 | } 244 | 245 | .mdl-switch { 246 | margin-top: 10px; 247 | margin-bottom: 10px; 248 | margin-left: 5px; 249 | } 250 | 251 | .slider-container { 252 | margin-bottom: 10px; 253 | } 254 | 255 | .effects-form-label { 256 | margin-left: 25px; 257 | } 258 | 259 | .hex-display { 260 | margin-top: 2px; 261 | margin-left: 25px; 262 | margin-bottom: 5px; 263 | margin-right: -3px; 264 | width: 80px; 265 | height: 27px; 266 | background-color: #f4f4f4; 267 | border:1px solid #c8c8c8; 268 | border-right: 0px; 269 | padding-bottom: 2px; 270 | padding-left: 8px; 271 | color: #6f6d6d; 272 | } 273 | } 274 | 275 | .spectrum-box { 276 | background-color: #ecebeb; 277 | border: 1px solid #c8c5c5; 278 | } 279 | -------------------------------------------------------------------------------- /src/js/app/fetchApi.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | var canvas = global.canvas; 4 | var config = require('./config.js'); 5 | 6 | /* ---- callbacks ---- */ 7 | var toggleSpinner, toggleNoResults, resultsDiv, actionHandler; 8 | 9 | /* ---- search state ---- */ 10 | var keyword = ""; 11 | var outstandingQueries = []; 12 | var numResults = 0; 13 | var numPages = 0; 14 | var currentPage = 0; 15 | var pageSize = 0; 16 | var loading = false; 17 | var urls = {}; 18 | var cachedResults = null; 19 | 20 | function queryNounApi(page, numResults, callback) { 21 | var endpoint = config.icons.host; 22 | 23 | if (cachedResults === null) { 24 | var xhr = $.ajax({ 25 | url: endpoint, 26 | data: { 27 | t: keyword 28 | }, 29 | success: function(response) { 30 | var icons = JSON.parse(response); 31 | var totalResults = icons.length; 32 | 33 | var payload = []; 34 | for (var i = 0; i < totalResults; i++) { 35 | var thisId = icons[i][0]; 36 | var folder = parseInt(thisId/1000); 37 | payload.push({ 38 | id: thisId, 39 | title: icons[i][1], 40 | uploader: icons[i][2], 41 | uploader_url: icons[i][3], 42 | svg: { 43 | url: endpoint + "/svg/" + folder + "/" + thisId + ".svg", 44 | png_thumb: endpoint + "/png/" + folder + "/" + thisId + ".png" 45 | } 46 | }); 47 | } 48 | cachedResults = payload; 49 | var thisPage = payload.slice(0, numResults); 50 | callback(thisPage, thisPage.length, Math.ceil(payload.length/numResults), page, true); 51 | } 52 | }); 53 | outstandingQueries.push(xhr); 54 | } else { 55 | var thisPage = cachedResults.slice((page-1)*numResults,((page-1)*numResults)+numResults); 56 | var allPages = Math.ceil(cachedResults.length/numResults); 57 | callback(thisPage, thisPage.length, allPages, page, true); 58 | } 59 | } 60 | 61 | function queryClipartApi(page, numResults, callback) { 62 | var clipartAPI = "//openclipart.org/search/json/"; 63 | var xhr = $.ajax({ 64 | url: clipartAPI, 65 | jsonp: "callback", 66 | dataType: "jsonp", 67 | data: { 68 | query: keyword, 69 | amount: numResults, 70 | sort: "downloads", 71 | page: page 72 | }, 73 | success: function(response) { 74 | var i = response.info; 75 | callback(response.payload, i.results, i.pages, i.current_page, false); 76 | } 77 | }); 78 | outstandingQueries.push(xhr); 79 | } 80 | 81 | function loadMore(_nouns) { 82 | if (currentPage === numPages) { 83 | return; 84 | } else if (loading === true) { 85 | return; 86 | } 87 | 88 | // Load results 89 | loading = true; 90 | currentPage += 1; 91 | toggleSpinner(true); 92 | 93 | if (_nouns === true) { 94 | queryNounApi(currentPage, pageSize, function(results, _numResults, _pages, _currentPage) { 95 | currentPage = _currentPage; 96 | displayResults(results); 97 | toggleSpinner(false); 98 | loading = false; 99 | }); 100 | } else { 101 | queryClipartApi(currentPage, pageSize, function(results, _numResults, _pages, _currentPage) { 102 | currentPage = _currentPage; 103 | displayResults(results); 104 | toggleSpinner(false); 105 | loading = false; 106 | }); 107 | } 108 | } 109 | 110 | function displayResults(results) { 111 | for (var i = 0; i < results.length ; i++) { 112 | var image = results[i]; 113 | var id = image.id; 114 | urls[id] = image.svg.url; 115 | 116 | var title, source, source_href, user_href; 117 | if ("total_favorites" in image) { 118 | // Openclipart 119 | title = null; 120 | source = "Openclipart"; 121 | source_href = "https://data.daringlogos.com/redirect?url=http://openclipart.org"; 122 | user_href = "https://data.daringlogos.com/redirect?url=http://openclipart.org/user-detail/" + image.uploader; 123 | } else { 124 | // Noun Project 125 | title = image.title; 126 | source = "The Noun Project"; 127 | source_href = "https://data.daringlogos.com/redirect?url=http://thenounproject.com"; 128 | user_href = "https://data.daringlogos.com/redirect?url=http://thenounproject.com" + image.uploader_url; 129 | } 130 | 131 | var attribution = '
'; 132 | if (title !== null && title !== "") { 133 | attribution += "" + title + "

"; 134 | } 135 | attribution += "By "; 136 | attribution += image.uploader + " From " + source + "
 "; 137 | 138 | resultsDiv.append(''); 139 | } 140 | 141 | // Show attribution tooltips 142 | $('.tooltip').tooltipster({ 143 | theme: 'tooltipster-light', 144 | contentAsHTML: true, 145 | animation: 'grow', 146 | delay: 50, 147 | speed: 150, 148 | maxWidth: 250, 149 | hideOnClick: true, 150 | interactive: true, 151 | interactiveTolerance: 350, 152 | onlyOne: true, 153 | position: 'right' 154 | }); 155 | 156 | resultsDiv.off("click.insertimage"); 157 | resultsDiv.on("click.insertimage", ".preview-image", function(e) { 158 | var url = urls[e.currentTarget.id]; 159 | actionHandler(url); 160 | }); 161 | } 162 | 163 | function cancelOutstandingQueries() { 164 | for (var i = 0; i < outstandingQueries.length; i++) { 165 | outstandingQueries[i].abort(); 166 | } 167 | } 168 | 169 | function parseResults(results, _numResults, _numPages, _currentPage, _nouns) { 170 | // Metadata 171 | numResults = _numResults; 172 | numPages = _numPages; 173 | currentPage = _currentPage; 174 | 175 | // Process results 176 | toggleSpinner(false); 177 | if (_numResults === 0) { 178 | toggleNoResults(true); 179 | return; 180 | } 181 | 182 | // Populate previews 183 | displayResults(results); 184 | 185 | // Setup scroll handler 186 | if (_numPages > 1) { 187 | resultsDiv.on("scroll.scrollHandler", function(){ 188 | if($(this).scrollTop() + $(this).innerHeight() >= $(this)[0].scrollHeight){ 189 | loadMore(_nouns); 190 | } 191 | }); 192 | } 193 | } 194 | 195 | function search(_keyword, _toggleSpinner, _toggleNoResults, _resultsDiv, _actionHandler, _clipart) { 196 | keyword = _keyword.trim(); 197 | if (keyword === "") { 198 | return; 199 | } 200 | 201 | // Cancel all outstanding queries 202 | cancelOutstandingQueries(); 203 | outstandingQueries.length = 0; 204 | 205 | // callbacks 206 | toggleSpinner = _toggleSpinner; 207 | toggleNoResults = _toggleNoResults; 208 | resultsDiv = _resultsDiv; 209 | actionHandler = _actionHandler; 210 | 211 | // Clear search results 212 | resultsDiv.off("scroll.scrollHandler"); 213 | resultsDiv.children(".preview-image").remove(); 214 | 215 | // Reset 216 | loading = false; 217 | urls = {}; 218 | 219 | // Query API 220 | toggleSpinner(true); 221 | pageSize = Math.ceil((window.innerHeight - 50) / 100) * 3; 222 | 223 | if (_clipart === true) { 224 | queryClipartApi(1, pageSize, parseResults); 225 | } else { 226 | cachedResults = null; 227 | queryNounApi(1, pageSize, parseResults); 228 | } 229 | 230 | } 231 | 232 | /* ----- exports ----- */ 233 | 234 | function FetchApiModule() { 235 | if (!(this instanceof FetchApiModule)) return new FetchApiModule(); 236 | } 237 | 238 | FetchApiModule.prototype.search = search; 239 | 240 | module.exports = FetchApiModule; 241 | -------------------------------------------------------------------------------- /src/css/lib/normalize.css: -------------------------------------------------------------------------------- 1 | /*! normalize.css v3.0.2 | MIT License | git.io/normalize */ 2 | 3 | /** 4 | * 1. Set default font family to sans-serif. 5 | * 2. Prevent iOS text size adjust after orientation change, without disabling 6 | * user zoom. 7 | */ 8 | 9 | html { 10 | font-family: sans-serif; /* 1 */ 11 | -ms-text-size-adjust: 100%; /* 2 */ 12 | -webkit-text-size-adjust: 100%; /* 2 */ 13 | } 14 | 15 | /** 16 | * Remove default margin. 17 | */ 18 | 19 | body { 20 | margin: 0; 21 | } 22 | 23 | /* HTML5 display definitions 24 | ========================================================================== */ 25 | 26 | /** 27 | * Correct `block` display not defined for any HTML5 element in IE 8/9. 28 | * Correct `block` display not defined for `details` or `summary` in IE 10/11 29 | * and Firefox. 30 | * Correct `block` display not defined for `main` in IE 11. 31 | */ 32 | 33 | article, 34 | aside, 35 | details, 36 | figcaption, 37 | figure, 38 | footer, 39 | header, 40 | hgroup, 41 | main, 42 | menu, 43 | nav, 44 | section, 45 | summary { 46 | display: block; 47 | } 48 | 49 | /** 50 | * 1. Correct `inline-block` display not defined in IE 8/9. 51 | * 2. Normalize vertical alignment of `progress` in Chrome, Firefox, and Opera. 52 | */ 53 | 54 | audio, 55 | canvas, 56 | progress, 57 | video { 58 | display: inline-block; /* 1 */ 59 | vertical-align: baseline; /* 2 */ 60 | } 61 | 62 | /** 63 | * Prevent modern browsers from displaying `audio` without controls. 64 | * Remove excess height in iOS 5 devices. 65 | */ 66 | 67 | audio:not([controls]) { 68 | display: none; 69 | height: 0; 70 | } 71 | 72 | /** 73 | * Address `[hidden]` styling not present in IE 8/9/10. 74 | * Hide the `template` element in IE 8/9/11, Safari, and Firefox < 22. 75 | */ 76 | 77 | [hidden], 78 | template { 79 | display: none; 80 | } 81 | 82 | /* Links 83 | ========================================================================== */ 84 | 85 | /** 86 | * Remove the gray background color from active links in IE 10. 87 | */ 88 | 89 | a { 90 | background-color: transparent; 91 | } 92 | 93 | /** 94 | * Improve readability when focused and also mouse hovered in all browsers. 95 | */ 96 | 97 | a:active, 98 | a:hover { 99 | outline: 0; 100 | } 101 | 102 | /* Text-level semantics 103 | ========================================================================== */ 104 | 105 | /** 106 | * Address styling not present in IE 8/9/10/11, Safari, and Chrome. 107 | */ 108 | 109 | abbr[title] { 110 | border-bottom: 1px dotted; 111 | } 112 | 113 | /** 114 | * Address style set to `bolder` in Firefox 4+, Safari, and Chrome. 115 | */ 116 | 117 | b, 118 | strong { 119 | font-weight: bold; 120 | } 121 | 122 | /** 123 | * Address styling not present in Safari and Chrome. 124 | */ 125 | 126 | dfn { 127 | font-style: italic; 128 | } 129 | 130 | /** 131 | * Address variable `h1` font-size and margin within `section` and `article` 132 | * contexts in Firefox 4+, Safari, and Chrome. 133 | */ 134 | 135 | h1 { 136 | font-size: 2em; 137 | margin: 0.67em 0; 138 | } 139 | 140 | /** 141 | * Address styling not present in IE 8/9. 142 | */ 143 | 144 | mark { 145 | background: #ff0; 146 | color: #000; 147 | } 148 | 149 | /** 150 | * Address inconsistent and variable font size in all browsers. 151 | */ 152 | 153 | small { 154 | font-size: 80%; 155 | } 156 | 157 | /** 158 | * Prevent `sub` and `sup` affecting `line-height` in all browsers. 159 | */ 160 | 161 | sub, 162 | sup { 163 | font-size: 75%; 164 | line-height: 0; 165 | position: relative; 166 | vertical-align: baseline; 167 | } 168 | 169 | sup { 170 | top: -0.5em; 171 | } 172 | 173 | sub { 174 | bottom: -0.25em; 175 | } 176 | 177 | /* Embedded content 178 | ========================================================================== */ 179 | 180 | /** 181 | * Remove border when inside `a` element in IE 8/9/10. 182 | */ 183 | 184 | img { 185 | border: 0; 186 | } 187 | 188 | /** 189 | * Correct overflow not hidden in IE 9/10/11. 190 | */ 191 | 192 | svg:not(:root) { 193 | overflow: hidden; 194 | } 195 | 196 | /* Grouping content 197 | ========================================================================== */ 198 | 199 | /** 200 | * Address margin not present in IE 8/9 and Safari. 201 | */ 202 | 203 | figure { 204 | margin: 1em 40px; 205 | } 206 | 207 | /** 208 | * Address differences between Firefox and other browsers. 209 | */ 210 | 211 | hr { 212 | -moz-box-sizing: content-box; 213 | box-sizing: content-box; 214 | height: 0; 215 | } 216 | 217 | /** 218 | * Contain overflow in all browsers. 219 | */ 220 | 221 | pre { 222 | overflow: auto; 223 | } 224 | 225 | /** 226 | * Address odd `em`-unit font size rendering in all browsers. 227 | */ 228 | 229 | code, 230 | kbd, 231 | pre, 232 | samp { 233 | font-family: monospace, monospace; 234 | font-size: 1em; 235 | } 236 | 237 | /* Forms 238 | ========================================================================== */ 239 | 240 | /** 241 | * Known limitation: by default, Chrome and Safari on OS X allow very limited 242 | * styling of `select`, unless a `border` property is set. 243 | */ 244 | 245 | /** 246 | * 1. Correct color not being inherited. 247 | * Known issue: affects color of disabled elements. 248 | * 2. Correct font properties not being inherited. 249 | * 3. Address margins set differently in Firefox 4+, Safari, and Chrome. 250 | */ 251 | 252 | button, 253 | input, 254 | optgroup, 255 | select, 256 | textarea { 257 | color: inherit; /* 1 */ 258 | font: inherit; /* 2 */ 259 | margin: 0; /* 3 */ 260 | } 261 | 262 | /** 263 | * Address `overflow` set to `hidden` in IE 8/9/10/11. 264 | */ 265 | 266 | button { 267 | overflow: visible; 268 | } 269 | 270 | /** 271 | * Address inconsistent `text-transform` inheritance for `button` and `select`. 272 | * All other form control elements do not inherit `text-transform` values. 273 | * Correct `button` style inheritance in Firefox, IE 8/9/10/11, and Opera. 274 | * Correct `select` style inheritance in Firefox. 275 | */ 276 | 277 | button, 278 | select { 279 | text-transform: none; 280 | } 281 | 282 | /** 283 | * 1. Avoid the WebKit bug in Android 4.0.* where (2) destroys native `audio` 284 | * and `video` controls. 285 | * 2. Correct inability to style clickable `input` types in iOS. 286 | * 3. Improve usability and consistency of cursor style between image-type 287 | * `input` and others. 288 | */ 289 | 290 | button, 291 | html input[type="button"], /* 1 */ 292 | input[type="reset"], 293 | input[type="submit"] { 294 | -webkit-appearance: button; /* 2 */ 295 | cursor: pointer; /* 3 */ 296 | } 297 | 298 | /** 299 | * Re-set default cursor for disabled elements. 300 | */ 301 | 302 | button[disabled], 303 | html input[disabled] { 304 | cursor: default; 305 | } 306 | 307 | /** 308 | * Remove inner padding and border in Firefox 4+. 309 | */ 310 | 311 | button::-moz-focus-inner, 312 | input::-moz-focus-inner { 313 | border: 0; 314 | padding: 0; 315 | } 316 | 317 | /** 318 | * Address Firefox 4+ setting `line-height` on `input` using `!important` in 319 | * the UA stylesheet. 320 | */ 321 | 322 | input { 323 | line-height: normal; 324 | } 325 | 326 | /** 327 | * It's recommended that you don't attempt to style these elements. 328 | * Firefox's implementation doesn't respect box-sizing, padding, or width. 329 | * 330 | * 1. Address box sizing set to `content-box` in IE 8/9/10. 331 | * 2. Remove excess padding in IE 8/9/10. 332 | */ 333 | 334 | input[type="checkbox"], 335 | input[type="radio"] { 336 | box-sizing: border-box; /* 1 */ 337 | padding: 0; /* 2 */ 338 | } 339 | 340 | /** 341 | * Fix the cursor style for Chrome's increment/decrement buttons. For certain 342 | * `font-size` values of the `input`, it causes the cursor style of the 343 | * decrement button to change from `default` to `text`. 344 | */ 345 | 346 | input[type="number"]::-webkit-inner-spin-button, 347 | input[type="number"]::-webkit-outer-spin-button { 348 | height: auto; 349 | } 350 | 351 | /** 352 | * 1. Address `appearance` set to `searchfield` in Safari and Chrome. 353 | * 2. Address `box-sizing` set to `border-box` in Safari and Chrome 354 | * (include `-moz` to future-proof). 355 | */ 356 | 357 | input[type="search"] { 358 | -webkit-appearance: textfield; /* 1 */ 359 | -moz-box-sizing: content-box; 360 | -webkit-box-sizing: content-box; /* 2 */ 361 | box-sizing: content-box; 362 | } 363 | 364 | /** 365 | * Remove inner padding and search cancel button in Safari and Chrome on OS X. 366 | * Safari (but not Chrome) clips the cancel button when the search input has 367 | * padding (and `textfield` appearance). 368 | */ 369 | 370 | input[type="search"]::-webkit-search-cancel-button, 371 | input[type="search"]::-webkit-search-decoration { 372 | -webkit-appearance: none; 373 | } 374 | 375 | /** 376 | * Define consistent border, margin, and padding. 377 | */ 378 | 379 | fieldset { 380 | border: 1px solid #c0c0c0; 381 | margin: 0 2px; 382 | padding: 0.35em 0.625em 0.75em; 383 | } 384 | 385 | /** 386 | * 1. Correct `color` not being inherited in IE 8/9/10/11. 387 | * 2. Remove padding so people aren't caught out if they zero out fieldsets. 388 | */ 389 | 390 | legend { 391 | border: 0; /* 1 */ 392 | padding: 0; /* 2 */ 393 | } 394 | 395 | /** 396 | * Remove default vertical scrollbar in IE 8/9/10/11. 397 | */ 398 | 399 | textarea { 400 | overflow: auto; 401 | } 402 | 403 | /** 404 | * Don't inherit the `font-weight` (applied by a rule above). 405 | * NOTE: the default cannot safely be changed in Chrome and Safari on OS X. 406 | */ 407 | 408 | optgroup { 409 | font-weight: bold; 410 | } 411 | 412 | /* Tables 413 | ========================================================================== */ 414 | 415 | /** 416 | * Remove most spacing between table cells. 417 | */ 418 | 419 | table { 420 | border-collapse: collapse; 421 | border-spacing: 0; 422 | } 423 | 424 | td, 425 | th { 426 | padding: 0; 427 | } 428 | -------------------------------------------------------------------------------- /src/css/lib/tooltipster.css: -------------------------------------------------------------------------------- 1 | /* This is the default Tooltipster theme (feel free to modify or duplicate and create multiple themes!): */ 2 | .tooltipster-default { 3 | border-radius: 5px; 4 | border: 2px solid #000; 5 | background: #4c4c4c; 6 | color: #fff; 7 | } 8 | 9 | /* Use this next selector to style things like font-size and line-height: */ 10 | .tooltipster-default .tooltipster-content { 11 | font-family: Arial, sans-serif; 12 | font-size: 14px; 13 | line-height: 16px; 14 | padding: 8px 10px; 15 | overflow: hidden; 16 | } 17 | 18 | /* This next selector defines the color of the border on the outside of the arrow. This will automatically match the color and size of the border set on the main tooltip styles. Set display: none; if you would like a border around the tooltip but no border around the arrow */ 19 | .tooltipster-default .tooltipster-arrow .tooltipster-arrow-border { 20 | /* border-color: ... !important; */ 21 | } 22 | 23 | 24 | /* If you're using the icon option, use this next selector to style them */ 25 | .tooltipster-icon { 26 | cursor: help; 27 | margin-left: 4px; 28 | } 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | /* This is the base styling required to make all Tooltipsters work */ 38 | .tooltipster-base { 39 | padding: 0; 40 | font-size: 0; 41 | line-height: 0; 42 | position: absolute; 43 | left: 0; 44 | top: 0; 45 | z-index: 9999999; 46 | pointer-events: none; 47 | width: auto; 48 | overflow: visible; 49 | } 50 | .tooltipster-base .tooltipster-content { 51 | overflow: hidden; 52 | } 53 | 54 | 55 | /* These next classes handle the styles for the little arrow attached to the tooltip. By default, the arrow will inherit the same colors and border as what is set on the main tooltip itself. */ 56 | .tooltipster-arrow { 57 | display: block; 58 | text-align: center; 59 | width: 100%; 60 | height: 100%; 61 | position: absolute; 62 | top: 0; 63 | left: 0; 64 | z-index: -1; 65 | } 66 | .tooltipster-arrow span, .tooltipster-arrow-border { 67 | display: block; 68 | width: 0; 69 | height: 0; 70 | position: absolute; 71 | } 72 | .tooltipster-arrow-top span, .tooltipster-arrow-top-right span, .tooltipster-arrow-top-left span { 73 | border-left: 8px solid transparent !important; 74 | border-right: 8px solid transparent !important; 75 | border-top: 8px solid; 76 | bottom: -7px; 77 | } 78 | .tooltipster-arrow-top .tooltipster-arrow-border, .tooltipster-arrow-top-right .tooltipster-arrow-border, .tooltipster-arrow-top-left .tooltipster-arrow-border { 79 | border-left: 9px solid transparent !important; 80 | border-right: 9px solid transparent !important; 81 | border-top: 9px solid; 82 | bottom: -7px; 83 | } 84 | 85 | .tooltipster-arrow-bottom span, .tooltipster-arrow-bottom-right span, .tooltipster-arrow-bottom-left span { 86 | border-left: 8px solid transparent !important; 87 | border-right: 8px solid transparent !important; 88 | border-bottom: 8px solid; 89 | top: -7px; 90 | } 91 | .tooltipster-arrow-bottom .tooltipster-arrow-border, .tooltipster-arrow-bottom-right .tooltipster-arrow-border, .tooltipster-arrow-bottom-left .tooltipster-arrow-border { 92 | border-left: 9px solid transparent !important; 93 | border-right: 9px solid transparent !important; 94 | border-bottom: 9px solid; 95 | top: -7px; 96 | } 97 | .tooltipster-arrow-top span, .tooltipster-arrow-top .tooltipster-arrow-border, .tooltipster-arrow-bottom span, .tooltipster-arrow-bottom .tooltipster-arrow-border { 98 | left: 0; 99 | right: 0; 100 | margin: 0 auto; 101 | } 102 | .tooltipster-arrow-top-left span, .tooltipster-arrow-bottom-left span { 103 | left: 6px; 104 | } 105 | .tooltipster-arrow-top-left .tooltipster-arrow-border, .tooltipster-arrow-bottom-left .tooltipster-arrow-border { 106 | left: 5px; 107 | } 108 | .tooltipster-arrow-top-right span, .tooltipster-arrow-bottom-right span { 109 | right: 6px; 110 | } 111 | .tooltipster-arrow-top-right .tooltipster-arrow-border, .tooltipster-arrow-bottom-right .tooltipster-arrow-border { 112 | right: 5px; 113 | } 114 | .tooltipster-arrow-left span, .tooltipster-arrow-left .tooltipster-arrow-border { 115 | border-top: 8px solid transparent !important; 116 | border-bottom: 8px solid transparent !important; 117 | border-left: 8px solid; 118 | top: 50%; 119 | margin-top: -7px; 120 | right: -7px; 121 | } 122 | .tooltipster-arrow-left .tooltipster-arrow-border { 123 | border-top: 9px solid transparent !important; 124 | border-bottom: 9px solid transparent !important; 125 | border-left: 9px solid; 126 | margin-top: -8px; 127 | } 128 | .tooltipster-arrow-right span, .tooltipster-arrow-right .tooltipster-arrow-border { 129 | border-top: 8px solid transparent !important; 130 | border-bottom: 8px solid transparent !important; 131 | border-right: 8px solid; 132 | top: 50%; 133 | margin-top: -7px; 134 | left: -7px; 135 | } 136 | .tooltipster-arrow-right .tooltipster-arrow-border { 137 | border-top: 9px solid transparent !important; 138 | border-bottom: 9px solid transparent !important; 139 | border-right: 9px solid; 140 | margin-top: -8px; 141 | } 142 | 143 | 144 | /* Some CSS magic for the awesome animations - feel free to make your own custom animations and reference it in your Tooltipster settings! */ 145 | 146 | .tooltipster-fade { 147 | opacity: 0; 148 | -webkit-transition-property: opacity; 149 | -moz-transition-property: opacity; 150 | -o-transition-property: opacity; 151 | -ms-transition-property: opacity; 152 | transition-property: opacity; 153 | } 154 | .tooltipster-fade-show { 155 | opacity: 1; 156 | } 157 | 158 | .tooltipster-grow { 159 | -webkit-transform: scale(0,0); 160 | -moz-transform: scale(0,0); 161 | -o-transform: scale(0,0); 162 | -ms-transform: scale(0,0); 163 | transform: scale(0,0); 164 | -webkit-transition-property: -webkit-transform; 165 | -moz-transition-property: -moz-transform; 166 | -o-transition-property: -o-transform; 167 | -ms-transition-property: -ms-transform; 168 | transition-property: transform; 169 | -webkit-backface-visibility: hidden; 170 | } 171 | .tooltipster-grow-show { 172 | -webkit-transform: scale(1,1); 173 | -moz-transform: scale(1,1); 174 | -o-transform: scale(1,1); 175 | -ms-transform: scale(1,1); 176 | transform: scale(1,1); 177 | -webkit-transition-timing-function: cubic-bezier(0.175, 0.885, 0.320, 1); 178 | -webkit-transition-timing-function: cubic-bezier(0.175, 0.885, 0.320, 1.15); 179 | -moz-transition-timing-function: cubic-bezier(0.175, 0.885, 0.320, 1.15); 180 | -ms-transition-timing-function: cubic-bezier(0.175, 0.885, 0.320, 1.15); 181 | -o-transition-timing-function: cubic-bezier(0.175, 0.885, 0.320, 1.15); 182 | transition-timing-function: cubic-bezier(0.175, 0.885, 0.320, 1.15); 183 | } 184 | 185 | .tooltipster-swing { 186 | opacity: 0; 187 | -webkit-transform: rotateZ(4deg); 188 | -moz-transform: rotateZ(4deg); 189 | -o-transform: rotateZ(4deg); 190 | -ms-transform: rotateZ(4deg); 191 | transform: rotateZ(4deg); 192 | -webkit-transition-property: -webkit-transform, opacity; 193 | -moz-transition-property: -moz-transform; 194 | -o-transition-property: -o-transform; 195 | -ms-transition-property: -ms-transform; 196 | transition-property: transform; 197 | } 198 | .tooltipster-swing-show { 199 | opacity: 1; 200 | -webkit-transform: rotateZ(0deg); 201 | -moz-transform: rotateZ(0deg); 202 | -o-transform: rotateZ(0deg); 203 | -ms-transform: rotateZ(0deg); 204 | transform: rotateZ(0deg); 205 | -webkit-transition-timing-function: cubic-bezier(0.230, 0.635, 0.495, 1); 206 | -webkit-transition-timing-function: cubic-bezier(0.230, 0.635, 0.495, 2.4); 207 | -moz-transition-timing-function: cubic-bezier(0.230, 0.635, 0.495, 2.4); 208 | -ms-transition-timing-function: cubic-bezier(0.230, 0.635, 0.495, 2.4); 209 | -o-transition-timing-function: cubic-bezier(0.230, 0.635, 0.495, 2.4); 210 | transition-timing-function: cubic-bezier(0.230, 0.635, 0.495, 2.4); 211 | } 212 | 213 | .tooltipster-fall { 214 | top: 0; 215 | -webkit-transition-property: top; 216 | -moz-transition-property: top; 217 | -o-transition-property: top; 218 | -ms-transition-property: top; 219 | transition-property: top; 220 | -webkit-transition-timing-function: cubic-bezier(0.175, 0.885, 0.320, 1); 221 | -webkit-transition-timing-function: cubic-bezier(0.175, 0.885, 0.320, 1.15); 222 | -moz-transition-timing-function: cubic-bezier(0.175, 0.885, 0.320, 1.15); 223 | -ms-transition-timing-function: cubic-bezier(0.175, 0.885, 0.320, 1.15); 224 | -o-transition-timing-function: cubic-bezier(0.175, 0.885, 0.320, 1.15); 225 | transition-timing-function: cubic-bezier(0.175, 0.885, 0.320, 1.15); 226 | } 227 | .tooltipster-fall-show { 228 | } 229 | .tooltipster-fall.tooltipster-dying { 230 | -webkit-transition-property: all; 231 | -moz-transition-property: all; 232 | -o-transition-property: all; 233 | -ms-transition-property: all; 234 | transition-property: all; 235 | top: 0px !important; 236 | opacity: 0; 237 | } 238 | 239 | .tooltipster-slide { 240 | left: -40px; 241 | -webkit-transition-property: left; 242 | -moz-transition-property: left; 243 | -o-transition-property: left; 244 | -ms-transition-property: left; 245 | transition-property: left; 246 | -webkit-transition-timing-function: cubic-bezier(0.175, 0.885, 0.320, 1); 247 | -webkit-transition-timing-function: cubic-bezier(0.175, 0.885, 0.320, 1.15); 248 | -moz-transition-timing-function: cubic-bezier(0.175, 0.885, 0.320, 1.15); 249 | -ms-transition-timing-function: cubic-bezier(0.175, 0.885, 0.320, 1.15); 250 | -o-transition-timing-function: cubic-bezier(0.175, 0.885, 0.320, 1.15); 251 | transition-timing-function: cubic-bezier(0.175, 0.885, 0.320, 1.15); 252 | } 253 | .tooltipster-slide.tooltipster-slide-show { 254 | } 255 | .tooltipster-slide.tooltipster-dying { 256 | -webkit-transition-property: all; 257 | -moz-transition-property: all; 258 | -o-transition-property: all; 259 | -ms-transition-property: all; 260 | transition-property: all; 261 | left: 0px !important; 262 | opacity: 0; 263 | } 264 | 265 | 266 | /* CSS transition for when contenting is changing in a tooltip that is still open. The only properties that will NOT transition are: width, height, top, and left */ 267 | .tooltipster-content-changing { 268 | opacity: 0.5; 269 | -webkit-transform: scale(1.1, 1.1); 270 | -moz-transform: scale(1.1, 1.1); 271 | -o-transform: scale(1.1, 1.1); 272 | -ms-transform: scale(1.1, 1.1); 273 | transform: scale(1.1, 1.1); 274 | } 275 | 276 | 277 | /* Light theme */ 278 | .tooltipster-light { 279 | border-radius: 5px; 280 | border: 1px solid #cccccc; 281 | background: #ededed; 282 | color: #666666; 283 | } 284 | .tooltipster-light .tooltipster-content { 285 | font-family: Arial, sans-serif; 286 | font-size: 14px; 287 | line-height: 16px; 288 | padding: 8px 10px; 289 | } 290 | 291 | /* Custom theme */ 292 | .tooltipster-daring { 293 | border-radius: 0px; 294 | border: 3px solid #8B8BDA; 295 | background: #fff; 296 | color: #2c2c2c; 297 | } 298 | .tooltipster-daring .tooltipster-content { 299 | font-family: 'Roboto','Helvetica','Arial',sans-serif; 300 | font-size: 14px; 301 | line-height: 16px; 302 | padding: 8px 10px; 303 | } 304 | .tooltipster-daring .tooltipster-arrow-right span { 305 | border-top: 20px solid transparent !important; 306 | border-bottom: 20px solid transparent !important; 307 | border-right: 20px solid; 308 | top: 50%; 309 | margin-top: -19px; 310 | left: -19px; 311 | } 312 | -------------------------------------------------------------------------------- /src/js/app/page.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | var canvas = global.canvas; 4 | var utils = new (require('./fabricUtils.js'))(); 5 | 6 | function noBackspace() { 7 | // Prevent the backspace key from navigating back 8 | $(document).off('keydown').on('keydown', function (event) { 9 | var doPrevent = false; 10 | if (event.keyCode === 8) { 11 | var d = event.srcElement || event.target; 12 | if ((d.tagName.toUpperCase() === 'INPUT' && 13 | ( 14 | d.type.toUpperCase() === 'TEXT' || 15 | d.type.toUpperCase() === 'PASSWORD' || 16 | d.type.toUpperCase() === 'FILE' || 17 | d.type.toUpperCase() === 'EMAIL' || 18 | d.type.toUpperCase() === 'SEARCH' || 19 | d.type.toUpperCase() === 'TEL' || 20 | d.type.toUpperCase() === 'DATE' ) 21 | ) || 22 | d.tagName.toUpperCase() === 'TEXTAREA') { 23 | doPrevent = d.readOnly || d.disabled; 24 | } 25 | else { 26 | doPrevent = true; 27 | } 28 | } 29 | 30 | if (doPrevent) { 31 | event.preventDefault(); 32 | } 33 | }); 34 | } 35 | 36 | function arrowKeys() { 37 | $(document).on("keydown", function(evt) { 38 | // Block the functionality if user is not on canvas 39 | // This is terrible 40 | var active = $(document.activeElement); 41 | if (active.is('input,textarea,text,password,file,email,search,tel,date')) { 42 | return; 43 | } else if (document.activeElement.tagName !== 'BODY') { 44 | return; 45 | } 46 | 47 | evt = evt || window.event; 48 | var movementDelta = 2; 49 | if (evt.shiftKey) { 50 | movementDelta = 10; 51 | } 52 | 53 | var activeObject = canvas.getActiveObject(); 54 | 55 | var a; 56 | if (evt.keyCode === 37) { 57 | evt.preventDefault(); // Prevent the default action 58 | if (activeObject) { 59 | a = activeObject.get('left') - movementDelta; 60 | activeObject.set('left', a); 61 | } 62 | } else if (evt.keyCode === 39) { 63 | evt.preventDefault(); // Prevent the default action 64 | if (activeObject) { 65 | a = activeObject.get('left') + movementDelta; 66 | activeObject.set('left', a); 67 | } 68 | } else if (evt.keyCode === 38) { 69 | evt.preventDefault(); // Prevent the default action 70 | if (activeObject) { 71 | a = activeObject.get('top') - movementDelta; 72 | activeObject.set('top', a); 73 | } 74 | } else if (evt.keyCode === 40) { 75 | evt.preventDefault(); // Prevent the default action 76 | if (activeObject) { 77 | a = activeObject.get('top') + movementDelta; 78 | activeObject.set('top', a); 79 | } 80 | } 81 | 82 | if (activeObject) { 83 | activeObject.setCoords(); 84 | canvas.renderAll(); 85 | } 86 | }); 87 | } 88 | 89 | function showPreview() { 90 | var preview = $("#preview"); 91 | 92 | // Get a cropped PNG 93 | var bounds = utils.getImageBounds(true); 94 | var url = canvas.toDataURL({ 95 | format: 'png', 96 | left: bounds.left, 97 | top: bounds.top, 98 | width: bounds.width, 99 | height: bounds.height 100 | }); 101 | $("#dynamic-preview").remove(); 102 | var dynamic = preview.append(""); 103 | var shirt = $("#preview-image"); 104 | var design = $("#dynamic-preview"); 105 | 106 | // Set width of image so that it fits in the tshirt 107 | design.width("18%"); 108 | 109 | // Show shirt 110 | preview.removeClass("noshow"); 111 | 112 | // Set image coordinates 113 | var shirtTop = shirt.position().top + parseInt(shirt.css("padding-top")); 114 | var shirtLeft = shirt.position().left; 115 | var shirtWidth = shirt.width(); 116 | var shirtHeight = shirt.height(); 117 | 118 | var top = shirtTop + Math.floor(shirtHeight * 0.32); 119 | var left = shirtLeft + Math.floor(shirtWidth * 0.32); 120 | design.css({top: top, left: left}); 121 | 122 | // Set event listener 123 | $(document).on("click.preview", function(event) { 124 | preview.addClass("noshow"); 125 | $(document).off("click.preview"); 126 | }); 127 | } 128 | 129 | /* --- Color Pickers --- */ 130 | 131 | var changedFillColor; 132 | var changedOutlineColor; 133 | 134 | function handleFillColorChangeEvent(color) { 135 | if (canvas.getActiveObject() === null || canvas.getActiveObject() === undefined) { 136 | // no object selected 137 | return; 138 | } 139 | 140 | var hex = color; 141 | if (typeof(hex) === "object") { 142 | hex = color.toHexString(); 143 | } 144 | 145 | if (hex === utils.getFillColor()) { 146 | // same color 147 | return; 148 | } 149 | 150 | utils.setFillColor(hex); 151 | canvas.renderAll(); 152 | changedFillColor = true; 153 | } 154 | 155 | function fillColorPicker() { 156 | $("#toolbar-fill-color").spectrum({ 157 | preferredFormat: "hex", 158 | showInput: true, 159 | color: 'white', 160 | showButtons: false, 161 | clickoutFiresChange: false, 162 | show: function(color) { 163 | changedFillColor = false; 164 | }, 165 | hide: function(color) { 166 | if (changedFillColor === true) { 167 | // Push the canvas state to history 168 | canvas.trigger("object:statechange"); 169 | } 170 | $("#toolbar-fill-color").removeClass("toolbar-item-active"); 171 | $(".mdl-tooltip").removeClass("noshow"); 172 | } 173 | }); 174 | 175 | $("#toolbar-fill-color").off("dragstop.spectrum"); 176 | $("#toolbar-fill-color").on("dragstop.spectrum", function(e, color) { 177 | handleFillColorChangeEvent(color); 178 | return false; 179 | }); 180 | 181 | $("#toolbar-fill-color").spectrum("container").find(".sp-input").on('keydown', function(evt) { 182 | var key = evt.keyCode || evt.which; 183 | if (key === 13) { 184 | handleFillColorChangeEvent($(this).val()); 185 | } 186 | }); 187 | } 188 | 189 | function handleOutlineColorChangeEvent(color) { 190 | if (canvas.getActiveObject() === null || canvas.getActiveObject() === undefined) { 191 | // no object selected 192 | return; 193 | } 194 | 195 | if (color === null) { 196 | // Transparent outline color 197 | utils.setOutlineColor(null); 198 | canvas.renderAll(); 199 | changedOutlineColor = true; 200 | return; 201 | } 202 | 203 | var hex = color; 204 | if (typeof(hex) === "object") { 205 | hex = color.toHexString(); 206 | } 207 | 208 | if (hex === utils.getOutlineColor()) { 209 | // same color 210 | return; 211 | } 212 | 213 | utils.setOutlineColor(hex); 214 | canvas.renderAll(); 215 | changedOutlineColor = true; 216 | } 217 | 218 | function outlineColorPicker() { 219 | $("#toolbar-outline-color").spectrum({ 220 | preferredFormat: "hex", 221 | showInput: true, 222 | color: 'white', 223 | showButtons: false, 224 | clickoutFiresChange: false, 225 | show: function(color) { 226 | changedOutlineColor = false; 227 | }, 228 | hide: function(color) { 229 | if (changedOutlineColor === true) { 230 | // Push the canvas state to history 231 | canvas.trigger("object:statechange"); 232 | } 233 | $("#toolbar-outline-color").removeClass("toolbar-item-active"); 234 | $(".mdl-tooltip").removeClass("noshow"); 235 | }, 236 | move: function(color) { 237 | if (color === null) { 238 | // User selected transparent option 239 | handleOutlineColorChangeEvent(color); 240 | } 241 | }, 242 | allowEmpty:true 243 | }); 244 | 245 | $("#toolbar-outline-color").off("dragstop.spectrum"); 246 | $("#toolbar-outline-color").on("dragstop.spectrum", function(e, color) { 247 | handleOutlineColorChangeEvent(color); 248 | return false; 249 | }); 250 | 251 | $("#toolbar-outline-color").spectrum("container").find(".sp-input").on('keydown', function(evt) { 252 | var key = evt.keyCode || evt.which; 253 | if (key === 13) { 254 | handleOutlineColorChangeEvent($(this).val()); 255 | } 256 | }); 257 | } 258 | 259 | /* --- shadow color pickers --- */ 260 | 261 | function shadowColorPicker() { 262 | $("#shadow-color-picker").spectrum({ 263 | showAlpha: true, 264 | preferredFormat: "hex", 265 | showInput: true, 266 | color: '#000000', 267 | replacerClassName: 'spectrum-box', 268 | showButtons: false, 269 | clickoutFiresChange: false 270 | }); 271 | 272 | $("#shadow-color-picker").off("dragstop.spectrum"); 273 | $("#shadow-color-picker").on("dragstop.spectrum", function(e, color) { 274 | var hex = color.toHexString(); 275 | var rgba = color.toRgbString(); 276 | $("#shadow-color-hex").val(hex); 277 | utils.changeShadowColor(rgba); 278 | return false; 279 | }); 280 | 281 | $("#shadow-color-picker").spectrum("container").find(".sp-input").on('keydown', function(evt) { 282 | var key = evt.keyCode || evt.which; 283 | if (key === 13) { 284 | $("#shadow-color-hex").val($(this).val()); 285 | utils.changeShadowColor($(this).val()); 286 | } 287 | }); 288 | } 289 | 290 | function glowColorPicker() { 291 | $("#glow-color-picker").spectrum({ 292 | preferredFormat: "hex", 293 | showInput: true, 294 | color: '#000000', 295 | replacerClassName: 'spectrum-box', 296 | showButtons: false, 297 | clickoutFiresChange: false 298 | }); 299 | 300 | $("#glow-color-picker").off("dragstop.spectrum"); 301 | $("#glow-color-picker").on("dragstop.spectrum", function(e, color) { 302 | var hex = color.toHexString(); 303 | $("#glow-color-hex").val(hex); 304 | utils.changeShadowColor(hex); 305 | return false; 306 | }); 307 | 308 | $("#glow-color-picker").spectrum("container").find(".sp-input").on('keydown', function(evt) { 309 | var key = evt.keyCode || evt.which; 310 | if (key === 13) { 311 | $("#glow-color-hex").val($(this).val()); 312 | utils.changeShadowColor($(this).val()); 313 | } 314 | }); 315 | } 316 | 317 | 318 | /* --- Sidebar --- */ 319 | 320 | // Close an open panel with the ESC key 321 | function escHandler(e) { 322 | if (e.keyCode == 27) { 323 | var open = $("div.sidebar-item-selected"); 324 | closePanel(open, true); 325 | 326 | // Unregister escape key handler 327 | $(document).off("keyup", escHandler); 328 | } 329 | } 330 | 331 | function openPanel(button, animate) { 332 | // Register escape key handler 333 | $(document).on("keyup", escHandler); 334 | 335 | // Change button style 336 | button.addClass("sidebar-item-selected"); 337 | $('.inactive', button).addClass("noshow"); 338 | $('.active', button).removeClass("noshow"); 339 | 340 | // Swap panels 341 | var pname = button.attr("id").split("-")[1]; 342 | $('#drawer .slideout-body').addClass("noshow"); 343 | $('#drawer #' + pname + "-panel").removeClass("noshow"); 344 | 345 | // Open panel 346 | if (animate) { 347 | if ($("#drawer.is-visible").length === 0) { 348 | $(".mdl-layout__drawer-button").trigger("click"); 349 | } 350 | } 351 | 352 | // Set artwork search results height 353 | // so that scrolling handler works correctly 354 | fitArtworkResultsHeight(); 355 | 356 | // Register click event handler 357 | $(document).on("click.menu", function(event) { 358 | if (!( $(event.target).closest($("#drawer")).length )) { 359 | if ($(event.target)[0].id === "sidebar") { 360 | closePanel(null, true); 361 | } else { 362 | closePanel(null, false); 363 | } 364 | } 365 | }); 366 | 367 | // Panel-specific logic 368 | if (pname === "artwork") { 369 | $("#drawer #artwork-search").trigger("select"); 370 | } 371 | } 372 | 373 | function closePanel(button, animate) { 374 | // Unregister escape key handler 375 | $(document).off("keyup", escHandler); 376 | 377 | // Find open panel 378 | if (button === null) { 379 | button = $(".sidebar-item-selected"); 380 | } 381 | 382 | // Error catching 383 | if (button === null || button === []) return; 384 | 385 | // Change button style 386 | $('.inactive', button).removeClass("noshow"); 387 | $('.active', button).addClass("noshow"); 388 | button.removeClass("sidebar-item-selected"); 389 | 390 | // Close panel 391 | if (animate === true) { 392 | if ($("#drawer.is-visible").length === 1) { 393 | $(".mdl-layout__drawer-button").trigger("click"); 394 | } 395 | } 396 | 397 | // Unregister click event handler 398 | $(document).off("click.menu"); 399 | } 400 | 401 | function closeSubmenu(menu, noTooltips) { 402 | menu.removeClass("toolbar-item-active"); 403 | menu.children(".toolbar-submenu").addClass("noshow"); 404 | 405 | if (noTooltips !== true) { 406 | // Delay showing of tooltips to prevent flashing behavior 407 | setTimeout(function(){ $(".mdl-tooltip").removeClass("noshow"); }, 200); 408 | } 409 | } 410 | 411 | /* --- artwork search --- */ 412 | 413 | function toggleArtworkSearchSpinner(show) { 414 | if (show === true) { 415 | $("#no-results").addClass("noshow"); 416 | var parent = $("#artwork-panel > .search-results"); 417 | parent.append($("#artwork-spinner").remove()); 418 | $("#artwork-spinner").removeClass("noshow"); 419 | } else { 420 | $("#artwork-spinner").addClass("noshow"); 421 | } 422 | } 423 | 424 | function toggleArtworkNoResults(show) { 425 | if (show === true) { 426 | $("#no-results").removeClass("noshow"); 427 | } else { 428 | $("#no-results").addClass("noshow"); 429 | } 430 | } 431 | 432 | function fitArtworkResultsHeight() { 433 | if ($("#drawer.is-visible").length !== 0) { 434 | // Set artwork search results height 435 | // so that scrolling handler works correctly 436 | var height = window.innerHeight - ($("#artwork-panel > .mdl-layout-title").height() + $("#artwork-panel > form").height() + 85); 437 | $("#artwork-panel > .search-results").height(height); 438 | } 439 | } 440 | 441 | /* ----- exports ----- */ 442 | 443 | function PageModule() { 444 | if (!(this instanceof PageModule)) return new PageModule(); 445 | 446 | // handlers 447 | noBackspace(); 448 | arrowKeys(); 449 | 450 | // fix broken MDL sliders in IE Edge Browser 451 | var user_agent = navigator.userAgent; 452 | var edge = /(edge)\/((\d+)?[\w\.]+)/i; 453 | if (edge.test(user_agent)) { 454 | $("input.mdl-slider.mdl-js-slider").removeClass("mdl-slider mdl-js-slider"); 455 | } 456 | 457 | // Fix broken sidebar in Safari 458 | var isSafari = navigator.vendor && navigator.vendor.indexOf('Apple') > -1 && 459 | navigator.userAgent && !navigator.userAgent.match('CriOS'); 460 | if (isSafari === true) { 461 | $(".mdl-navigation").css("height", "100%"); 462 | } 463 | } 464 | 465 | PageModule.prototype.showPreview = showPreview; 466 | PageModule.prototype.openPanel = openPanel; 467 | PageModule.prototype.closePanel = closePanel; 468 | PageModule.prototype.closeSubmenu = closeSubmenu; 469 | PageModule.prototype.fillColorPicker = fillColorPicker; 470 | PageModule.prototype.outlineColorPicker = outlineColorPicker; 471 | PageModule.prototype.toggleArtworkSearchSpinner = toggleArtworkSearchSpinner; 472 | PageModule.prototype.toggleArtworkNoResults = toggleArtworkNoResults; 473 | PageModule.prototype.fitArtworkResultsHeight = fitArtworkResultsHeight; 474 | PageModule.prototype.shadowColorPicker = shadowColorPicker; 475 | PageModule.prototype.glowColorPicker = glowColorPicker; 476 | 477 | module.exports = PageModule; 478 | -------------------------------------------------------------------------------- /src/js/app/fabricUtils.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | /* ----------------- 4 | Good references: 5 | https://github.com/michaeljcalkins/angular-fabric/blob/master/assets/fabric.js 6 | http://fabricjs.com/js/kitchensink/controller.js 7 | ----------------- */ 8 | 9 | var canvas = global.canvas; 10 | var filesaver = require('../lib/filesaver.min.js'); 11 | 12 | function selectAll() { 13 | canvas.discardActiveObject(); 14 | var sel = new fabric.ActiveSelection(canvas.getObjects(), { 15 | canvas: canvas, 16 | }); 17 | canvas.setActiveObject(sel); 18 | canvas.requestRenderAll(); 19 | } 20 | 21 | function sendForward() { 22 | var activeObject = canvas.getActiveObject(); 23 | if (activeObject) { 24 | canvas.bringForward(activeObject); 25 | // Push the canvas state to history 26 | canvas.trigger("object:statechange"); 27 | } 28 | } 29 | 30 | function sendBackward() { 31 | var activeObject = canvas.getActiveObject(); 32 | if (activeObject) { 33 | canvas.sendBackwards(activeObject); 34 | // Push the canvas state to history 35 | canvas.trigger("object:statechange"); 36 | } 37 | } 38 | 39 | function sendToFront() { 40 | var activeObject = canvas.getActiveObject(); 41 | if (activeObject) { 42 | canvas.bringToFront(activeObject); 43 | // Push the canvas state to history 44 | canvas.trigger("object:statechange"); 45 | } 46 | } 47 | 48 | function sendToBack() { 49 | var activeObject = canvas.getActiveObject(); 50 | if (activeObject) { 51 | canvas.sendToBack(activeObject); 52 | // Push the canvas state to history 53 | canvas.trigger("object:statechange"); 54 | } 55 | } 56 | 57 | function clone() { 58 | // clone what are you copying since you 59 | // may want copy and paste on different moment. 60 | // and you do not want the changes happened 61 | // later to reflect on the copy. 62 | canvas.getActiveObject().clone(function(cloned) { 63 | canvas.discardActiveObject(); 64 | cloned.set({ 65 | left: cloned.left + 10, 66 | top: cloned.top + 10, 67 | evented: true, 68 | }); 69 | if (cloned.type === 'activeSelection') { 70 | // active selection needs a reference to the canvas. 71 | cloned.canvas = canvas; 72 | cloned.forEachObject(function(obj) { 73 | canvas.add(obj); 74 | obj.perPixelTargetFind = true; 75 | obj.targetFindTolerance = 4; 76 | }); 77 | // this should solve the unselectability 78 | cloned.setCoords(); 79 | } else { 80 | canvas.add(cloned); 81 | cloned.perPixelTargetFind = true; 82 | cloned.targetFindTolerance = 4; 83 | } 84 | 85 | canvas.setActiveObject(cloned); 86 | canvas.requestRenderAll(); 87 | }); 88 | 89 | // Push the canvas state to history 90 | canvas.trigger("object:statechange"); 91 | } 92 | 93 | // This is the shadow-less version 94 | /* 95 | function getImageBounds() { 96 | selectAll(); 97 | var rect = canvas.getActiveObjects().getBoundingRect(); 98 | canvas.discardActiveObject() ; 99 | canvas.renderAll(); 100 | return rect; 101 | } 102 | */ 103 | 104 | // inludes shadows 105 | function getImageBounds(fitToCanvas) { 106 | var objs = canvas.getObjects(); 107 | 108 | if (objs.length === 0) { 109 | return { 110 | top: 0, left: 0, height: 0, width: 0 111 | }; 112 | } 113 | 114 | // Fabric.js bug getting an objects bounds when all objects are selected 115 | canvas.discardActiveObject() ; 116 | var bounds = objs[0].getBoundingRect(); 117 | 118 | // Find maximum bounds 119 | for (var i = 0; i < objs.length; i++) { 120 | var obj = objs[i]; 121 | var rect = getObjBounds(obj); 122 | 123 | if (rect.left < bounds.left) { 124 | bounds.width += bounds.left - rect.left; 125 | bounds.left = rect.left; 126 | } 127 | 128 | if (rect.top < bounds.top) { 129 | bounds.height += bounds.top - rect.top; 130 | bounds.top = rect.top; 131 | } 132 | 133 | var right = rect.left + rect.width; 134 | var bottom = rect.top + rect.height; 135 | 136 | if (right > bounds.left + bounds.width) { 137 | bounds.width = right - bounds.left; 138 | } 139 | 140 | if (bottom > bounds.top + bounds.height) { 141 | bounds.height = bottom - bounds.top; 142 | } 143 | } 144 | 145 | if (fitToCanvas) { 146 | // Fit to canvas 147 | if (bounds.left < 0) { 148 | bounds.width -= Math.abs(bounds.left); 149 | bounds.left = 0; 150 | } 151 | 152 | if (bounds.top < 0) { 153 | bounds.height -= Math.abs(bounds.top); 154 | bounds.top = 0; 155 | } 156 | 157 | if (bounds.left + bounds.width > canvas.width) { 158 | bounds.width = canvas.width - bounds.left; 159 | } 160 | 161 | if (bounds.top + bounds.height > canvas.height) { 162 | bounds.height = canvas.height - bounds.top; 163 | } 164 | } 165 | 166 | // Don't show selection tools 167 | selectAll(); 168 | canvas.discardActiveObject() ; 169 | canvas.renderAll(); 170 | 171 | return bounds; 172 | } 173 | 174 | // includes shadows 175 | function getObjBounds(obj) { 176 | var bounds = obj.getBoundingRect(); 177 | var shadow = obj.Shadow; 178 | 179 | if (shadow != null) { 180 | var blur = shadow.blur; 181 | var mBlur = blur * Math.abs(obj.scaleX + obj.scaleY) / 4; 182 | var signX = shadow.offsetX >= 0.0 ? 1.0 : -1.0; 183 | var signY = shadow.offsetY >= 0.0 ? 1.0 : -1.0; 184 | var mOffsetX = shadow.offsetX * Math.abs(obj.scaleX); 185 | var mOffsetY = shadow.offsetY * Math.abs(obj.scaleY); 186 | var offsetX = mOffsetX + (signX * mBlur); 187 | var offsetY = mOffsetY + (signY * mBlur); 188 | 189 | if (mOffsetX > mBlur) { 190 | bounds.width += offsetX; 191 | } else if (mOffsetX < -mBlur) { 192 | bounds.width -= offsetX; 193 | bounds.left += offsetX; 194 | } else { 195 | bounds.width += mBlur * 2; 196 | bounds.left -= mBlur - mOffsetX; 197 | } 198 | 199 | if (mOffsetY > mBlur) { 200 | bounds.height += offsetY; 201 | } else if (mOffsetY < -mBlur) { 202 | bounds.height -= offsetY; 203 | bounds.top += offsetY; 204 | } else { 205 | bounds.height += mBlur * 2; 206 | bounds.top -= mBlur - mOffsetY; 207 | } 208 | } 209 | 210 | return bounds; 211 | } 212 | 213 | // fileType should be "png", "jpeg", or "svg" 214 | function exportFile(fileType) { 215 | // Get bounds of image 216 | var bounds = getImageBounds(true); 217 | var blob = null; 218 | 219 | // Get image data 220 | if (fileType === "svg") { 221 | var svg = canvas.toSVG({ 222 | viewBox: { 223 | x: bounds.left, 224 | y: bounds.top, 225 | width: bounds.width, 226 | height: bounds.height 227 | } 228 | }); 229 | blob = new Blob([svg], {type: "image/svg+xml"}); 230 | } else { 231 | var dataURL = canvas.toDataURL({ 232 | format: fileType, 233 | left: bounds.left, 234 | top: bounds.top, 235 | width: bounds.width, 236 | height: bounds.height, 237 | quality: 1.0 238 | }); 239 | 240 | var data = atob(dataURL.replace(/^.*?base64,/, '')); 241 | var asArray = new Uint8Array(data.length); 242 | for( var i = 0, len = data.length; i < len; ++i ) { 243 | asArray[i] = data.charCodeAt(i); 244 | } 245 | blob = new Blob([asArray.buffer], {type: "image/" + fileType}); 246 | } 247 | 248 | // Save file 249 | filesaver.saveAs(blob, "design." + fileType); 250 | } 251 | 252 | function deleteSelected() { 253 | // Delete the current object(s) 254 | var activeObjects = canvas.getActiveObjects(); 255 | canvas.discardActiveObject() ; 256 | if (activeObjects.length) { 257 | canvas.remove.apply(canvas, activeObjects); 258 | } 259 | } 260 | 261 | function insertSvg(url, loader) { 262 | loader.removeClass("noshow"); 263 | fabric.loadSVGFromURL(url, function(objects, options) { 264 | var obj = fabric.util.groupSVGElements(objects, options); 265 | 266 | var scaleFactor = 1; 267 | if (obj.width > obj.height) { 268 | scaleFactor = (canvas.width / 3) / obj.width; 269 | } else { 270 | scaleFactor = (canvas.height / 3) / obj.height; 271 | } 272 | 273 | obj.set({ 274 | top: Math.floor(canvas.height / 5), 275 | left: Math.floor(canvas.width / 5), 276 | scaleY: scaleFactor, 277 | scaleX: scaleFactor 278 | }); 279 | 280 | canvas.add(obj); 281 | obj.perPixelTargetFind = true; 282 | obj.targetFindTolerance = 4; 283 | canvas.discardActiveObject() ; 284 | canvas.setActiveObject(obj); 285 | canvas.renderAll(); 286 | 287 | // Push the canvas state to history 288 | canvas.trigger("object:statechange"); 289 | 290 | loader.addClass("noshow"); 291 | }); 292 | } 293 | 294 | function getActiveStyle(styleName, object) { 295 | object = object || canvas.getActiveObject(); 296 | 297 | if (typeof object !== 'object' || object === null) { 298 | return ''; 299 | } 300 | 301 | return (object[styleName] || ''); 302 | } 303 | 304 | function setActiveStyle(styleName, value, object) { 305 | object = object || canvas.getActiveObject(); 306 | object[styleName] = value; 307 | } 308 | 309 | function getFillColor() { 310 | var object = canvas.getActiveObject(); 311 | if (object.customFillColor !== undefined) { 312 | return object.customFillColor; 313 | } else if (object.type === 'line') { 314 | return getActiveStyle("stroke"); 315 | } else { 316 | return getActiveStyle("fill"); 317 | } 318 | } 319 | function setActiveStyle(styleName, value, object) { 320 | object = object || canvas.getActiveObject(); 321 | if (!object) return; 322 | 323 | if (object.setSelectionStyles && object.isEditing) { 324 | var style = { }; 325 | style[styleName] = value; 326 | object.setSelectionStyles(style); 327 | object.setCoords(); 328 | } 329 | else { 330 | object.set(styleName, value); 331 | } 332 | 333 | object.setCoords(); 334 | canvas.renderAll(); 335 | } 336 | 337 | function setFillColor(hex) { 338 | var object = canvas.getActiveObject(); 339 | if (object) { 340 | setActiveStyle('fill',hex); // transparency 341 | } 342 | } 343 | 344 | function getOutlineColor() { 345 | var object = canvas.getActiveObject(); 346 | if (object.customOutlineColor !== undefined) { 347 | return object.customOutlineColor; 348 | } 349 | 350 | return getActiveStyle("stroke"); 351 | } 352 | 353 | function setOutlineColor(hex) { 354 | var object = canvas.getActiveObject(); 355 | if (object) { 356 | setActiveStyle('stroke',hex); 357 | } 358 | } 359 | 360 | function getFontSize() { 361 | return getActiveStyle('fontSize'); 362 | } 363 | 364 | function setFontSize(value) { 365 | setActiveStyle('fontSize', parseInt(value, 10)); 366 | canvas.renderAll(); 367 | } 368 | 369 | function getFont() { 370 | var fontFamily = canvas.getActiveObject().fontFamily; 371 | return fontFamily ? fontFamily.toLowerCase() : ''; 372 | } 373 | 374 | function setFont(font) { 375 | canvas.getActiveObject().fontFamily = font.toLowerCase(); 376 | canvas.renderAll(); 377 | } 378 | 379 | 380 | /* ----- shadow and glow ----- */ 381 | 382 | function setShadow(_color, _blur, _offsetX, _offsetY, object) { 383 | object = object || canvas.getActiveObject(); 384 | object.setShadow({ 385 | color: _color, 386 | blur: _blur, 387 | offsetX: _offsetX, 388 | offsetY: _offsetY 389 | }); 390 | canvas.renderAll(); 391 | 392 | // Push the canvas state to history 393 | canvas.trigger("object:statechange"); 394 | } 395 | 396 | function changeShadowColor(color, object) { 397 | object = object || canvas.getActiveObject(); 398 | var shadow = object.shadow; 399 | if (shadow === null) { 400 | return null; 401 | } 402 | setShadow(color, shadow.blur, shadow.offsetX, shadow.offsetY, object); 403 | } 404 | 405 | function clearShadow(object) { 406 | object = object || canvas.getActiveObject(); 407 | object.setShadow(null); 408 | canvas.renderAll(); 409 | 410 | // Push the canvas state to history 411 | canvas.trigger("object:statechange"); 412 | } 413 | 414 | function isShadow(object) { 415 | object = object || canvas.getActiveObject(); 416 | var shadow = object.shadow ; 417 | return (shadow !== null && (shadow.offsetX !== 0 || shadow.offsetY !== 0)); 418 | } 419 | 420 | // Glow is just a shadow with an offset of zero 421 | function isGlow(object) { 422 | object = object || canvas.getActiveObject(); 423 | var shadow = object.shadow; 424 | return (shadow !== null && shadow.offsetX === 0 && shadow.offsetY === 0); 425 | } 426 | 427 | function getShadowBlur(object) { 428 | object = object || canvas.getActiveObject(); 429 | var shadow = object.shadow; 430 | if (shadow === null) { 431 | return null; 432 | } 433 | 434 | return parseInt(shadow.blur); 435 | } 436 | 437 | function getShadowColor(object) { 438 | object = object || canvas.getActiveObject(); 439 | var shadow = object.shadow; 440 | if (shadow === null) { 441 | return null; 442 | } 443 | 444 | return shadow.color; 445 | } 446 | 447 | function getShadowOffset(object) { 448 | object = object || canvas.getActiveObject(); 449 | var shadow = object.shadow; 450 | if (shadow == null) { 451 | return null; 452 | } 453 | var x = parseInt(shadow.offsetX); 454 | var y = parseInt(shadow.offsetY); 455 | return {"x": x, "y": y}; 456 | } 457 | 458 | function changeImageOffset(left, top) { 459 | var objs = canvas.getObjects(); 460 | for (var i = 0; i < objs.length; i++) { 461 | objs[i].left += left; 462 | objs[i].top += top; 463 | } 464 | } 465 | 466 | function centerContent() { 467 | var bounds = getImageBounds(false); 468 | 469 | var canvasMidpointLeft = Math.round(canvas.width / 2); 470 | var canvasMidpointTop = Math.round(canvas.height / 2); 471 | var imageMidpointLeft = Math.round((bounds.width / 2) + bounds.left); 472 | var imageMidpointTop = Math.round((bounds.height / 2) + bounds.top); 473 | var diffLeft = canvasMidpointLeft - imageMidpointLeft; 474 | var diffTop = canvasMidpointTop - imageMidpointTop; 475 | 476 | changeImageOffset(diffLeft, diffTop); 477 | canvas.renderAll(); 478 | } 479 | 480 | 481 | /* ----- exports ----- */ 482 | 483 | function UtilsModule() { 484 | if (!(this instanceof UtilsModule)) return new UtilsModule(); 485 | // constructor 486 | } 487 | 488 | UtilsModule.prototype.selectAll = selectAll; 489 | UtilsModule.prototype.exportFile = exportFile; 490 | UtilsModule.prototype.getImageBounds = getImageBounds; 491 | UtilsModule.prototype.deleteSelected = deleteSelected; 492 | UtilsModule.prototype.insertSvg = insertSvg; 493 | UtilsModule.prototype.sendToFront = sendToFront; 494 | UtilsModule.prototype.sendToBack = sendToBack; 495 | UtilsModule.prototype.sendBackward = sendBackward; 496 | UtilsModule.prototype.sendForward = sendForward; 497 | UtilsModule.prototype.clone = clone; 498 | UtilsModule.prototype.getFillColor = getFillColor; 499 | UtilsModule.prototype.setFillColor = setFillColor; 500 | UtilsModule.prototype.getOutlineColor = getOutlineColor; 501 | UtilsModule.prototype.setOutlineColor = setOutlineColor; 502 | UtilsModule.prototype.getFontSize = getFontSize; 503 | UtilsModule.prototype.setFontSize = setFontSize; 504 | UtilsModule.prototype.getFont = getFont; 505 | UtilsModule.prototype.setFont = setFont; 506 | UtilsModule.prototype.setShadow = setShadow; 507 | UtilsModule.prototype.clearShadow = clearShadow; 508 | UtilsModule.prototype.isShadow = isShadow; 509 | UtilsModule.prototype.isGlow = isGlow; 510 | UtilsModule.prototype.getShadowBlur = getShadowBlur; 511 | UtilsModule.prototype.getShadowOffset = getShadowOffset; 512 | UtilsModule.prototype.changeShadowColor = changeShadowColor; 513 | UtilsModule.prototype.getShadowColor = getShadowColor; 514 | UtilsModule.prototype.centerContent = centerContent; 515 | 516 | module.exports = UtilsModule; 517 | -------------------------------------------------------------------------------- /src/js/lib/jquery.tooltipster.min.js: -------------------------------------------------------------------------------- 1 | /* Tooltipster v3.3.0 */;(function(e,t,n){function s(t,n){this.bodyOverflowX;this.callbacks={hide:[],show:[]};this.checkInterval=null;this.Content;this.$el=e(t);this.$elProxy;this.elProxyPosition;this.enabled=true;this.options=e.extend({},i,n);this.mouseIsOverProxy=false;this.namespace="tooltipster-"+Math.round(Math.random()*1e5);this.Status="hidden";this.timerHide=null;this.timerShow=null;this.$tooltip;this.options.iconTheme=this.options.iconTheme.replace(".","");this.options.theme=this.options.theme.replace(".","");this._init()}function o(t,n){var r=true;e.each(t,function(e,i){if(typeof n[e]==="undefined"||t[e]!==n[e]){r=false;return false}});return r}function f(){return!a&&u}function l(){var e=n.body||n.documentElement,t=e.style,r="transition";if(typeof t[r]=="string"){return true}v=["Moz","Webkit","Khtml","O","ms"],r=r.charAt(0).toUpperCase()+r.substr(1);for(var i=0;i');t.$elProxy.text(t.options.icon)}else{if(t.options.iconCloning)t.$elProxy=t.options.icon.clone(true);else t.$elProxy=t.options.icon}t.$elProxy.insertAfter(t.$el)}else{t.$elProxy=t.$el}if(t.options.trigger=="hover"){t.$elProxy.on("mouseenter."+t.namespace,function(){if(!f()||t.options.touchDevices){t.mouseIsOverProxy=true;t._show()}}).on("mouseleave."+t.namespace,function(){if(!f()||t.options.touchDevices){t.mouseIsOverProxy=false}});if(u&&t.options.touchDevices){t.$elProxy.on("touchstart."+t.namespace,function(){t._showNow()})}}else if(t.options.trigger=="click"){t.$elProxy.on("click."+t.namespace,function(){if(!f()||t.options.touchDevices){t._show()}})}}},_show:function(){var e=this;if(e.Status!="shown"&&e.Status!="appearing"){if(e.options.delay){e.timerShow=setTimeout(function(){if(e.options.trigger=="click"||e.options.trigger=="hover"&&e.mouseIsOverProxy){e._showNow()}},e.options.delay)}else e._showNow()}},_showNow:function(n){var r=this;r.options.functionBefore.call(r.$el,r.$el,function(){if(r.enabled&&r.Content!==null){if(n)r.callbacks.show.push(n);r.callbacks.hide=[];clearTimeout(r.timerShow);r.timerShow=null;clearTimeout(r.timerHide);r.timerHide=null;if(r.options.onlyOne){e(".tooltipstered").not(r.$el).each(function(t,n){var r=e(n),i=r.data("tooltipster-ns");e.each(i,function(e,t){var n=r.data(t),i=n.status(),s=n.option("autoClose");if(i!=="hidden"&&i!=="disappearing"&&s){n.hide()}})})}var i=function(){r.Status="shown";e.each(r.callbacks.show,function(e,t){t.call(r.$el)});r.callbacks.show=[]};if(r.Status!=="hidden"){var s=0;if(r.Status==="disappearing"){r.Status="appearing";if(l()){r.$tooltip.clearQueue().removeClass("tooltipster-dying").addClass("tooltipster-"+r.options.animation+"-show");if(r.options.speed>0)r.$tooltip.delay(r.options.speed);r.$tooltip.queue(i)}else{r.$tooltip.stop().fadeIn(i)}}else if(r.Status==="shown"){i()}}else{r.Status="appearing";var s=r.options.speed;r.bodyOverflowX=e("body").css("overflow-x");e("body").css("overflow-x","hidden");var o="tooltipster-"+r.options.animation,a="-webkit-transition-duration: "+r.options.speed+"ms; -webkit-animation-duration: "+r.options.speed+"ms; -moz-transition-duration: "+r.options.speed+"ms; -moz-animation-duration: "+r.options.speed+"ms; -o-transition-duration: "+r.options.speed+"ms; -o-animation-duration: "+r.options.speed+"ms; -ms-transition-duration: "+r.options.speed+"ms; -ms-animation-duration: "+r.options.speed+"ms; transition-duration: "+r.options.speed+"ms; animation-duration: "+r.options.speed+"ms;",f=r.options.minWidth?"min-width:"+Math.round(r.options.minWidth)+"px;":"",c=r.options.maxWidth?"max-width:"+Math.round(r.options.maxWidth)+"px;":"",h=r.options.interactive?"pointer-events: auto;":"";r.$tooltip=e('
');if(l())r.$tooltip.addClass(o);r._content_insert();r.$tooltip.appendTo("body");r.reposition();r.options.functionReady.call(r.$el,r.$el,r.$tooltip);if(l()){r.$tooltip.addClass(o+"-show");if(r.options.speed>0)r.$tooltip.delay(r.options.speed);r.$tooltip.queue(i)}else{r.$tooltip.css("display","none").fadeIn(r.options.speed,i)}r._interval_set();e(t).on("scroll."+r.namespace+" resize."+r.namespace,function(){r.reposition()});if(r.options.autoClose){e("body").off("."+r.namespace);if(r.options.trigger=="hover"){if(u){setTimeout(function(){e("body").on("touchstart."+r.namespace,function(){r.hide()})},0)}if(r.options.interactive){if(u){r.$tooltip.on("touchstart."+r.namespace,function(e){e.stopPropagation()})}var p=null;r.$elProxy.add(r.$tooltip).on("mouseleave."+r.namespace+"-autoClose",function(){clearTimeout(p);p=setTimeout(function(){r.hide()},r.options.interactiveTolerance)}).on("mouseenter."+r.namespace+"-autoClose",function(){clearTimeout(p)})}else{r.$elProxy.on("mouseleave."+r.namespace+"-autoClose",function(){r.hide()})}if(r.options.hideOnClick){r.$elProxy.on("click."+r.namespace+"-autoClose",function(){r.hide()})}}else if(r.options.trigger=="click"){setTimeout(function(){e("body").on("click."+r.namespace+" touchstart."+r.namespace,function(){r.hide()})},0);if(r.options.interactive){r.$tooltip.on("click."+r.namespace+" touchstart."+r.namespace,function(e){e.stopPropagation()})}}}}if(r.options.timer>0){r.timerHide=setTimeout(function(){r.timerHide=null;r.hide()},r.options.timer+s)}}})},_interval_set:function(){var t=this;t.checkInterval=setInterval(function(){if(e("body").find(t.$el).length===0||e("body").find(t.$elProxy).length===0||t.Status=="hidden"||e("body").find(t.$tooltip).length===0){if(t.Status=="shown"||t.Status=="appearing")t.hide();t._interval_cancel()}else{if(t.options.positionTracker){var n=t._repositionInfo(t.$elProxy),r=false;if(o(n.dimension,t.elProxyPosition.dimension)){if(t.$elProxy.css("position")==="fixed"){if(o(n.position,t.elProxyPosition.position))r=true}else{if(o(n.offset,t.elProxyPosition.offset))r=true}}if(!r){t.reposition();t.options.positionTrackerCallback.call(t,t.$el)}}}},200)},_interval_cancel:function(){clearInterval(this.checkInterval);this.checkInterval=null},_content_set:function(e){if(typeof e==="object"&&e!==null&&this.options.contentCloning){e=e.clone(true)}this.Content=e},_content_insert:function(){var e=this,t=this.$tooltip.find(".tooltipster-content");if(typeof e.Content==="string"&&!e.options.contentAsHTML){t.text(e.Content)}else{t.empty().append(e.Content)}},_update:function(e){var t=this;t._content_set(e);if(t.Content!==null){if(t.Status!=="hidden"){t._content_insert();t.reposition();if(t.options.updateAnimation){if(l()){t.$tooltip.css({width:"","-webkit-transition":"all "+t.options.speed+"ms, width 0ms, height 0ms, left 0ms, top 0ms","-moz-transition":"all "+t.options.speed+"ms, width 0ms, height 0ms, left 0ms, top 0ms","-o-transition":"all "+t.options.speed+"ms, width 0ms, height 0ms, left 0ms, top 0ms","-ms-transition":"all "+t.options.speed+"ms, width 0ms, height 0ms, left 0ms, top 0ms",transition:"all "+t.options.speed+"ms, width 0ms, height 0ms, left 0ms, top 0ms"}).addClass("tooltipster-content-changing");setTimeout(function(){if(t.Status!="hidden"){t.$tooltip.removeClass("tooltipster-content-changing");setTimeout(function(){if(t.Status!=="hidden"){t.$tooltip.css({"-webkit-transition":t.options.speed+"ms","-moz-transition":t.options.speed+"ms","-o-transition":t.options.speed+"ms","-ms-transition":t.options.speed+"ms",transition:t.options.speed+"ms"})}},t.options.speed)}},t.options.speed)}else{t.$tooltip.fadeTo(t.options.speed,.5,function(){if(t.Status!="hidden"){t.$tooltip.fadeTo(t.options.speed,1)}})}}}}else{t.hide()}},_repositionInfo:function(e){return{dimension:{height:e.outerHeight(false),width:e.outerWidth(false)},offset:e.offset(),position:{left:parseInt(e.css("left")),top:parseInt(e.css("top"))}}},hide:function(n){var r=this;if(n)r.callbacks.hide.push(n);r.callbacks.show=[];clearTimeout(r.timerShow);r.timerShow=null;clearTimeout(r.timerHide);r.timerHide=null;var i=function(){e.each(r.callbacks.hide,function(e,t){t.call(r.$el)});r.callbacks.hide=[]};if(r.Status=="shown"||r.Status=="appearing"){r.Status="disappearing";var s=function(){r.Status="hidden";if(typeof r.Content=="object"&&r.Content!==null){r.Content.detach()}r.$tooltip.remove();r.$tooltip=null;e(t).off("."+r.namespace);e("body").off("."+r.namespace).css("overflow-x",r.bodyOverflowX);e("body").off("."+r.namespace);r.$elProxy.off("."+r.namespace+"-autoClose");r.options.functionAfter.call(r.$el,r.$el);i()};if(l()){r.$tooltip.clearQueue().removeClass("tooltipster-"+r.options.animation+"-show").addClass("tooltipster-dying");if(r.options.speed>0)r.$tooltip.delay(r.options.speed);r.$tooltip.queue(s)}else{r.$tooltip.stop().fadeOut(r.options.speed,s)}}else if(r.Status=="hidden"){i()}return r},show:function(e){this._showNow(e);return this},update:function(e){return this.content(e)},content:function(e){if(typeof e==="undefined"){return this.Content}else{this._update(e);return this}},reposition:function(){var n=this;if(e("body").find(n.$tooltip).length!==0){n.$tooltip.css("width","");n.elProxyPosition=n._repositionInfo(n.$elProxy);var r=null,i=e(t).width(),s=n.elProxyPosition,o=n.$tooltip.outerWidth(false),u=n.$tooltip.innerWidth()+1,a=n.$tooltip.outerHeight(false);if(n.$elProxy.is("area")){var f=n.$elProxy.attr("shape"),l=n.$elProxy.parent().attr("name"),c=e('img[usemap="#'+l+'"]'),h=c.offset().left,p=c.offset().top,d=n.$elProxy.attr("coords")!==undefined?n.$elProxy.attr("coords").split(","):undefined;if(f=="circle"){var v=parseInt(d[0]),m=parseInt(d[1]),g=parseInt(d[2]);s.dimension.height=g*2;s.dimension.width=g*2;s.offset.top=p+m-g;s.offset.left=h+v-g}else if(f=="rect"){var v=parseInt(d[0]),m=parseInt(d[1]),y=parseInt(d[2]),b=parseInt(d[3]);s.dimension.height=b-m;s.dimension.width=y-v;s.offset.top=p+m;s.offset.left=h+v}else if(f=="poly"){var w=[],E=[],S=0,x=0,T=0,N=0,C="even";for(var k=0;kT){T=L;if(k===0){S=T}}if(LN){N=L;if(k==1){x=N}}if(Li){r=A-(i+n-o);A=i+n-o}}function B(n,r){if(s.offset.top-e(t).scrollTop()-a-_-12<0&&r.indexOf("top")>-1){P=n}if(s.offset.top+s.dimension.height+a+12+_>e(t).scrollTop()+e(t).height()&&r.indexOf("bottom")>-1){P=n;M=s.offset.top-a-_-12}}if(P=="top"){var j=s.offset.left+o-(s.offset.left+s.dimension.width);A=s.offset.left+D-j/2;M=s.offset.top-a-_-12;H();B("bottom","top")}if(P=="top-left"){A=s.offset.left+D;M=s.offset.top-a-_-12;H();B("bottom-left","top-left")}if(P=="top-right"){A=s.offset.left+s.dimension.width+D-o;M=s.offset.top-a-_-12;H();B("bottom-right","top-right")}if(P=="bottom"){var j=s.offset.left+o-(s.offset.left+s.dimension.width);A=s.offset.left-j/2+D;M=s.offset.top+s.dimension.height+_+12;H();B("top","bottom")}if(P=="bottom-left"){A=s.offset.left+D;M=s.offset.top+s.dimension.height+_+12;H();B("top-left","bottom-left")}if(P=="bottom-right"){A=s.offset.left+s.dimension.width+D-o;M=s.offset.top+s.dimension.height+_+12;H();B("top-right","bottom-right")}if(P=="left"){A=s.offset.left-D-o-12;O=s.offset.left+D+s.dimension.width+12;var F=s.offset.top+a-(s.offset.top+s.dimension.height);M=s.offset.top-F/2-_;if(A<0&&O+o>i){var I=parseFloat(n.$tooltip.css("border-width"))*2,q=o+A-I;n.$tooltip.css("width",q+"px");a=n.$tooltip.outerHeight(false);A=s.offset.left-D-q-12-I;F=s.offset.top+a-(s.offset.top+s.dimension.height);M=s.offset.top-F/2-_}else if(A<0){A=s.offset.left+D+s.dimension.width+12;r="left"}}if(P=="right"){A=s.offset.left+D+s.dimension.width+12;O=s.offset.left-D-o-12;var F=s.offset.top+a-(s.offset.top+s.dimension.height);M=s.offset.top-F/2-_;if(A+o>i&&O<0){var I=parseFloat(n.$tooltip.css("border-width"))*2,q=i-A-I;n.$tooltip.css("width",q+"px");a=n.$tooltip.outerHeight(false);F=s.offset.top+a-(s.offset.top+s.dimension.height);M=s.offset.top-F/2-_}else if(A+o>i){A=s.offset.left-D-o-12;r="right"}}if(n.options.arrow){var R="tooltipster-arrow-"+P;if(n.options.arrowColor.length<1){var U=n.$tooltip.css("background-color")}else{var U=n.options.arrowColor}if(!r){r=""}else if(r=="left"){R="tooltipster-arrow-right";r=""}else if(r=="right"){R="tooltipster-arrow-left";r=""}else{r="left:"+Math.round(r)+"px;"}if(P=="top"||P=="top-left"||P=="top-right"){var z=parseFloat(n.$tooltip.css("border-bottom-width")),W=n.$tooltip.css("border-bottom-color")}else if(P=="bottom"||P=="bottom-left"||P=="bottom-right"){var z=parseFloat(n.$tooltip.css("border-top-width")),W=n.$tooltip.css("border-top-color")}else if(P=="left"){var z=parseFloat(n.$tooltip.css("border-right-width")),W=n.$tooltip.css("border-right-color")}else if(P=="right"){var z=parseFloat(n.$tooltip.css("border-left-width")),W=n.$tooltip.css("border-left-color")}else{var z=parseFloat(n.$tooltip.css("border-bottom-width")),W=n.$tooltip.css("border-bottom-color")}if(z>1){z++}var X="";if(z!==0){var V="",J="border-color: "+W+";";if(R.indexOf("bottom")!==-1){V="margin-top: -"+Math.round(z)+"px;"}else if(R.indexOf("top")!==-1){V="margin-bottom: -"+Math.round(z)+"px;"}else if(R.indexOf("left")!==-1){V="margin-right: -"+Math.round(z)+"px;"}else if(R.indexOf("right")!==-1){V="margin-left: -"+Math.round(z)+"px;"}X=''}n.$tooltip.find(".tooltipster-arrow").remove();var K='
'+X+'
';n.$tooltip.append(K)}n.$tooltip.css({top:Math.round(M)+"px",left:Math.round(A)+"px"})}return n},enable:function(){this.enabled=true;return this},disable:function(){this.hide();this.enabled=false;return this},destroy:function(){var t=this;t.hide();if(t.$el[0]!==t.$elProxy[0]){t.$elProxy.remove()}t.$el.removeData(t.namespace).off("."+t.namespace);var n=t.$el.data("tooltipster-ns");if(n.length===1){var r=null;if(t.options.restoration==="previous"){r=t.$el.data("tooltipster-initialTitle")}else if(t.options.restoration==="current"){r=typeof t.Content==="string"?t.Content:e("
").append(t.Content).html()}if(r){t.$el.attr("title",r)}t.$el.removeClass("tooltipstered").removeData("tooltipster-ns").removeData("tooltipster-initialTitle")}else{n=e.grep(n,function(e,n){return e!==t.namespace});t.$el.data("tooltipster-ns",n)}return t},elementIcon:function(){return this.$el[0]!==this.$elProxy[0]?this.$elProxy[0]:undefined},elementTooltip:function(){return this.$tooltip?this.$tooltip[0]:undefined},option:function(e,t){if(typeof t=="undefined")return this.options[e];else{this.options[e]=t;return this}},status:function(){return this.Status}};e.fn[r]=function(){var t=arguments;if(this.length===0){if(typeof t[0]==="string"){var n=true;switch(t[0]){case"setDefaults":e.extend(i,t[1]);break;default:n=false;break}if(n)return true;else return this}else{return this}}else{if(typeof t[0]==="string"){var r="#*$~&";this.each(function(){var n=e(this).data("tooltipster-ns"),i=n?e(this).data(n[0]):null;if(i){if(typeof i[t[0]]==="function"){var s=i[t[0]](t[1],t[2])}else{throw new Error('Unknown method .tooltipster("'+t[0]+'")')}if(s!==i){r=s;return false}}else{throw new Error("You called Tooltipster's \""+t[0]+'" method on an uninitialized element')}});return r!=="#*$~&"?r:this}else{var o=[],u=t[0]&&typeof t[0].multiple!=="undefined",a=u&&t[0].multiple||!u&&i.multiple,f=t[0]&&typeof t[0].debug!=="undefined",l=f&&t[0].debug||!f&&i.debug;this.each(function(){var n=false,r=e(this).data("tooltipster-ns"),i=null;if(!r){n=true}else if(a){n=true}else if(l){console.log('Tooltipster: one or more tooltips are already attached to this element: ignoring. Use the "multiple" option to attach more tooltips.')}if(n){i=new s(this,t[0]);if(!r)r=[];r.push(i.namespace);e(this).data("tooltipster-ns",r);e(this).data(i.namespace,i)}o.push(i)});if(a)return o;else return this}}};var u=!!("ontouchstart"in t);var a=false;e("body").one("mousemove",function(){a=true})})(jQuery,window,document); -------------------------------------------------------------------------------- /src/css/lib/spectrum.css: -------------------------------------------------------------------------------- 1 | /*** 2 | Spectrum Colorpicker v1.8.0 3 | https://github.com/bgrins/spectrum 4 | Author: Brian Grinstead 5 | License: MIT 6 | ***/ 7 | 8 | .sp-container { 9 | position:absolute; 10 | top:0; 11 | left:0; 12 | display:inline-block; 13 | *display: inline; 14 | *zoom: 1; 15 | /* https://github.com/bgrins/spectrum/issues/40 */ 16 | z-index: 9999994; 17 | overflow: hidden; 18 | } 19 | .sp-container.sp-flat { 20 | position: relative; 21 | } 22 | 23 | /* Fix for * { box-sizing: border-box; } */ 24 | .sp-container, 25 | .sp-container * { 26 | -webkit-box-sizing: content-box; 27 | -moz-box-sizing: content-box; 28 | box-sizing: content-box; 29 | } 30 | 31 | /* http://ansciath.tumblr.com/post/7347495869/css-aspect-ratio */ 32 | .sp-top { 33 | position:relative; 34 | width: 100%; 35 | display:inline-block; 36 | } 37 | .sp-top-inner { 38 | position:absolute; 39 | top:0; 40 | left:0; 41 | bottom:0; 42 | right:0; 43 | } 44 | .sp-color { 45 | position: absolute; 46 | top:0; 47 | left:0; 48 | bottom:0; 49 | right:20%; 50 | } 51 | .sp-hue { 52 | position: absolute; 53 | top:0; 54 | right:0; 55 | bottom:0; 56 | left:84%; 57 | height: 100%; 58 | } 59 | 60 | .sp-clear-enabled .sp-hue { 61 | top:33px; 62 | height: 77.5%; 63 | } 64 | 65 | .sp-fill { 66 | padding-top: 80%; 67 | } 68 | .sp-sat, .sp-val { 69 | position: absolute; 70 | top:0; 71 | left:0; 72 | right:0; 73 | bottom:0; 74 | } 75 | 76 | .sp-alpha-enabled .sp-top { 77 | margin-bottom: 18px; 78 | } 79 | .sp-alpha-enabled .sp-alpha { 80 | display: block; 81 | } 82 | .sp-alpha-handle { 83 | position:absolute; 84 | top:-4px; 85 | bottom: -4px; 86 | width: 6px; 87 | left: 50%; 88 | cursor: pointer; 89 | border: 1px solid black; 90 | background: white; 91 | opacity: .8; 92 | } 93 | .sp-alpha { 94 | display: none; 95 | position: absolute; 96 | bottom: -14px; 97 | right: 0; 98 | left: 0; 99 | height: 8px; 100 | } 101 | .sp-alpha-inner { 102 | border: solid 1px #333; 103 | } 104 | 105 | .sp-clear { 106 | display: none; 107 | } 108 | 109 | .sp-clear.sp-clear-display { 110 | background-position: center; 111 | } 112 | 113 | .sp-clear-enabled .sp-clear { 114 | display: block; 115 | position:absolute; 116 | top:0px; 117 | right:0; 118 | bottom:0; 119 | left:84%; 120 | height: 28px; 121 | } 122 | 123 | /* Don't allow text selection */ 124 | .sp-container, .sp-replacer, .sp-preview, .sp-dragger, .sp-slider, .sp-alpha, .sp-clear, .sp-alpha-handle, .sp-container.sp-dragging .sp-input, .sp-container button { 125 | -webkit-user-select:none; 126 | -moz-user-select: -moz-none; 127 | -o-user-select:none; 128 | user-select: none; 129 | } 130 | 131 | .sp-container.sp-input-disabled .sp-input-container { 132 | display: none; 133 | } 134 | .sp-container.sp-buttons-disabled .sp-button-container { 135 | display: none; 136 | } 137 | .sp-container.sp-palette-buttons-disabled .sp-palette-button-container { 138 | display: none; 139 | } 140 | .sp-palette-only .sp-picker-container { 141 | display: none; 142 | } 143 | .sp-palette-disabled .sp-palette-container { 144 | display: none; 145 | } 146 | 147 | .sp-initial-disabled .sp-initial { 148 | display: none; 149 | } 150 | 151 | 152 | /* Gradients for hue, saturation and value instead of images. Not pretty... but it works */ 153 | .sp-sat { 154 | background-image: -webkit-gradient(linear, 0 0, 100% 0, from(#FFF), to(rgba(204, 154, 129, 0))); 155 | background-image: -webkit-linear-gradient(left, #FFF, rgba(204, 154, 129, 0)); 156 | background-image: -moz-linear-gradient(left, #fff, rgba(204, 154, 129, 0)); 157 | background-image: -o-linear-gradient(left, #fff, rgba(204, 154, 129, 0)); 158 | background-image: -ms-linear-gradient(left, #fff, rgba(204, 154, 129, 0)); 159 | background-image: linear-gradient(to right, #fff, rgba(204, 154, 129, 0)); 160 | -ms-filter: "progid:DXImageTransform.Microsoft.gradient(GradientType = 1, startColorstr=#FFFFFFFF, endColorstr=#00CC9A81)"; 161 | filter : progid:DXImageTransform.Microsoft.gradient(GradientType = 1, startColorstr='#FFFFFFFF', endColorstr='#00CC9A81'); 162 | } 163 | .sp-val { 164 | background-image: -webkit-gradient(linear, 0 100%, 0 0, from(#000000), to(rgba(204, 154, 129, 0))); 165 | background-image: -webkit-linear-gradient(bottom, #000000, rgba(204, 154, 129, 0)); 166 | background-image: -moz-linear-gradient(bottom, #000, rgba(204, 154, 129, 0)); 167 | background-image: -o-linear-gradient(bottom, #000, rgba(204, 154, 129, 0)); 168 | background-image: -ms-linear-gradient(bottom, #000, rgba(204, 154, 129, 0)); 169 | background-image: linear-gradient(to top, #000, rgba(204, 154, 129, 0)); 170 | -ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorstr=#00CC9A81, endColorstr=#FF000000)"; 171 | filter : progid:DXImageTransform.Microsoft.gradient(startColorstr='#00CC9A81', endColorstr='#FF000000'); 172 | } 173 | 174 | .sp-hue { 175 | background: -moz-linear-gradient(top, #ff0000 0%, #ffff00 17%, #00ff00 33%, #00ffff 50%, #0000ff 67%, #ff00ff 83%, #ff0000 100%); 176 | background: -ms-linear-gradient(top, #ff0000 0%, #ffff00 17%, #00ff00 33%, #00ffff 50%, #0000ff 67%, #ff00ff 83%, #ff0000 100%); 177 | background: -o-linear-gradient(top, #ff0000 0%, #ffff00 17%, #00ff00 33%, #00ffff 50%, #0000ff 67%, #ff00ff 83%, #ff0000 100%); 178 | background: -webkit-gradient(linear, left top, left bottom, from(#ff0000), color-stop(0.17, #ffff00), color-stop(0.33, #00ff00), color-stop(0.5, #00ffff), color-stop(0.67, #0000ff), color-stop(0.83, #ff00ff), to(#ff0000)); 179 | background: -webkit-linear-gradient(top, #ff0000 0%, #ffff00 17%, #00ff00 33%, #00ffff 50%, #0000ff 67%, #ff00ff 83%, #ff0000 100%); 180 | background: linear-gradient(to bottom, #ff0000 0%, #ffff00 17%, #00ff00 33%, #00ffff 50%, #0000ff 67%, #ff00ff 83%, #ff0000 100%); 181 | } 182 | 183 | /* IE filters do not support multiple color stops. 184 | Generate 6 divs, line them up, and do two color gradients for each. 185 | Yes, really. 186 | */ 187 | .sp-1 { 188 | height:17%; 189 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff0000', endColorstr='#ffff00'); 190 | } 191 | .sp-2 { 192 | height:16%; 193 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffff00', endColorstr='#00ff00'); 194 | } 195 | .sp-3 { 196 | height:17%; 197 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#00ff00', endColorstr='#00ffff'); 198 | } 199 | .sp-4 { 200 | height:17%; 201 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#00ffff', endColorstr='#0000ff'); 202 | } 203 | .sp-5 { 204 | height:16%; 205 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#0000ff', endColorstr='#ff00ff'); 206 | } 207 | .sp-6 { 208 | height:17%; 209 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff00ff', endColorstr='#ff0000'); 210 | } 211 | 212 | .sp-hidden { 213 | display: none !important; 214 | } 215 | 216 | /* Clearfix hack */ 217 | .sp-cf:before, .sp-cf:after { content: ""; display: table; } 218 | .sp-cf:after { clear: both; } 219 | .sp-cf { *zoom: 1; } 220 | 221 | /* Mobile devices, make hue slider bigger so it is easier to slide */ 222 | @media (max-device-width: 480px) { 223 | .sp-color { right: 40%; } 224 | .sp-hue { left: 63%; } 225 | .sp-fill { padding-top: 60%; } 226 | } 227 | .sp-dragger { 228 | border-radius: 5px; 229 | height: 5px; 230 | width: 5px; 231 | border: 1px solid #fff; 232 | background: #000; 233 | cursor: pointer; 234 | position:absolute; 235 | top:0; 236 | left: 0; 237 | } 238 | .sp-slider { 239 | position: absolute; 240 | top:0; 241 | cursor:pointer; 242 | height: 3px; 243 | left: -1px; 244 | right: -1px; 245 | border: 1px solid #000; 246 | background: white; 247 | opacity: .8; 248 | } 249 | 250 | /* 251 | Theme authors: 252 | Here are the basic themeable display options (colors, fonts, global widths). 253 | See http://bgrins.github.io/spectrum/themes/ for instructions. 254 | */ 255 | 256 | .sp-container { 257 | border-radius: 0; 258 | background-color: #ECECEC; 259 | border: solid 1px #f0c49B; 260 | padding: 0; 261 | } 262 | .sp-container, .sp-container button, .sp-container input, .sp-color, .sp-hue, .sp-clear { 263 | font: normal 12px "Lucida Grande", "Lucida Sans Unicode", "Lucida Sans", Geneva, Verdana, sans-serif; 264 | -webkit-box-sizing: border-box; 265 | -moz-box-sizing: border-box; 266 | -ms-box-sizing: border-box; 267 | box-sizing: border-box; 268 | } 269 | .sp-top { 270 | margin-bottom: 3px; 271 | } 272 | .sp-color, .sp-hue, .sp-clear { 273 | border: solid 1px #666; 274 | } 275 | 276 | /* Input */ 277 | .sp-input-container { 278 | float:right; 279 | width: 100px; 280 | margin-bottom: 4px; 281 | } 282 | .sp-initial-disabled .sp-input-container { 283 | width: 100%; 284 | } 285 | .sp-input { 286 | font-size: 12px !important; 287 | border: 1px inset; 288 | padding: 4px 5px; 289 | margin: 0; 290 | width: 100%; 291 | background:transparent; 292 | border-radius: 3px; 293 | color: #222; 294 | } 295 | .sp-input:focus { 296 | border: 1px solid orange; 297 | } 298 | .sp-input.sp-validation-error { 299 | border: 1px solid red; 300 | background: #fdd; 301 | } 302 | .sp-picker-container , .sp-palette-container { 303 | float:left; 304 | position: relative; 305 | padding: 10px; 306 | padding-bottom: 300px; 307 | margin-bottom: -290px; 308 | } 309 | .sp-picker-container { 310 | width: 172px; 311 | border-left: solid 1px #fff; 312 | } 313 | 314 | /* Palettes */ 315 | .sp-palette-container { 316 | border-right: solid 1px #ccc; 317 | } 318 | 319 | .sp-palette-only .sp-palette-container { 320 | border: 0; 321 | } 322 | 323 | .sp-palette .sp-thumb-el { 324 | display: block; 325 | position:relative; 326 | float:left; 327 | width: 24px; 328 | height: 15px; 329 | margin: 3px; 330 | cursor: pointer; 331 | border:solid 2px transparent; 332 | } 333 | .sp-palette .sp-thumb-el:hover, .sp-palette .sp-thumb-el.sp-thumb-active { 334 | border-color: orange; 335 | } 336 | .sp-thumb-el { 337 | position:relative; 338 | } 339 | 340 | /* Initial */ 341 | .sp-initial { 342 | float: left; 343 | border: solid 1px #333; 344 | } 345 | .sp-initial span { 346 | width: 30px; 347 | height: 25px; 348 | border:none; 349 | display:block; 350 | float:left; 351 | margin:0; 352 | } 353 | 354 | .sp-initial .sp-clear-display { 355 | background-position: center; 356 | } 357 | 358 | /* Buttons */ 359 | .sp-palette-button-container, 360 | .sp-button-container { 361 | float: right; 362 | } 363 | 364 | /* Replacer (the little preview div that shows up instead of the ) */ 365 | .sp-replacer { 366 | margin:0; 367 | overflow:hidden; 368 | cursor:pointer; 369 | padding: 4px; 370 | display:inline-block; 371 | *zoom: 1; 372 | *display: inline; 373 | border: solid 1px #91765d; 374 | background: #eee; 375 | color: #333; 376 | vertical-align: middle; 377 | } 378 | .sp-replacer:hover, .sp-replacer.sp-active { 379 | border-color: #F0C49B; 380 | color: #111; 381 | } 382 | .sp-replacer.sp-disabled { 383 | cursor:default; 384 | border-color: silver; 385 | color: silver; 386 | } 387 | .sp-dd { 388 | padding: 2px 0; 389 | height: 16px; 390 | line-height: 16px; 391 | float:left; 392 | font-size:10px; 393 | } 394 | .sp-preview { 395 | position:relative; 396 | width:25px; 397 | height: 20px; 398 | border: solid 1px #222; 399 | margin-right: 5px; 400 | float:left; 401 | z-index: 0; 402 | } 403 | 404 | .sp-palette { 405 | *width: 220px; 406 | max-width: 220px; 407 | } 408 | .sp-palette .sp-thumb-el { 409 | width:16px; 410 | height: 16px; 411 | margin:2px 1px; 412 | border: solid 1px #d0d0d0; 413 | } 414 | 415 | .sp-container { 416 | padding-bottom:0; 417 | } 418 | 419 | 420 | /* Buttons: http://hellohappy.org/css3-buttons/ */ 421 | .sp-container button { 422 | background-color: #eeeeee; 423 | background-image: -webkit-linear-gradient(top, #eeeeee, #cccccc); 424 | background-image: -moz-linear-gradient(top, #eeeeee, #cccccc); 425 | background-image: -ms-linear-gradient(top, #eeeeee, #cccccc); 426 | background-image: -o-linear-gradient(top, #eeeeee, #cccccc); 427 | background-image: linear-gradient(to bottom, #eeeeee, #cccccc); 428 | border: 1px solid #ccc; 429 | border-bottom: 1px solid #bbb; 430 | border-radius: 3px; 431 | color: #333; 432 | font-size: 14px; 433 | line-height: 1; 434 | padding: 5px 4px; 435 | text-align: center; 436 | text-shadow: 0 1px 0 #eee; 437 | vertical-align: middle; 438 | } 439 | .sp-container button:hover { 440 | background-color: #dddddd; 441 | background-image: -webkit-linear-gradient(top, #dddddd, #bbbbbb); 442 | background-image: -moz-linear-gradient(top, #dddddd, #bbbbbb); 443 | background-image: -ms-linear-gradient(top, #dddddd, #bbbbbb); 444 | background-image: -o-linear-gradient(top, #dddddd, #bbbbbb); 445 | background-image: linear-gradient(to bottom, #dddddd, #bbbbbb); 446 | border: 1px solid #bbb; 447 | border-bottom: 1px solid #999; 448 | cursor: pointer; 449 | text-shadow: 0 1px 0 #ddd; 450 | } 451 | .sp-container button:active { 452 | border: 1px solid #aaa; 453 | border-bottom: 1px solid #888; 454 | -webkit-box-shadow: inset 0 0 5px 2px #aaaaaa, 0 1px 0 0 #eeeeee; 455 | -moz-box-shadow: inset 0 0 5px 2px #aaaaaa, 0 1px 0 0 #eeeeee; 456 | -ms-box-shadow: inset 0 0 5px 2px #aaaaaa, 0 1px 0 0 #eeeeee; 457 | -o-box-shadow: inset 0 0 5px 2px #aaaaaa, 0 1px 0 0 #eeeeee; 458 | box-shadow: inset 0 0 5px 2px #aaaaaa, 0 1px 0 0 #eeeeee; 459 | } 460 | .sp-cancel { 461 | font-size: 11px; 462 | color: #d93f3f !important; 463 | margin:0; 464 | padding:2px; 465 | margin-right: 5px; 466 | vertical-align: middle; 467 | text-decoration:none; 468 | 469 | } 470 | .sp-cancel:hover { 471 | color: #d93f3f !important; 472 | text-decoration: underline; 473 | } 474 | 475 | 476 | .sp-palette span:hover, .sp-palette span.sp-thumb-active { 477 | border-color: #000; 478 | } 479 | 480 | .sp-preview, .sp-alpha, .sp-thumb-el { 481 | position:relative; 482 | background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAAMCAIAAADZF8uwAAAAGUlEQVQYV2M4gwH+YwCGIasIUwhT25BVBADtzYNYrHvv4gAAAABJRU5ErkJggg==); 483 | } 484 | .sp-preview-inner, .sp-alpha-inner, .sp-thumb-inner { 485 | display:block; 486 | position:absolute; 487 | top:0;left:0;bottom:0;right:0; 488 | } 489 | 490 | .sp-palette .sp-thumb-inner { 491 | background-position: 50% 50%; 492 | background-repeat: no-repeat; 493 | } 494 | 495 | .sp-palette .sp-thumb-light.sp-thumb-active .sp-thumb-inner { 496 | background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABIAAAASCAYAAABWzo5XAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAIVJREFUeNpiYBhsgJFMffxAXABlN5JruT4Q3wfi/0DsT64h8UD8HmpIPCWG/KemIfOJCUB+Aoacx6EGBZyHBqI+WsDCwuQ9mhxeg2A210Ntfo8klk9sOMijaURm7yc1UP2RNCMbKE9ODK1HM6iegYLkfx8pligC9lCD7KmRof0ZhjQACDAAceovrtpVBRkAAAAASUVORK5CYII=); 497 | } 498 | 499 | .sp-palette .sp-thumb-dark.sp-thumb-active .sp-thumb-inner { 500 | background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABIAAAASCAYAAABWzo5XAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAadEVYdFNvZnR3YXJlAFBhaW50Lk5FVCB2My41LjEwMPRyoQAAAMdJREFUOE+tkgsNwzAMRMugEAahEAahEAZhEAqlEAZhEAohEAYh81X2dIm8fKpEspLGvudPOsUYpxE2BIJCroJmEW9qJ+MKaBFhEMNabSy9oIcIPwrB+afvAUFoK4H0tMaQ3XtlrggDhOVVMuT4E5MMG0FBbCEYzjYT7OxLEvIHQLY2zWwQ3D+9luyOQTfKDiFD3iUIfPk8VqrKjgAiSfGFPecrg6HN6m/iBcwiDAo7WiBeawa+Kwh7tZoSCGLMqwlSAzVDhoK+6vH4G0P5wdkAAAAASUVORK5CYII=); 501 | } 502 | 503 | .sp-clear-display { 504 | background-repeat:no-repeat; 505 | background-position: center; 506 | background-image: url(data:image/gif;base64,R0lGODlhFAAUAPcAAAAAAJmZmZ2dnZ6enqKioqOjo6SkpKWlpaampqenp6ioqKmpqaqqqqurq/Hx8fLy8vT09PX19ff39/j4+Pn5+fr6+vv7+wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAEAAP8ALAAAAAAUABQAAAihAP9FoPCvoMGDBy08+EdhQAIJCCMybCDAAYUEARBAlFiQQoMABQhKUJBxY0SPICEYHBnggEmDKAuoPMjS5cGYMxHW3IiT478JJA8M/CjTZ0GgLRekNGpwAsYABHIypcAgQMsITDtWJYBR6NSqMico9cqR6tKfY7GeBCuVwlipDNmefAtTrkSzB1RaIAoXodsABiZAEFB06gIBWC1mLVgBa0AAOw==); 507 | } 508 | -------------------------------------------------------------------------------- /src/html/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Image Editor 7 | 8 | 12 | 13 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 |
36 | 37 |
38 | 39 | 56 | 57 |
58 |
59 | 162 |
163 | 164 |
165 |
166 | 167 |
168 | 169 |
170 |
Undo
171 | 172 |
173 |
Redo
174 | 175 |
176 | 177 |
178 |
Insert Text
179 | 180 |
181 |
182 | 183 |
184 | 188 | 192 | 196 | 200 |
201 | 202 |
203 |
Insert Shape
204 | 205 |
206 | 207 |
208 |
209 | 210 |
211 | Arrange 212 |
213 | 214 |
215 | 219 | 223 | 227 | 231 |
232 | 233 |
234 |
Arrange Objects
235 | 236 |
237 | 238 |
239 | 240 |
241 | 242 | 251 | 252 |
253 | 254 |
255 | 256 |
257 | 260 |
261 | 262 |
263 |
Font
264 | 265 |
266 | 267 |
268 |
Bold
269 | 270 |
271 |
Italics
272 | 273 |
274 |
Underline
275 | 276 |
277 | 278 |
279 | 280 |
281 | 282 |
283 |
284 |
285 |
Fill Color
286 | 287 |
288 |
289 |
290 |
Outline Color
291 | 292 |
293 |
294 | 295 |
296 | 297 | 298 | 299 |
300 | 304 | 305 |
306 | Offset
307 |
308 | 309 |
310 | 311 | Blur
312 |
313 | 314 |
315 | 316 | Color
317 | 318 | 319 | 320 |
321 | 322 | 323 | 324 | 328 | 329 |
330 | Size
331 |
332 | 333 |
334 | 335 | Color
336 | 337 | 338 | 339 |
340 | 341 |
342 | 343 |
344 | 345 |
346 |
Effects
347 | 348 |
349 | 350 |
351 | 352 |
353 | 354 |
355 | 356 |
357 | 358 |
359 | 360 |
361 | 362 |
363 | 364 |
365 | 366 |
367 |
368 | 369 |
370 |
371 |
372 | 373 |
374 |
375 |
376 | 377 |
378 | Preview 379 |
380 | 381 |
382 | Download 383 |
384 | 385 |
386 |
387 | 388 | 389 | 390 | 391 | 392 | 393 | 394 | 395 | 414 | 415 | 416 | 417 | -------------------------------------------------------------------------------- /src/js/lib/jquery.contextMenu.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * jQuery contextMenu v2.0.0 - Plugin for simple contextMenu handling 3 | * 4 | * Version: v2.0.0 5 | * 6 | * Authors: Björn Brala (SWIS.nl), Rodney Rehm, Addy Osmani (patches for FF) 7 | * Web: http://swisnl.github.io/jQuery-contextMenu/ 8 | * 9 | * Copyright (c) 2011-2015 SWIS BV and contributors 10 | * 11 | * Licensed under 12 | * MIT License http://www.opensource.org/licenses/mit-license 13 | * GPL v3 http://opensource.org/licenses/GPL-3.0 14 | * 15 | * Date: 2015-10-28T15:16:15.572Z 16 | */ 17 | !function(e){"function"==typeof define&&define.amd?define(["jquery"],e):e("object"==typeof exports?require("jquery"):jQuery)}(function(e){"use strict";function t(e){for(var t,n=e.split(/\s+/),a=[],o=0;t=n[o];o++)t=t.charAt(0).toUpperCase(),a.push(t);return a}function n(t){return t.id&&e('label[for="'+t.id+'"]').val()||t.name}function a(t,o,s){return s||(s=0),o.each(function(){var o,i,c=e(this),r=this,l=this.nodeName.toLowerCase();switch("label"===l&&c.find("input, textarea, select").length&&(o=c.text(),c=c.children().first(),r=c.get(0),l=r.nodeName.toLowerCase()),l){case"menu":i={name:c.attr("label"),items:{}},s=a(i.items,c.children(),s);break;case"a":case"button":i={name:c.text(),disabled:!!c.attr("disabled"),callback:function(){return function(){c.click()}}()};break;case"menuitem":case"command":switch(c.attr("type")){case void 0:case"command":case"menuitem":i={name:c.attr("label"),disabled:!!c.attr("disabled"),icon:c.attr("icon"),callback:function(){return function(){c.click()}}()};break;case"checkbox":i={type:"checkbox",disabled:!!c.attr("disabled"),name:c.attr("label"),selected:!!c.attr("checked")};break;case"radio":i={type:"radio",disabled:!!c.attr("disabled"),name:c.attr("label"),radio:c.attr("radiogroup"),value:c.attr("id"),selected:!!c.attr("checked")};break;default:i=void 0}break;case"hr":i="-------";break;case"input":switch(c.attr("type")){case"text":i={type:"text",name:o||n(r),disabled:!!c.attr("disabled"),value:c.val()};break;case"checkbox":i={type:"checkbox",name:o||n(r),disabled:!!c.attr("disabled"),selected:!!c.attr("checked")};break;case"radio":i={type:"radio",name:o||n(r),disabled:!!c.attr("disabled"),radio:!!c.attr("name"),value:c.val(),selected:!!c.attr("checked")};break;default:i=void 0}break;case"select":i={type:"select",name:o||n(r),disabled:!!c.attr("disabled"),selected:c.val(),options:{}},c.children().each(function(){i.options[this.value]=e(this).text()});break;case"textarea":i={type:"textarea",name:o||n(r),disabled:!!c.attr("disabled"),value:c.val()};break;case"label":break;default:i={type:"html",html:c.clone(!0)}}i&&(s++,t["key"+s]=i)}),s}e.support.htmlMenuitem="HTMLMenuItemElement"in window,e.support.htmlCommand="HTMLCommandElement"in window,e.support.eventSelectstart="onselectstart"in document.documentElement,e.ui&&e.widget||(e.cleanData=function(t){return function(n){var a,o,s;for(s=0;null!=(o=n[s]);s++)try{a=e._data(o,"events"),a&&a.remove&&e(o).triggerHandler("remove")}catch(i){}t(n)}}(e.cleanData));var o=null,s=!1,i=e(window),c=0,r={},l={},u={},d={selector:null,appendTo:null,trigger:"right",autoHide:!1,delay:200,reposition:!0,classNames:{hover:"context-menu-hover",disabled:"context-menu-disabled",visible:"context-menu-visible",notSelectable:"context-menu-not-selectable",icon:"context-menu-icon",iconEdit:"context-menu-icon-edit",iconCut:"context-menu-icon-cut",iconCopy:"context-menu-icon-copy",iconPaste:"context-menu-icon-paste",iconDelete:"context-menu-icon-delete",iconAdd:"context-menu-icon-add",iconQuit:"context-menu-icon-quit"},determinePosition:function(t){if(e.ui&&e.ui.position)t.css("display","block").position({my:"center top",at:"center bottom",of:this,offset:"0 5",collision:"fit"}).css("display","none");else{var n=this.offset();n.top+=this.outerHeight(),n.left+=this.outerWidth()/2-t.outerWidth()/2,t.css(n)}},position:function(e,t,n){var a;if(!t&&!n)return void e.determinePosition.call(this,e.$menu);a="maintain"===t&&"maintain"===n?e.$menu.position():{top:n,left:t};var o=i.scrollTop()+i.height(),s=i.scrollLeft()+i.width(),c=e.$menu.outerHeight(),r=e.$menu.outerWidth();a.top+c>o&&(a.top-=c),a.top<0&&(a.top=0),a.left+r>s&&(a.left-=r),a.left<0&&(a.left=0),e.$menu.css(a)},positionSubmenu:function(t){if(e.ui&&e.ui.position)t.css("display","block").position({my:"left top",at:"right top",of:this,collision:"flipfit fit"}).css("display","");else{var n={top:0,left:this.outerWidth()};t.css(n)}},zIndex:1,animation:{duration:50,show:"slideDown",hide:"slideUp"},events:{show:e.noop,hide:e.noop},callback:null,items:{}},m={timer:null,pageX:null,pageY:null},p=function(e){for(var t=0,n=e;;)if(t=Math.max(t,parseInt(n.css("z-index"),10)||0),n=n.parent(),!n||!n.length||"html body".indexOf(n.prop("nodeName").toLowerCase())>-1)break;return t},f={abortevent:function(e){e.preventDefault(),e.stopImmediatePropagation()},contextmenu:function(t){var n=e(this);if("right"===t.data.trigger&&(t.preventDefault(),t.stopImmediatePropagation()),!("right"!==t.data.trigger&&"demand"!==t.data.trigger&&t.originalEvent||n.hasClass("context-menu-active")||n.hasClass("context-menu-disabled"))){if(o=n,t.data.build){var a=t.data.build(o,t);if(a===!1)return;if(t.data=e.extend(!0,{},d,t.data,a||{}),!t.data.items||e.isEmptyObject(t.data.items))throw window.console&&(console.error||console.log).call(console,"No items specified to show in contextMenu"),new Error("No Items specified");t.data.$trigger=o,h.create(t.data)}var s=!1;for(var i in t.data.items)if(t.data.items.hasOwnProperty(i)){var c;c=e.isFunction(t.data.items[i].visible)?t.data.items[i].visible.call(e(t.currentTarget),i,t.data):"undefined"!=typeof i.visible?t.data.items[i].visible===!0:!0,c&&(s=!0)}s&&h.show.call(n,t.data,t.pageX,t.pageY)}},click:function(t){t.preventDefault(),t.stopImmediatePropagation(),e(this).trigger(e.Event("contextmenu",{data:t.data,pageX:t.pageX,pageY:t.pageY}))},mousedown:function(t){var n=e(this);o&&o.length&&!o.is(n)&&o.data("contextMenu").$menu.trigger("contextmenu:hide"),2===t.button&&(o=n.data("contextMenuActive",!0))},mouseup:function(t){var n=e(this);n.data("contextMenuActive")&&o&&o.length&&o.is(n)&&!n.hasClass("context-menu-disabled")&&(t.preventDefault(),t.stopImmediatePropagation(),o=n,n.trigger(e.Event("contextmenu",{data:t.data,pageX:t.pageX,pageY:t.pageY}))),n.removeData("contextMenuActive")},mouseenter:function(t){var n=e(this),a=e(t.relatedTarget),s=e(document);a.is(".context-menu-list")||a.closest(".context-menu-list").length||o&&o.length||(m.pageX=t.pageX,m.pageY=t.pageY,m.data=t.data,s.on("mousemove.contextMenuShow",f.mousemove),m.timer=setTimeout(function(){m.timer=null,s.off("mousemove.contextMenuShow"),o=n,n.trigger(e.Event("contextmenu",{data:m.data,pageX:m.pageX,pageY:m.pageY}))},t.data.delay))},mousemove:function(e){m.pageX=e.pageX,m.pageY=e.pageY},mouseleave:function(t){var n=e(t.relatedTarget);if(!n.is(".context-menu-list")&&!n.closest(".context-menu-list").length){try{clearTimeout(m.timer)}catch(t){}m.timer=null}},layerClick:function(t){var n,a,o=e(this),s=o.data("contextMenuRoot"),c=t.button,r=t.pageX,l=t.pageY;t.preventDefault(),t.stopImmediatePropagation(),setTimeout(function(){var o,u="left"===s.trigger&&0===c||"right"===s.trigger&&2===c;if(document.elementFromPoint&&(s.$layer.hide(),n=document.elementFromPoint(r-i.scrollLeft(),l-i.scrollTop()),s.$layer.show()),s.reposition&&u)if(document.elementFromPoint){if(s.$trigger.is(n)||s.$trigger.has(n).length)return void s.position.call(s.$trigger,s,r,l)}else if(a=s.$trigger.offset(),o=e(window),a.top+=o.scrollTop(),a.top<=t.pageY&&(a.left+=o.scrollLeft(),a.left<=t.pageX&&(a.bottom=a.top+s.$trigger.outerHeight(),a.bottom>=t.pageY&&(a.right=a.left+s.$trigger.outerWidth(),a.right>=t.pageX))))return void s.position.call(s.$trigger,s,r,l);n&&u&&s.$trigger.one("contextmenu:hidden",function(){e(n).contextMenu({x:r,y:l})}),s.$menu.trigger("contextmenu:hide")},50)},keyStop:function(e,t){t.isInput||e.preventDefault(),e.stopPropagation()},key:function(e){var t={};switch(o&&(t=o.data("contextMenu")||{}),e.keyCode){case 9:case 38:if(f.keyStop(e,t),t.isInput){if(9===e.keyCode&&e.shiftKey)return e.preventDefault(),t.$selected&&t.$selected.find("input, textarea, select").blur(),void t.$menu.trigger("prevcommand");if(38===e.keyCode&&"checkbox"===t.$selected.find("input, textarea, select").prop("type"))return void e.preventDefault()}else if(9!==e.keyCode||e.shiftKey)return void t.$menu.trigger("prevcommand");case 40:if(f.keyStop(e,t),!t.isInput)return void t.$menu.trigger("nextcommand");if(9===e.keyCode)return e.preventDefault(),t.$selected&&t.$selected.find("input, textarea, select").blur(),void t.$menu.trigger("nextcommand");if(40===e.keyCode&&"checkbox"===t.$selected.find("input, textarea, select").prop("type"))return void e.preventDefault();break;case 37:if(f.keyStop(e,t),t.isInput||!t.$selected||!t.$selected.length)break;if(!t.$selected.parent().hasClass("context-menu-root")){var n=t.$selected.parent().parent();return t.$selected.trigger("contextmenu:blur"),void(t.$selected=n)}break;case 39:if(f.keyStop(e,t),t.isInput||!t.$selected||!t.$selected.length)break;var a=t.$selected.data("contextMenu")||{};if(a.$menu&&t.$selected.hasClass("context-menu-submenu"))return t.$selected=null,a.$selected=null,void a.$menu.trigger("nextcommand");break;case 35:case 36:return t.$selected&&t.$selected.find("input, textarea, select").length?void 0:((t.$selected&&t.$selected.parent()||t.$menu).children(":not(."+t.classNames.disabled+", ."+t.classNames.notSelectable+")")[36===e.keyCode?"first":"last"]().trigger("contextmenu:focus"),void e.preventDefault());case 13:if(f.keyStop(e,t),t.isInput){if(t.$selected&&!t.$selected.is("textarea, select"))return void e.preventDefault();break}return void("undefined"!=typeof t.$selected&&null!==t.$selected&&t.$selected.trigger("mouseup"));case 32:case 33:case 34:return void f.keyStop(e,t);case 27:return f.keyStop(e,t),void t.$menu.trigger("contextmenu:hide");default:var s=String.fromCharCode(e.keyCode).toUpperCase();if(t.accesskeys&&t.accesskeys[s])return void t.accesskeys[s].$node.trigger(t.accesskeys[s].$menu?"contextmenu:focus":"mouseup")}e.stopPropagation(),"undefined"!=typeof t.$selected&&null!==t.$selected&&t.$selected.trigger(e)},prevItem:function(t){t.stopPropagation();var n=e(this).data("contextMenu")||{},a=e(this).data("contextMenuRoot")||{};if(n.$selected){var o=n.$selected;n=n.$selected.parent().data("contextMenu")||{},n.$selected=o}for(var s=n.$menu.children(),i=n.$selected&&n.$selected.prev().length?n.$selected.prev():s.last(),c=i;i.hasClass(a.classNames.disabled)||i.hasClass(a.classNames.notSelectable);)if(i=i.prev().length?i.prev():s.last(),i.is(c))return;n.$selected&&f.itemMouseleave.call(n.$selected.get(0),t),f.itemMouseenter.call(i.get(0),t);var r=i.find("input, textarea, select");r.length&&r.focus()},nextItem:function(t){t.stopPropagation();var n=e(this).data("contextMenu")||{},a=e(this).data("contextMenuRoot")||{};if(n.$selected){var o=n.$selected;n=n.$selected.parent().data("contextMenu")||{},n.$selected=o}for(var s=n.$menu.children(),i=n.$selected&&n.$selected.next().length?n.$selected.next():s.first(),c=i;i.hasClass(a.classNames.disabled)||i.hasClass(a.classNames.notSelectable);)if(i=i.next().length?i.next():s.first(),i.is(c))return;n.$selected&&f.itemMouseleave.call(n.$selected.get(0),t),f.itemMouseenter.call(i.get(0),t);var r=i.find("input, textarea, select");r.length&&r.focus()},focusInput:function(){var t=e(this).closest(".context-menu-item"),n=t.data(),a=n.contextMenu,o=n.contextMenuRoot;o.$selected=a.$selected=t,o.isInput=a.isInput=!0},blurInput:function(){var t=e(this).closest(".context-menu-item"),n=t.data(),a=n.contextMenu,o=n.contextMenuRoot;o.isInput=a.isInput=!1},menuMouseenter:function(){var t=e(this).data().contextMenuRoot;t.hovering=!0},menuMouseleave:function(t){var n=e(this).data().contextMenuRoot;n.$layer&&n.$layer.is(t.relatedTarget)&&(n.hovering=!1)},itemMouseenter:function(t){var n=e(this),a=n.data(),o=a.contextMenu,s=a.contextMenuRoot;return s.hovering=!0,t&&s.$layer&&s.$layer.is(t.relatedTarget)&&(t.preventDefault(),t.stopImmediatePropagation()),(o.$menu?o:s).$menu.children(".hover").trigger("contextmenu:blur"),n.hasClass(s.classNames.disabled)||n.hasClass(s.classNames.notSelectable)?void(o.$selected=null):void n.trigger("contextmenu:focus")},itemMouseleave:function(t){var n=e(this),a=n.data(),o=a.contextMenu,s=a.contextMenuRoot;return s!==o&&s.$layer&&s.$layer.is(t.relatedTarget)?("undefined"!=typeof s.$selected&&null!==s.$selected&&s.$selected.trigger("contextmenu:blur"),t.preventDefault(),t.stopImmediatePropagation(),void(s.$selected=o.$selected=o.$node)):void n.trigger("contextmenu:blur")},itemClick:function(t){var n,a=e(this),o=a.data(),s=o.contextMenu,i=o.contextMenuRoot,c=o.contextMenuKey;if(s.items[c]&&!a.is("."+i.classNames.disabled+", .context-menu-submenu, .context-menu-separator, ."+i.classNames.notSelectable)){if(t.preventDefault(),t.stopImmediatePropagation(),e.isFunction(i.callbacks[c])&&Object.prototype.hasOwnProperty.call(i.callbacks,c))n=i.callbacks[c];else{if(!e.isFunction(i.callback))return;n=i.callback}n.call(i.$trigger,c,i)!==!1?i.$menu.trigger("contextmenu:hide"):i.$menu.parent().length&&h.update.call(i.$trigger,i)}},inputClick:function(e){e.stopImmediatePropagation()},hideMenu:function(t,n){var a=e(this).data("contextMenuRoot");h.hide.call(a.$trigger,a,n&&n.force)},focusItem:function(t){t.stopPropagation();var n=e(this),a=n.data(),o=a.contextMenu,s=a.contextMenuRoot;n.addClass([s.classNames.hover,s.classNames.visible].join(" ")).siblings().removeClass(s.classNames.visible).filter(s.classNames.hover).trigger("contextmenu:blur"),o.$selected=s.$selected=n,o.$node&&s.positionSubmenu.call(o.$node,o.$menu)},blurItem:function(t){t.stopPropagation();var n=e(this),a=n.data(),o=a.contextMenu,s=a.contextMenuRoot;o.autoHide&&n.removeClass(s.classNames.visible),n.removeClass(s.classNames.hover),o.$selected=null}},h={show:function(t,n,a){var s=e(this),i={};return e("#context-menu-layer").trigger("mousedown"),t.$trigger=s,t.events.show.call(s,t)===!1?void(o=null):(h.update.call(s,t),t.position.call(s,t,n,a),t.zIndex&&(i.zIndex=p(s)+t.zIndex),h.layer.call(t.$menu,t,i.zIndex),t.$menu.find("ul").css("zIndex",i.zIndex+1),t.$menu.css(i)[t.animation.show](t.animation.duration,function(){s.trigger("contextmenu:visible")}),s.data("contextMenu",t).addClass("context-menu-active"),e(document).off("keydown.contextMenu").on("keydown.contextMenu",f.key),void(t.autoHide&&e(document).on("mousemove.contextMenuAutoHide",function(e){var n=s.offset();n.right=n.left+s.outerWidth(),n.bottom=n.top+s.outerHeight(),!t.$layer||t.hovering||e.pageX>=n.left&&e.pageX<=n.right&&e.pageY>=n.top&&e.pageY<=n.bottom||t.$menu.trigger("contextmenu:hide")})))},hide:function(t,n){var a=e(this);if(t||(t=a.data("contextMenu")||{}),n||!t.events||t.events.hide.call(a,t)!==!1){if(a.removeData("contextMenu").removeClass("context-menu-active"),t.$layer){setTimeout(function(e){return function(){e.remove()}}(t.$layer),10);try{delete t.$layer}catch(s){t.$layer=null}}o=null,t.$menu.find("."+t.classNames.hover).trigger("contextmenu:blur"),t.$selected=null,e(document).off(".contextMenuAutoHide").off("keydown.contextMenu"),t.$menu&&t.$menu[t.animation.hide](t.animation.duration,function(){t.build&&(t.$menu.remove(),e.each(t,function(e){switch(e){case"ns":case"selector":case"build":case"trigger":return!0;default:t[e]=void 0;try{delete t[e]}catch(n){}return!0}})),setTimeout(function(){a.trigger("contextmenu:hidden")},10)})}},create:function(n,a){function o(t){var n=e("");return t._accesskey?(t._beforeAccesskey&&n.append(document.createTextNode(t._beforeAccesskey)),e("").addClass("context-menu-accesskey").text(t._accesskey).appendTo(n),t._afterAccesskey&&n.append(document.createTextNode(t._afterAccesskey))):n.text(t.name),n}void 0===a&&(a=n),n.$menu=e('
    ').addClass(n.className||"").data({contextMenu:n,contextMenuRoot:a}),e.each(["callbacks","commands","inputs"],function(e,t){n[t]={},a[t]||(a[t]={})}),a.accesskeys||(a.accesskeys={}),e.each(n.items,function(s,i){var c=e('
  • ').addClass(i.className||""),r=null,l=null;if(c.on("click",e.noop),"string"==typeof i&&(i={type:"cm_seperator"}),i.$node=c.data({contextMenu:n,contextMenuRoot:a,contextMenuKey:s}),"undefined"!=typeof i.accesskey)for(var d,m=t(i.accesskey),p=0;d=m[p];p++)if(!a.accesskeys[d]){a.accesskeys[d]=i;var x=i.name.match(new RegExp("^(.*?)("+d+")(.*)$","i"));x&&(i._beforeAccesskey=x[1],i._accesskey=x[2],i._afterAccesskey=x[3]);break}if(i.type&&u[i.type])u[i.type].call(c,i,n,a),e.each([n,a],function(t,n){n.commands[s]=i,e.isFunction(i.callback)&&(n.callbacks[s]=i.callback)});else{switch("cm_seperator"===i.type?c.addClass("context-menu-separator "+a.classNames.notSelectable):"html"===i.type?c.addClass("context-menu-html "+a.classNames.notSelectable):i.type?(r=e("").appendTo(c),o(i).appendTo(c),c.addClass("context-menu-input"),n.hasTypes=!0,e.each([n,a],function(e,t){t.commands[s]=i,t.inputs[s]=i})):i.items&&(i.type="sub"),i.type){case"seperator":break;case"text":l=e('').attr("name","context-menu-input-"+s).val(i.value||"").appendTo(r);break;case"textarea":l=e('').attr("name","context-menu-input-"+s).val(i.value||"").appendTo(r),i.height&&l.height(i.height);break;case"checkbox":l=e('').attr("name","context-menu-input-"+s).val(i.value||"").prop("checked",!!i.selected).prependTo(r);break;case"radio":l=e('').attr("name","context-menu-input-"+i.radio).val(i.value||"").prop("checked",!!i.selected).prependTo(r);break;case"select":l=e('