├── LICENSE.txt ├── assets └── cursors.ai ├── css └── style.css ├── editor.html ├── img ├── cursor-arrow-black-point.png ├── cursor-arrow-black-shape.png ├── cursor-arrow-black.png ├── cursor-arrow-duplicate.png ├── cursor-arrow-small-point.png ├── cursor-arrow-small.png ├── cursor-arrow-white-point.png ├── cursor-arrow-white-shape.png ├── cursor-arrow-white.png ├── cursor-hand-grab.png ├── cursor-hand.png ├── cursor-pen-add.ico ├── cursor-pen-add.png ├── cursor-pen-adjust.png ├── cursor-pen-close.png ├── cursor-pen-create.png ├── cursor-pen-edit.png ├── cursor-pen-freehand.png ├── cursor-pen-join.png ├── cursor-pen-remove.png ├── cursor-rotate-0.png ├── cursor-rotate-135.png ├── cursor-rotate-180.png ├── cursor-rotate-225.png ├── cursor-rotate-270.png ├── cursor-rotate-315.png ├── cursor-rotate-45.png ├── cursor-rotate-90.png ├── cursor-scale-0.png ├── cursor-scale-135.png ├── cursor-scale-45.png ├── cursor-scale-90.png ├── cursor-zoom-in.png ├── cursor-zoom-out.png ├── cursor-zoom.png ├── icon-arrow-black.png ├── icon-arrow-white.png ├── icon-hand.png └── icon-pen.png ├── js ├── editor.js ├── jquery.js └── paper.js └── todo.md /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013-2014 Mikko Mononen memon@inside.org 2 | 3 | This software is provided 'as-is', without any express or implied 4 | warranty. In no event will the authors be held liable for any damages 5 | arising from the use of this software. 6 | 7 | Permission is granted to anyone to use this software for any purpose, 8 | including commercial applications, and to alter it and redistribute it 9 | freely, subject to the following restrictions: 10 | 11 | 1. The origin of this software must not be misrepresented; you must not 12 | claim that you wrote the original software. If you use this software 13 | in a product, an acknowledgment in the product documentation would be 14 | appreciated but is not required. 15 | 2. Altered source versions must be plainly marked as such, and must not be 16 | misrepresented as being the original software. 17 | 3. This notice may not be removed or altered from any source distribution. 18 | 19 | -------------------------------------------------------------------------------- /assets/cursors.ai: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/memononen/stylii/39af974399bc6d36c2ddb94b4ffa1832bd928ebe/assets/cursors.ai -------------------------------------------------------------------------------- /css/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | overflow: hidden; 4 | font-family: Helvetica, sans-serif; 5 | font-size: 14px; 6 | } 7 | 8 | #tools { 9 | position: fixed; 10 | left: 40px; 11 | top: 50px; 12 | width: 28px; 13 | padding: 0; 14 | background: #fff; 15 | background: rgba(255,255,255,0.8); 16 | margin: 0; 17 | box-shadow: 0px 3px 6px rgba(0,0,0,0.4); 18 | } 19 | 20 | #menu { 21 | position: fixed; 22 | left: 5px; 23 | top: 5px; 24 | padding: 4px; 25 | background: rgba(255,255,255,0.5); 26 | } 27 | 28 | #menu a { 29 | padding: 2px 6px; 30 | text-decoration: none; 31 | color: #000; 32 | cursor: default; 33 | } 34 | 35 | #menu a.disabled { 36 | color: #888; 37 | } 38 | 39 | #menu a:hover { 40 | background: rgba(0,0,0,0.9); 41 | color: #fff; 42 | } 43 | 44 | #menu a.disabled:hover { 45 | background: transparent; 46 | color: #888; 47 | } 48 | 49 | #menu a:active { 50 | background: rgba(0,0,0,1.0); 51 | color: #ddd; 52 | } 53 | 54 | .button { 55 | display: inline-block; 56 | cursor: default; 57 | padding: 4px; 58 | } 59 | 60 | .button:last-child { 61 | margin-bottom: 0px; 62 | } 63 | 64 | .button:hover { 65 | background: #222; 66 | } 67 | 68 | .button.selected { 69 | background: #888; 70 | } 71 | 72 | .button.selected:hover { 73 | background: rgba(0,0,0,0.9); 74 | } 75 | 76 | .cursor-pen-create { 77 | cursor: url('../img/cursor-pen-create.png') 6 2, default; 78 | } 79 | 80 | .cursor-pen-add { 81 | cursor: url('../img/cursor-pen-add.png') 6 2, default; 82 | } 83 | 84 | .cursor-pen-remove { 85 | cursor: url('../img/cursor-pen-remove.png') 6 2, default; 86 | } 87 | 88 | .cursor-pen-edit { 89 | cursor: url('../img/cursor-pen-edit.png') 6 2, default; 90 | } 91 | 92 | .cursor-pen-close { 93 | cursor: url('../img/cursor-pen-close.png') 6 2, default; 94 | } 95 | 96 | .cursor-pen-adjust { 97 | cursor: url('../img/cursor-pen-adjust.png') 6 2, default; 98 | } 99 | 100 | .cursor-pen-join { 101 | cursor: url('../img/cursor-pen-join.png') 6 2, default; 102 | } 103 | 104 | .cursor-zoom-in { 105 | cursor: url('../img/cursor-zoom-in.png') 7 7, default; 106 | } 107 | 108 | .cursor-zoom-out { 109 | cursor: url('../img/cursor-zoom-out.png') 7 7, default; 110 | } 111 | 112 | .cursor-hand { 113 | cursor: url('../img/cursor-hand.png') 10 10, default; 114 | } 115 | 116 | .cursor-hand-grab { 117 | cursor: url('../img/cursor-hand-grab.png') 10 10, default; 118 | } 119 | 120 | .cursor-arrow-black { 121 | cursor: url('../img/cursor-arrow-black.png') 2 2, default; 122 | } 123 | 124 | .cursor-arrow-black-point { 125 | cursor: url('../img/cursor-arrow-black-point.png') 2 2, default; 126 | } 127 | 128 | .cursor-arrow-black-shape { 129 | cursor: url('../img/cursor-arrow-black-shape.png') 2 2, default; 130 | } 131 | 132 | .cursor-arrow-white { 133 | cursor: url('../img/cursor-arrow-white.png') 2 2, default; 134 | } 135 | 136 | .cursor-arrow-white-point { 137 | cursor: url('../img/cursor-arrow-white-point.png') 2 2, default; 138 | } 139 | 140 | .cursor-arrow-white-shape { 141 | cursor: url('../img/cursor-arrow-white-shape.png') 2 2, default; 142 | } 143 | 144 | .cursor-arrow-small { 145 | cursor: url('../img/cursor-arrow-small.png') 2 2, default; 146 | } 147 | 148 | .cursor-arrow-small-point { 149 | cursor: url('../img/cursor-arrow-small-point.png') 2 2, default; 150 | } 151 | 152 | .cursor-arrow-duplicate { 153 | cursor: url('../img/cursor-arrow-duplicate.png') 2 2, default; 154 | } 155 | 156 | .cursor-scale-0 { 157 | cursor: url('../img/cursor-scale-0.png') 9 9, default; 158 | } 159 | 160 | .cursor-scale-45 { 161 | cursor: url('../img/cursor-scale-45.png') 9 9, default; 162 | } 163 | 164 | .cursor-scale-90 { 165 | cursor: url('../img/cursor-scale-90.png') 9 9, default; 166 | } 167 | 168 | .cursor-scale-135 { 169 | cursor: url('../img/cursor-scale-135.png') 9 9, default; 170 | } 171 | 172 | .cursor-rotate-0 { 173 | cursor: url('../img/cursor-rotate-0.png') 9 9, default; 174 | } 175 | 176 | .cursor-rotate-45 { 177 | cursor: url('../img/cursor-rotate-45.png') 9 9, default; 178 | } 179 | 180 | .cursor-rotate-90 { 181 | cursor: url('../img/cursor-rotate-90.png') 9 9, default; 182 | } 183 | 184 | .cursor-rotate-135 { 185 | cursor: url('../img/cursor-rotate-135.png') 9 9, default; 186 | } 187 | 188 | .cursor-rotate-180 { 189 | cursor: url('../img/cursor-rotate-180.png') 9 9, default; 190 | } 191 | 192 | .cursor-rotate-225 { 193 | cursor: url('../img/cursor-rotate-225.png') 9 9, default; 194 | } 195 | 196 | .cursor-rotate-270 { 197 | cursor: url('../img/cursor-rotate-270.png') 9 9, default; 198 | } 199 | 200 | .cursor-rotate-315 { 201 | cursor: url('../img/cursor-rotate-315.png') 9 9, default; 202 | } 203 | 204 | 205 | #canvas { 206 | } 207 | -------------------------------------------------------------------------------- /editor.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Stylii 6 | 7 | 8 | 9 | 10 | 11 | 12 | 22 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /img/cursor-arrow-black-point.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/memononen/stylii/39af974399bc6d36c2ddb94b4ffa1832bd928ebe/img/cursor-arrow-black-point.png -------------------------------------------------------------------------------- /img/cursor-arrow-black-shape.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/memononen/stylii/39af974399bc6d36c2ddb94b4ffa1832bd928ebe/img/cursor-arrow-black-shape.png -------------------------------------------------------------------------------- /img/cursor-arrow-black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/memononen/stylii/39af974399bc6d36c2ddb94b4ffa1832bd928ebe/img/cursor-arrow-black.png -------------------------------------------------------------------------------- /img/cursor-arrow-duplicate.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/memononen/stylii/39af974399bc6d36c2ddb94b4ffa1832bd928ebe/img/cursor-arrow-duplicate.png -------------------------------------------------------------------------------- /img/cursor-arrow-small-point.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/memononen/stylii/39af974399bc6d36c2ddb94b4ffa1832bd928ebe/img/cursor-arrow-small-point.png -------------------------------------------------------------------------------- /img/cursor-arrow-small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/memononen/stylii/39af974399bc6d36c2ddb94b4ffa1832bd928ebe/img/cursor-arrow-small.png -------------------------------------------------------------------------------- /img/cursor-arrow-white-point.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/memononen/stylii/39af974399bc6d36c2ddb94b4ffa1832bd928ebe/img/cursor-arrow-white-point.png -------------------------------------------------------------------------------- /img/cursor-arrow-white-shape.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/memononen/stylii/39af974399bc6d36c2ddb94b4ffa1832bd928ebe/img/cursor-arrow-white-shape.png -------------------------------------------------------------------------------- /img/cursor-arrow-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/memononen/stylii/39af974399bc6d36c2ddb94b4ffa1832bd928ebe/img/cursor-arrow-white.png -------------------------------------------------------------------------------- /img/cursor-hand-grab.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/memononen/stylii/39af974399bc6d36c2ddb94b4ffa1832bd928ebe/img/cursor-hand-grab.png -------------------------------------------------------------------------------- /img/cursor-hand.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/memononen/stylii/39af974399bc6d36c2ddb94b4ffa1832bd928ebe/img/cursor-hand.png -------------------------------------------------------------------------------- /img/cursor-pen-add.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/memononen/stylii/39af974399bc6d36c2ddb94b4ffa1832bd928ebe/img/cursor-pen-add.ico -------------------------------------------------------------------------------- /img/cursor-pen-add.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/memononen/stylii/39af974399bc6d36c2ddb94b4ffa1832bd928ebe/img/cursor-pen-add.png -------------------------------------------------------------------------------- /img/cursor-pen-adjust.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/memononen/stylii/39af974399bc6d36c2ddb94b4ffa1832bd928ebe/img/cursor-pen-adjust.png -------------------------------------------------------------------------------- /img/cursor-pen-close.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/memononen/stylii/39af974399bc6d36c2ddb94b4ffa1832bd928ebe/img/cursor-pen-close.png -------------------------------------------------------------------------------- /img/cursor-pen-create.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/memononen/stylii/39af974399bc6d36c2ddb94b4ffa1832bd928ebe/img/cursor-pen-create.png -------------------------------------------------------------------------------- /img/cursor-pen-edit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/memononen/stylii/39af974399bc6d36c2ddb94b4ffa1832bd928ebe/img/cursor-pen-edit.png -------------------------------------------------------------------------------- /img/cursor-pen-freehand.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/memononen/stylii/39af974399bc6d36c2ddb94b4ffa1832bd928ebe/img/cursor-pen-freehand.png -------------------------------------------------------------------------------- /img/cursor-pen-join.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/memononen/stylii/39af974399bc6d36c2ddb94b4ffa1832bd928ebe/img/cursor-pen-join.png -------------------------------------------------------------------------------- /img/cursor-pen-remove.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/memononen/stylii/39af974399bc6d36c2ddb94b4ffa1832bd928ebe/img/cursor-pen-remove.png -------------------------------------------------------------------------------- /img/cursor-rotate-0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/memononen/stylii/39af974399bc6d36c2ddb94b4ffa1832bd928ebe/img/cursor-rotate-0.png -------------------------------------------------------------------------------- /img/cursor-rotate-135.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/memononen/stylii/39af974399bc6d36c2ddb94b4ffa1832bd928ebe/img/cursor-rotate-135.png -------------------------------------------------------------------------------- /img/cursor-rotate-180.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/memononen/stylii/39af974399bc6d36c2ddb94b4ffa1832bd928ebe/img/cursor-rotate-180.png -------------------------------------------------------------------------------- /img/cursor-rotate-225.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/memononen/stylii/39af974399bc6d36c2ddb94b4ffa1832bd928ebe/img/cursor-rotate-225.png -------------------------------------------------------------------------------- /img/cursor-rotate-270.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/memononen/stylii/39af974399bc6d36c2ddb94b4ffa1832bd928ebe/img/cursor-rotate-270.png -------------------------------------------------------------------------------- /img/cursor-rotate-315.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/memononen/stylii/39af974399bc6d36c2ddb94b4ffa1832bd928ebe/img/cursor-rotate-315.png -------------------------------------------------------------------------------- /img/cursor-rotate-45.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/memononen/stylii/39af974399bc6d36c2ddb94b4ffa1832bd928ebe/img/cursor-rotate-45.png -------------------------------------------------------------------------------- /img/cursor-rotate-90.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/memononen/stylii/39af974399bc6d36c2ddb94b4ffa1832bd928ebe/img/cursor-rotate-90.png -------------------------------------------------------------------------------- /img/cursor-scale-0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/memononen/stylii/39af974399bc6d36c2ddb94b4ffa1832bd928ebe/img/cursor-scale-0.png -------------------------------------------------------------------------------- /img/cursor-scale-135.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/memononen/stylii/39af974399bc6d36c2ddb94b4ffa1832bd928ebe/img/cursor-scale-135.png -------------------------------------------------------------------------------- /img/cursor-scale-45.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/memononen/stylii/39af974399bc6d36c2ddb94b4ffa1832bd928ebe/img/cursor-scale-45.png -------------------------------------------------------------------------------- /img/cursor-scale-90.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/memononen/stylii/39af974399bc6d36c2ddb94b4ffa1832bd928ebe/img/cursor-scale-90.png -------------------------------------------------------------------------------- /img/cursor-zoom-in.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/memononen/stylii/39af974399bc6d36c2ddb94b4ffa1832bd928ebe/img/cursor-zoom-in.png -------------------------------------------------------------------------------- /img/cursor-zoom-out.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/memononen/stylii/39af974399bc6d36c2ddb94b4ffa1832bd928ebe/img/cursor-zoom-out.png -------------------------------------------------------------------------------- /img/cursor-zoom.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/memononen/stylii/39af974399bc6d36c2ddb94b4ffa1832bd928ebe/img/cursor-zoom.png -------------------------------------------------------------------------------- /img/icon-arrow-black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/memononen/stylii/39af974399bc6d36c2ddb94b4ffa1832bd928ebe/img/icon-arrow-black.png -------------------------------------------------------------------------------- /img/icon-arrow-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/memononen/stylii/39af974399bc6d36c2ddb94b4ffa1832bd928ebe/img/icon-arrow-white.png -------------------------------------------------------------------------------- /img/icon-hand.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/memononen/stylii/39af974399bc6d36c2ddb94b4ffa1832bd928ebe/img/icon-hand.png -------------------------------------------------------------------------------- /img/icon-pen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/memononen/stylii/39af974399bc6d36c2ddb94b4ffa1832bd928ebe/img/icon-pen.png -------------------------------------------------------------------------------- /js/editor.js: -------------------------------------------------------------------------------- 1 | 2 | function Undo(maxUndos) { 3 | this.states = []; 4 | this.head = -1; 5 | this.maxUndos = maxUndos || 10; 6 | this.updateUI(); 7 | } 8 | 9 | Undo.prototype.snapshot = function(name) { 10 | 11 | // console.log("snapshot() layers: "+paper.project.layers); 12 | 13 | // Update previous state's selection to the selection as of now 14 | // so that undo feels more natural after undo. Omitting this 15 | // makes the undo feel like it lost your selection. 16 | if (this.head >= 0 && this.head < this.states.length) 17 | this.states[this.head].selection = this.snapshotSelection(); 18 | 19 | // HACK: Store original ID into the data of an item. 20 | this.captureIDs(); 21 | 22 | var state = { 23 | name: name, 24 | stamp: Date.now(), 25 | json: this.snapshotProject(), 26 | selection: this.snapshotSelection() 27 | }; 28 | 29 | // Group similar actions done close to each other. 30 | /* if (this.states.length > 0 && this.head == (this.states.length-1)) { 31 | var last = this.states[this.states.length-1]; 32 | if (last.name == state.name && (state.stamp - last.stamp) < 5000) { 33 | last.json = state.json; 34 | last.selection = state.selection; 35 | return; 36 | } 37 | }*/ 38 | 39 | // Discard states after the current one. 40 | if (this.head < this.states.length-1) 41 | this.states = this.states.slice(0, this.head+1); 42 | 43 | this.states.push(state); 44 | 45 | // Remove the oldest state if we have too many states. 46 | if (this.states.length > this.maxUndos) 47 | this.states.shift(); 48 | 49 | this.head = this.states.length-1; 50 | 51 | this.updateUI(); 52 | } 53 | 54 | Undo.prototype.restoreIDs = function() { 55 | // Restore IDs from the 'data'. 56 | var maxId = 0; 57 | function visitItem(item) { 58 | if (item.data.id) { 59 | item._id = item.data.id; 60 | if (item.id > maxId) 61 | maxId = item.id; 62 | } 63 | if (item.children) { 64 | for (var j = item.children.length-1; j >= 0; j--) 65 | visitItem(item.children[j]); 66 | } 67 | } 68 | for (var i = 0, l = paper.project.layers.length; i < l; i++) { 69 | var layer = paper.project.layers[i]; 70 | visitItem(layer); 71 | } 72 | if (maxId > paper.Item._id) 73 | Item._id = maxId; 74 | } 75 | 76 | Undo.prototype.captureIDs = function() { 77 | // Store IDs of the items into 'data' so that they get serialized. 78 | function visitItem(item) { 79 | item.data.id = item.id; 80 | if (item.children) { 81 | for (var j = item.children.length-1; j >= 0; j--) 82 | visitItem(item.children[j]); 83 | } 84 | } 85 | for (var i = 0, l = paper.project.layers.length; i < l; i++) { 86 | var layer = paper.project.layers[i]; 87 | visitItem(layer); 88 | } 89 | } 90 | 91 | Undo.prototype.snapshotProject = function() { 92 | var json = paper.project.exportJSON({ asString: false }); 93 | // TODO: Remove objects marked as guides. 94 | return json; 95 | } 96 | 97 | Undo.prototype.snapshotSelection = function() { 98 | var selection = []; 99 | var selected = paper.project.selectedItems; 100 | for (var i = 0; i < selected.length; i++) { 101 | var item = selected[i]; 102 | if (item.guide) continue; 103 | var state = {id: item.id, segs: []}; 104 | if (item instanceof paper.Path) { 105 | var segs = []; 106 | for (var j = 0; j < item.segments.length; j++) { 107 | if (item.segments[j].selected) 108 | segs.push(item.segments[j].index); 109 | } 110 | if (segs.length > 0) { 111 | state.segs = segs; 112 | } 113 | } 114 | selection.push(state); 115 | } 116 | return selection; 117 | } 118 | 119 | Undo.prototype.restoreSelection = function(sel) { 120 | paper.project.deselectAll(); 121 | // HACK: some logic in Paper.js prevents deselectAll in some cases, 122 | // enforce deselect. 123 | paper.project._selectedItems = {}; 124 | 125 | for (var i = 0; i < sel.length; i++) { 126 | var state = sel[i]; 127 | var item = findItemById(state.id); 128 | if (item == null) { 129 | console.log("restoreSelection: could not find "+state.id); 130 | continue; 131 | } 132 | item.selected = true; 133 | for (var j = 0; j < state.segs.length; j++) { 134 | var idx = state.segs[j]; 135 | if (idx >= 0 && idx < item.segments.length) 136 | item.segments[idx].selected = true; 137 | } 138 | } 139 | } 140 | 141 | Undo.prototype.restore = function(state) { 142 | // Empty the project and deserialize the project from JSON. 143 | paper.project.clear(); 144 | paper.project.importJSON(state.json); 145 | // HACK: paper does not retain IDs, we capture them on snapshot, 146 | // restore them here. 147 | this.restoreIDs(); 148 | 149 | // Selection is serialized separately, restore now (requires correct IDs). 150 | this.restoreSelection(state.selection); 151 | 152 | // Update UI 153 | updateSelectionState(); 154 | paper.project.view.update(); 155 | } 156 | 157 | Undo.prototype.undo = function() { 158 | if (this.head > 0) { 159 | this.head--; 160 | this.restore(this.states[this.head]); 161 | } 162 | this.updateUI(); 163 | } 164 | 165 | Undo.prototype.redo = function() { 166 | if (this.head < this.states.length-1) { 167 | this.head++; 168 | this.restore(this.states[this.head]); 169 | } 170 | this.updateUI(); 171 | } 172 | 173 | Undo.prototype.canUndo = function() { 174 | return this.head > 0; 175 | } 176 | 177 | Undo.prototype.canRedo = function() { 178 | return this.head < this.states.length-1; 179 | } 180 | 181 | Undo.prototype.updateUI = function() { 182 | if (this.canUndo()) 183 | $("#undo").removeClass("disabled"); 184 | else 185 | $("#undo").addClass("disabled"); 186 | 187 | if (this.canRedo()) 188 | $("#redo").removeClass("disabled"); 189 | else 190 | $("#redo").addClass("disabled"); 191 | } 192 | 193 | var undo = null; 194 | 195 | 196 | function setCanvasCursor(name) { 197 | $("#canvas").removeClass (function (index, css) { 198 | return (css.match (/\bcursor-\S+/g) || []).join(' '); 199 | }).addClass(name); 200 | } 201 | 202 | function snapDeltaToAngle(delta, snapAngle) { 203 | var angle = Math.atan2(delta.y, delta.x); 204 | angle = Math.round(angle/snapAngle) * snapAngle; 205 | var dirx = Math.cos(angle); 206 | var diry = Math.sin(angle); 207 | var d = dirx*delta.x + diry*delta.y; 208 | return new paper.Point(dirx*d, diry*d); 209 | } 210 | 211 | function indexFromAngle(angle) { 212 | var octant = Math.PI*2/8; 213 | var index = Math.round(angle/octant); 214 | if (index < 0) index += 8; 215 | return index % 8; 216 | } 217 | 218 | var oppositeCorner = { 219 | 'top-left': 'bottom-right', 220 | 'top-center': 'bottom-center', 221 | 'top-right': 'bottom-left', 222 | 'right-center': 'left-center', 223 | 'bottom-right': 'top-left', 224 | 'bottom-center': 'top-center', 225 | 'bottom-left': 'top-right', 226 | 'left-center': 'right-center', 227 | }; 228 | 229 | function setCanvasRotateCursor(dir, da) { 230 | // zero is up, counter clockwise 231 | var angle = Math.atan2(dir.x, -dir.y) + da; 232 | var index = indexFromAngle(angle); 233 | var cursors = [ 234 | 'cursor-rotate-0', 235 | 'cursor-rotate-45', 236 | 'cursor-rotate-90', 237 | 'cursor-rotate-135', 238 | 'cursor-rotate-180', 239 | 'cursor-rotate-225', 240 | 'cursor-rotate-270', 241 | 'cursor-rotate-315' 242 | ]; 243 | setCanvasCursor(cursors[index % 8]); 244 | } 245 | 246 | function setCanvasScaleCursor(dir) { 247 | // zero is up, counter clockwise 248 | var angle = Math.atan2(dir.x, -dir.y); 249 | var index = indexFromAngle(angle); 250 | var cursors = [ 251 | 'cursor-scale-0', 252 | 'cursor-scale-45', 253 | 'cursor-scale-90', 254 | 'cursor-scale-135' 255 | ]; 256 | setCanvasCursor(cursors[index % 4]); 257 | } 258 | 259 | function dragRect(p1, p2) { 260 | // Create pixel perfect dotted rectable for drag selections. 261 | var half = new paper.Point(0.5 / paper.view.zoom, 0.5 / paper.view.zoom); 262 | var start = p1.add(half); 263 | var end = p2.add(half); 264 | var rect = new paper.CompoundPath(); 265 | rect.moveTo(start); 266 | rect.lineTo(new paper.Point(start.x, end.y)); 267 | rect.lineTo(end); 268 | rect.moveTo(start); 269 | rect.lineTo(new paper.Point(end.x, start.y)); 270 | rect.lineTo(end); 271 | rect.strokeColor = 'black'; 272 | rect.strokeWidth = 1.0 / paper.view.zoom; 273 | rect.dashOffset = 0.5 / paper.view.zoom; 274 | rect.dashArray = [1.0 / paper.view.zoom, 1.0 / paper.view.zoom]; 275 | rect.removeOn({ 276 | drag: true, 277 | up: true 278 | }); 279 | rect.guide = true; 280 | return rect; 281 | } 282 | 283 | function findItemById(id) { 284 | if (id == -1) return null; 285 | function findItem(item) { 286 | if (item.id == id) 287 | return item; 288 | if (item.children) { 289 | for (var j = item.children.length-1; j >= 0; j--) { 290 | var it = findItem(item.children[j]); 291 | if (it != null) 292 | return it; 293 | } 294 | } 295 | return null; 296 | } 297 | 298 | for (var i = 0, l = paper.project.layers.length; i < l; i++) { 299 | var layer = paper.project.layers[i]; 300 | var it = findItem(layer); 301 | if (it != null) 302 | return it; 303 | } 304 | return null; 305 | } 306 | 307 | 308 | var clipboard = null; 309 | 310 | var selectionBounds = null; 311 | var selectionBoundsShape = null; 312 | var drawSelectionBounds = 0; 313 | 314 | function clearSelectionBounds() { 315 | if (selectionBoundsShape) 316 | selectionBoundsShape.remove(); 317 | selectionBoundsShape = null; 318 | selectionBounds = null; 319 | }; 320 | 321 | function showSelectionBounds() { 322 | drawSelectionBounds++; 323 | if (drawSelectionBounds > 0) { 324 | if (selectionBoundsShape) 325 | selectionBoundsShape.visible = true; 326 | } 327 | } 328 | 329 | function hideSelectionBounds() { 330 | if (drawSelectionBounds > 0) 331 | drawSelectionBounds--; 332 | if (drawSelectionBounds == 0) { 333 | if (selectionBoundsShape) 334 | selectionBoundsShape.visible = false; 335 | } 336 | } 337 | 338 | function updateSelectionState() { 339 | clearSelectionBounds(); 340 | selectionBounds = getSelectionBounds(); 341 | if (selectionBounds != null) { 342 | var rect = new paper.Path.Rectangle(selectionBounds); 343 | //var color = paper.project.activeLayer.getSelectedColor(); 344 | rect.strokeColor = 'rgba(0,0,0,0)'; //color ? color : '#009dec'; 345 | rect.strokeWidth = 1.0 / paper.view.zoom; 346 | // rect._boundsSelected = true; 347 | rect.selected = true; 348 | rect.setFullySelected(true); 349 | rect.guide = true; 350 | rect.visible = drawSelectionBounds > 0; 351 | // rect.transformContent = false; 352 | selectionBoundsShape = rect; 353 | } 354 | updateSelectionUI(); 355 | } 356 | 357 | function updateSelectionUI() { 358 | if (selectionBounds == null) { 359 | $("#cut").addClass("disabled"); 360 | $("#copy").addClass("disabled"); 361 | $("#delete").addClass("disabled"); 362 | } else { 363 | $("#cut").removeClass("disabled"); 364 | $("#copy").removeClass("disabled"); 365 | $("#delete").removeClass("disabled"); 366 | } 367 | 368 | if (clipboard == null) { 369 | $("#paste").addClass("disabled"); 370 | } else { 371 | $("#paste").removeClass("disabled"); 372 | } 373 | } 374 | 375 | function cutSelection() { 376 | clipboard = captureSelectionState(); 377 | var selected = paper.project.selectedItems; 378 | for (var i = 0; i < selected.length; i++) { 379 | selected[i].remove(); 380 | } 381 | undo.snapshot("Cut"); 382 | } 383 | 384 | function copySelection() { 385 | clipboard = captureSelectionState(); 386 | updateSelectionState(); 387 | } 388 | 389 | function pasteSelection() { 390 | if (clipboard == null) 391 | return; 392 | 393 | deselectAll(); 394 | 395 | var items = []; 396 | for (var i = 0; i < clipboard.length; i++) { 397 | var content = clipboard[i]; 398 | var item = paper.Base.importJSON(content.json); 399 | if (item) { 400 | item.selected = true; 401 | items.push(item); 402 | } 403 | } 404 | 405 | // Center pasted items to center of the view 406 | var bounds = null; 407 | for (var i = 0; i < items.length; i++) { 408 | if (bounds == null) 409 | bounds = items[i].bounds.clone(); 410 | else 411 | bounds = bounds.unite(items[i].bounds); 412 | } 413 | if (bounds) { 414 | var delta = paper.view.center.subtract(bounds.center); 415 | for (var i = 0; i < items.length; i++) { 416 | items[i].position = items[i].position.add(delta); 417 | } 418 | } 419 | 420 | undo.snapshot("Paste"); 421 | 422 | updateSelectionState(); 423 | paper.project.view.update(); 424 | } 425 | 426 | function deleteSelection() { 427 | var selected = paper.project.selectedItems; 428 | for (var i = 0; i < selected.length; i++) 429 | selected[i].remove(); 430 | 431 | undo.snapshot("Delete"); 432 | 433 | updateSelectionState(); 434 | paper.project.view.update(); 435 | } 436 | 437 | // Returns serialized contents of selected items. 438 | function captureSelectionState() { 439 | var originalContent = []; 440 | var selected = paper.project.selectedItems; 441 | for (var i = 0; i < selected.length; i++) { 442 | var item = selected[i]; 443 | if (item.guide) continue; 444 | var orig = { 445 | id: item.id, 446 | json: item.exportJSON({ asString: false }), 447 | selectedSegments: [] 448 | }; 449 | originalContent.push(orig); 450 | } 451 | return originalContent; 452 | } 453 | 454 | // Restore the state of selected items. 455 | function restoreSelectionState(originalContent) { 456 | // TODO: could use findItemById() instead. 457 | for (var i = 0; i < originalContent.length; i++) { 458 | var orig = originalContent[i]; 459 | var item = findItemById(orig.id); 460 | if (!item) continue; 461 | // HACK: paper does not retain item IDs after importJSON, 462 | // store the ID here, and restore after deserialization. 463 | var id = item.id; 464 | item.importJSON(orig.json); 465 | item._id = id; 466 | } 467 | } 468 | 469 | function deselectAll() { 470 | paper.project.deselectAll(); 471 | } 472 | 473 | function deselectAllPoints() { 474 | var selected = paper.project.selectedItems; 475 | for (var i = 0; i < selected.length; i++) { 476 | var item = selected[i]; 477 | if (item instanceof paper.Path) { 478 | for (var j = 0; j < item.segments.length; j++) 479 | if (item.segments[j].selected) 480 | item.segments[j].selected = false; 481 | } 482 | } 483 | } 484 | 485 | // Returns path points which are contained in the rect. 486 | function getSegmentsInRect(rect) { 487 | var segments = []; 488 | 489 | function checkPathItem(item) { 490 | if (item._locked || !item._visible || item._guide) 491 | return; 492 | var children = item.children; 493 | if (!rect.intersects(item.bounds)) 494 | return; 495 | if (item instanceof paper.Path) { 496 | for (var i = 0; i < item.segments.length; i++) { 497 | if (rect.contains(item.segments[i].point)) 498 | segments.push(item.segments[i]); 499 | } 500 | } else { 501 | for (var j = children.length-1; j >= 0; j--) 502 | checkPathItem(children[j]); 503 | } 504 | } 505 | 506 | for (var i = paper.project.layers.length - 1; i >= 0; i--) { 507 | checkPathItem(paper.project.layers[i]); 508 | } 509 | 510 | return segments; 511 | } 512 | 513 | // Returns all items intersecting the rect. 514 | // Note: only the item outlines are tested. 515 | function getPathsIntersectingRect(rect) { 516 | var paths = []; 517 | var boundingRect = new paper.Path.Rectangle(rect); 518 | 519 | function checkPathItem(item) { 520 | var children = item.children; 521 | if (item.equals(boundingRect)) 522 | return; 523 | if (!rect.intersects(item.bounds)) 524 | return; 525 | if (item instanceof paper.PathItem) { 526 | if (rect.contains(item.bounds)) { 527 | paths.push(item); 528 | return; 529 | } 530 | var isects = boundingRect.getIntersections(item); 531 | if (isects.length > 0) 532 | paths.push(item); 533 | } else { 534 | for (var j = children.length-1; j >= 0; j--) 535 | checkPathItem(children[j]); 536 | } 537 | } 538 | 539 | for (var i = 0, l = paper.project.layers.length; i < l; i++) { 540 | var layer = paper.project.layers[i]; 541 | checkPathItem(layer); 542 | } 543 | 544 | boundingRect.remove(); 545 | 546 | return paths; 547 | } 548 | 549 | // Returns bounding box of all selected items. 550 | function getSelectionBounds() { 551 | var bounds = null; 552 | var selected = paper.project.selectedItems; 553 | for (var i = 0; i < selected.length; i++) { 554 | if (bounds == null) 555 | bounds = selected[i].bounds.clone(); 556 | else 557 | bounds = bounds.unite(selected[i].bounds); 558 | } 559 | return bounds; 560 | } 561 | 562 | 563 | var toolSelect = new paper.Tool(); 564 | toolSelect.mouseStartPos = new paper.Point(); 565 | toolSelect.mode = null; 566 | toolSelect.hitItem = null; 567 | toolSelect.originalContent = null; 568 | toolSelect.changed = false; 569 | toolSelect.duplicates = null; 570 | 571 | toolSelect.createDuplicates = function(content) { 572 | this.duplicates = []; 573 | for (var i = 0; i < content.length; i++) { 574 | var orig = content[i]; 575 | var item = paper.Base.importJSON(orig.json); 576 | if (item) { 577 | item.selected = false; 578 | this.duplicates.push(item); 579 | } 580 | } 581 | }; 582 | toolSelect.removeDuplicates = function() { 583 | for (var i = 0; i < this.duplicates.length; i++) 584 | this.duplicates[i].remove(); 585 | this.duplicates = null; 586 | }; 587 | 588 | toolSelect.resetHot = function(type, event, mode) { 589 | }; 590 | toolSelect.testHot = function(type, event, mode) { 591 | /* if (mode != 'tool-select') 592 | return false;*/ 593 | return this.hitTest(event); 594 | }; 595 | toolSelect.hitTest = function(event) { 596 | var hitSize = 4.0; // / paper.view.zoom; 597 | this.hitItem = null; 598 | 599 | // Hit test items. 600 | if (event.point) 601 | this.hitItem = paper.project.hitTest(event.point, { fill:true, stroke:true, tolerance: hitSize }); 602 | 603 | if (this.hitItem) { 604 | if (this.hitItem.type == 'fill' || this.hitItem.type == 'stroke') { 605 | if (this.hitItem.item.selected) { 606 | setCanvasCursor('cursor-arrow-small'); 607 | } else { 608 | setCanvasCursor('cursor-arrow-black-shape'); 609 | } 610 | } 611 | } else { 612 | setCanvasCursor('cursor-arrow-black'); 613 | } 614 | 615 | return true; 616 | }; 617 | toolSelect.on({ 618 | activate: function() { 619 | $("#tools").children().removeClass("selected"); 620 | $("#tool-select").addClass("selected"); 621 | setCanvasCursor('cursor-arrow-black'); 622 | updateSelectionState(); 623 | showSelectionBounds(); 624 | }, 625 | deactivate: function() { 626 | hideSelectionBounds(); 627 | }, 628 | mousedown: function(event) { 629 | this.mode = null; 630 | this.changed = false; 631 | 632 | if (this.hitItem) { 633 | if (this.hitItem.type == 'fill' || this.hitItem.type == 'stroke') { 634 | if (event.modifiers.shift) { 635 | this.hitItem.item.selected = !this.hitItem.item.selected; 636 | } else { 637 | if (!this.hitItem.item.selected) 638 | deselectAll(); 639 | this.hitItem.item.selected = true; 640 | } 641 | if (this.hitItem.item.selected) { 642 | this.mode = 'move-shapes'; 643 | deselectAllPoints(); 644 | this.mouseStartPos = event.point.clone(); 645 | this.originalContent = captureSelectionState(); 646 | } 647 | } 648 | updateSelectionState(); 649 | } else { 650 | // Clicked on and empty area, engage box select. 651 | this.mouseStartPos = event.point.clone(); 652 | this.mode = 'box-select'; 653 | } 654 | }, 655 | mouseup: function(event) { 656 | if (this.mode == 'move-shapes') { 657 | if (this.changed) { 658 | clearSelectionBounds(); 659 | undo.snapshot("Move Shapes"); 660 | } 661 | this.duplicates = null; 662 | } else if (this.mode == 'box-select') { 663 | var box = new paper.Rectangle(this.mouseStartPos, event.point); 664 | 665 | if (!event.modifiers.shift) 666 | deselectAll(); 667 | 668 | var selectedPaths = getPathsIntersectingRect(box); 669 | for (var i = 0; i < selectedPaths.length; i++) 670 | selectedPaths[i].selected = !selectedPaths[i].selected; 671 | } 672 | 673 | updateSelectionState(); 674 | 675 | if (this.hitItem) { 676 | if (this.hitItem.item.selected) { 677 | setCanvasCursor('cursor-arrow-small'); 678 | } else { 679 | setCanvasCursor('cursor-arrow-black-shape'); 680 | } 681 | } 682 | }, 683 | mousedrag: function(event) { 684 | if (this.mode == 'move-shapes') { 685 | 686 | this.changed = true; 687 | 688 | if (event.modifiers.option) { 689 | if (this.duplicates == null) 690 | this.createDuplicates(this.originalContent); 691 | setCanvasCursor('cursor-arrow-duplicate'); 692 | } else { 693 | if (this.duplicates) 694 | this.removeDuplicates(); 695 | setCanvasCursor('cursor-arrow-small'); 696 | } 697 | 698 | var delta = event.point.subtract(this.mouseStartPos); 699 | if (event.modifiers.shift) { 700 | delta = snapDeltaToAngle(delta, Math.PI*2/8); 701 | } 702 | 703 | restoreSelectionState(this.originalContent); 704 | 705 | var selected = paper.project.selectedItems; 706 | for (var i = 0; i < selected.length; i++) { 707 | selected[i].position = selected[i].position.add(delta); 708 | } 709 | updateSelectionState(); 710 | } else if (this.mode == 'box-select') { 711 | dragRect(this.mouseStartPos, event.point); 712 | } 713 | }, 714 | mousemove: function(event) { 715 | this.hitTest(event); 716 | } 717 | }); 718 | 719 | 720 | var toolDirectSelect = new paper.Tool(); 721 | toolDirectSelect.mouseStartPos = new paper.Point(); 722 | toolDirectSelect.mode = null; 723 | toolDirectSelect.hitItem = null; 724 | toolDirectSelect.originalContent = null; 725 | toolDirectSelect.originalHandleIn = null; 726 | toolDirectSelect.originalHandleOut = null; 727 | toolDirectSelect.changed = false; 728 | 729 | toolDirectSelect.resetHot = function(type, event, mode) { 730 | }; 731 | toolDirectSelect.testHot = function(type, event, mode) { 732 | if (mode != 'tool-direct-select') 733 | return; 734 | return this.hitTest(event); 735 | }; 736 | 737 | toolDirectSelect.hitTest = function(event) { 738 | var hitSize = 4.0; // / paper.view.zoom; 739 | var hit = null; 740 | this.hitItem = null; 741 | 742 | // Hit test items. 743 | if (event.point) 744 | this.hitItem = paper.project.hitTest(event.point, { fill:true, stroke:true, tolerance: hitSize }); 745 | 746 | // Hit test selected handles 747 | hit = null; 748 | if (event.point) 749 | hit = paper.project.hitTest(event.point, { selected: true, handles: true, tolerance: hitSize }); 750 | if (hit) 751 | this.hitItem = hit; 752 | // Hit test points 753 | hit = null; 754 | if (event.point) 755 | hit = paper.project.hitTest(event.point, { segments: true, tolerance: hitSize }); 756 | if (hit) 757 | this.hitItem = hit; 758 | 759 | if (this.hitItem) { 760 | if (this.hitItem.type == 'fill' || this.hitItem.type == 'stroke') { 761 | if (this.hitItem.item.selected) { 762 | setCanvasCursor('cursor-arrow-small'); 763 | } else { 764 | setCanvasCursor('cursor-arrow-white-shape'); 765 | } 766 | } else if (this.hitItem.type == 'segment' || this.hitItem.type == 'handle-in' || this.hitItem.type == 'handle-out') { 767 | if (this.hitItem.segment.selected) { 768 | setCanvasCursor('cursor-arrow-small-point'); 769 | } else { 770 | setCanvasCursor('cursor-arrow-white-point'); 771 | } 772 | } 773 | } else { 774 | setCanvasCursor('cursor-arrow-white'); 775 | } 776 | 777 | return true; 778 | }; 779 | toolDirectSelect.on({ 780 | activate: function() { 781 | $("#tools").children().removeClass("selected"); 782 | $("#tool-direct-select").addClass("selected"); 783 | setCanvasCursor('cursor-arrow-white'); 784 | // this.hitItem = null; 785 | }, 786 | deactivate: function() { 787 | // this.clearSelectionBounds(); 788 | }, 789 | mousedown: function(event) { 790 | this.mode = null; 791 | this.changed = false; 792 | 793 | if (this.hitItem) { 794 | if (this.hitItem.type == 'fill' || this.hitItem.type == 'stroke') { 795 | if (event.modifiers.shift) { 796 | this.hitItem.item.selected = !this.hitItem.item.selected; 797 | } else { 798 | if (!this.hitItem.item.selected) 799 | deselectAll(); 800 | this.hitItem.item.selected = true; 801 | } 802 | if (this.hitItem.item.selected) { 803 | this.mode = 'move-shapes'; 804 | deselectAllPoints(); 805 | this.mouseStartPos = event.point.clone(); 806 | this.originalContent = captureSelectionState(); 807 | } 808 | } else if (this.hitItem.type == 'segment') { 809 | if (event.modifiers.shift) { 810 | this.hitItem.segment.selected = !this.hitItem.segment.selected; 811 | } else { 812 | if (!this.hitItem.segment.selected) 813 | deselectAllPoints(); 814 | this.hitItem.segment.selected = true; 815 | } 816 | if (this.hitItem.segment.selected) { 817 | this.mode = 'move-points'; 818 | this.mouseStartPos = event.point.clone(); 819 | this.originalContent = captureSelectionState(); 820 | } 821 | } else if (this.hitItem.type == 'handle-in' || this.hitItem.type == 'handle-out') { 822 | this.mode = 'move-handle'; 823 | this.mouseStartPos = event.point.clone(); 824 | this.originalHandleIn = this.hitItem.segment.handleIn.clone(); 825 | this.originalHandleOut = this.hitItem.segment.handleOut.clone(); 826 | 827 | /* if (this.hitItem.type == 'handle-out') { 828 | this.originalHandlePos = this.hitItem.segment.handleOut.clone(); 829 | this.originalOppHandleLength = this.hitItem.segment.handleIn.length; 830 | } else { 831 | this.originalHandlePos = this.hitItem.segment.handleIn.clone(); 832 | this.originalOppHandleLength = this.hitItem.segment.handleOut.length; 833 | }*/ 834 | // this.originalContent = captureSelectionState(); // For some reason this does not work! 835 | } 836 | updateSelectionState(); 837 | } else { 838 | // Clicked on and empty area, engage box select. 839 | this.mouseStartPos = event.point.clone(); 840 | this.mode = 'box-select'; 841 | } 842 | }, 843 | mouseup: function(event) { 844 | if (this.mode == 'move-shapes') { 845 | if (this.changed) { 846 | clearSelectionBounds(); 847 | undo.snapshot("Move Shapes"); 848 | } 849 | } else if (this.mode == 'move-points') { 850 | if (this.changed) { 851 | clearSelectionBounds(); 852 | undo.snapshot("Move Points"); 853 | } 854 | } else if (this.mode == 'move-handle') { 855 | if (this.changed) { 856 | clearSelectionBounds(); 857 | undo.snapshot("Move Handle"); 858 | } 859 | } else if (this.mode == 'box-select') { 860 | var box = new paper.Rectangle(this.mouseStartPos, event.point); 861 | 862 | if (!event.modifiers.shift) 863 | deselectAll(); 864 | 865 | var selectedSegments = getSegmentsInRect(box); 866 | if (selectedSegments.length > 0) { 867 | for (var i = 0; i < selectedSegments.length; i++) { 868 | selectedSegments[i].selected = !selectedSegments[i].selected; 869 | } 870 | } else { 871 | var selectedPaths = getPathsIntersectingRect(box); 872 | for (var i = 0; i < selectedPaths.length; i++) 873 | selectedPaths[i].selected = !selectedPaths[i].selected; 874 | } 875 | } 876 | 877 | updateSelectionState(); 878 | 879 | if (this.hitItem) { 880 | if (this.hitItem.item.selected) { 881 | setCanvasCursor('cursor-arrow-small'); 882 | } else { 883 | setCanvasCursor('cursor-arrow-white-shape'); 884 | } 885 | } 886 | }, 887 | mousedrag: function(event) { 888 | this.changed = true; 889 | if (this.mode == 'move-shapes') { 890 | setCanvasCursor('cursor-arrow-small'); 891 | 892 | var delta = event.point.subtract(this.mouseStartPos); 893 | if (event.modifiers.shift) { 894 | delta = snapDeltaToAngle(delta, Math.PI*2/8); 895 | } 896 | restoreSelectionState(this.originalContent); 897 | 898 | var selected = paper.project.selectedItems; 899 | for (var i = 0; i < selected.length; i++) { 900 | selected[i].position = selected[i].position.add(delta); 901 | } 902 | updateSelectionState(); 903 | } else if (this.mode == 'move-points') { 904 | setCanvasCursor('cursor-arrow-small'); 905 | 906 | var delta = event.point.subtract(this.mouseStartPos); 907 | if (event.modifiers.shift) { 908 | delta = snapDeltaToAngle(delta, Math.PI*2/8); 909 | } 910 | restoreSelectionState(this.originalContent); 911 | 912 | var selected = paper.project.selectedItems; 913 | for (var i = 0; i < selected.length; i++) { 914 | var path = selected[i]; 915 | for (var j = 0; j < path.segments.length; j++) { 916 | if (path.segments[j].selected) 917 | path.segments[j].point = path.segments[j].point.add(delta); 918 | } 919 | } 920 | updateSelectionState(); 921 | } else if (this.mode == 'move-handle') { 922 | 923 | var delta = event.point.subtract(this.mouseStartPos); 924 | 925 | if (this.hitItem.type == 'handle-out') { 926 | var handlePos = this.originalHandleOut.add(delta); 927 | if (event.modifiers.shift) { 928 | handlePos = snapDeltaToAngle(handlePos, Math.PI*2/8); 929 | } 930 | this.hitItem.segment.handleOut = handlePos; 931 | this.hitItem.segment.handleIn = handlePos.normalize(-this.originalHandleIn.length); 932 | } else { 933 | var handlePos = this.originalHandleIn.add(delta); 934 | if (event.modifiers.shift) { 935 | handlePos = snapDeltaToAngle(handlePos, Math.PI*2/8); 936 | } 937 | this.hitItem.segment.handleIn = handlePos; 938 | this.hitItem.segment.handleOut = handlePos.normalize(-this.originalHandleOut.length); 939 | } 940 | 941 | updateSelectionState(); 942 | } else if (this.mode == 'box-select') { 943 | dragRect(this.mouseStartPos, event.point); 944 | } 945 | }, 946 | mousemove: function(event) { 947 | this.hitTest(event); 948 | } 949 | }); 950 | 951 | 952 | var toolScale = new paper.Tool(); 953 | toolScale.mouseStartPos = new paper.Point(); 954 | toolScale.mode = null; 955 | toolScale.hitItem = null; 956 | toolScale.pivot = null; 957 | toolScale.corner = null; 958 | toolScale.originalCenter = null; 959 | toolScale.originalSize = null; 960 | toolScale.originalContent = null; 961 | toolScale.changed = false; 962 | 963 | toolScale.resetHot = function(type, event, mode) { 964 | }; 965 | toolScale.testHot = function(type, event, mode) { 966 | /* if (mode != 'tool-select') 967 | return false;*/ 968 | return this.hitTest(event); 969 | }; 970 | 971 | toolScale.hitTest = function(event) { 972 | var hitSize = 6.0; // / paper.view.zoom; 973 | this.hitItem = null; 974 | 975 | if (!selectionBoundsShape || !selectionBounds) 976 | updateSelectionState(); 977 | 978 | if (!selectionBoundsShape || !selectionBounds) 979 | return; 980 | 981 | // Hit test selection rectangle 982 | if (event.point) 983 | this.hitItem = selectionBoundsShape.hitTest(event.point, { bounds: true, guides: true, tolerance: hitSize }); 984 | 985 | if (this.hitItem && this.hitItem.type == 'bounds') { 986 | // Normalize the direction so that corners are at 45° angles. 987 | var dir = event.point.subtract(selectionBounds.center); 988 | dir.x /= selectionBounds.width*0.5; 989 | dir.y /= selectionBounds.height*0.5; 990 | setCanvasScaleCursor(dir); 991 | return true; 992 | } 993 | 994 | return false; 995 | }; 996 | 997 | toolScale.on({ 998 | activate: function() { 999 | $("#tools").children().removeClass("selected"); 1000 | $("#tool-select").addClass("selected"); 1001 | setCanvasCursor('cursor-arrow-black'); 1002 | updateSelectionState(); 1003 | showSelectionBounds(); 1004 | }, 1005 | deactivate: function() { 1006 | hideSelectionBounds(); 1007 | }, 1008 | mousedown: function(event) { 1009 | this.mode = null; 1010 | this.changed = false; 1011 | if (this.hitItem) { 1012 | if (this.hitItem.type == 'bounds') { 1013 | this.originalContent = captureSelectionState(); 1014 | this.mode = 'scale'; 1015 | var pivotName = paper.Base.camelize(oppositeCorner[this.hitItem.name]); 1016 | var cornerName = paper.Base.camelize(this.hitItem.name); 1017 | this.pivot = selectionBounds[pivotName].clone(); 1018 | this.corner = selectionBounds[cornerName].clone(); 1019 | this.originalSize = this.corner.subtract(this.pivot); 1020 | this.originalCenter = selectionBounds.center; 1021 | } 1022 | updateSelectionState(); 1023 | } 1024 | }, 1025 | mouseup: function(event) { 1026 | if (this.mode == 'scale') { 1027 | if (this.changed) { 1028 | clearSelectionBounds(); 1029 | undo.snapshot("Scale Shapes"); 1030 | } 1031 | } 1032 | }, 1033 | mousedrag: function(event) { 1034 | if (this.mode == 'scale') { 1035 | var pivot = this.pivot; 1036 | var originalSize = this.originalSize; 1037 | 1038 | if (event.modifiers.option) { 1039 | pivot = this.originalCenter; 1040 | originalSize = originalSize.multiply(0.5); 1041 | } 1042 | 1043 | this.corner = this.corner.add(event.delta); 1044 | var size = this.corner.subtract(pivot); 1045 | var sx = 1.0, sy = 1.0; 1046 | if (Math.abs(originalSize.x) > 0.0000001) 1047 | sx = size.x / originalSize.x; 1048 | if (Math.abs(originalSize.y) > 0.0000001) 1049 | sy = size.y / originalSize.y; 1050 | 1051 | if (event.modifiers.shift) { 1052 | var signx = sx > 0 ? 1 : -1; 1053 | var signy = sy > 0 ? 1 : -1; 1054 | sx = sy = Math.max(Math.abs(sx), Math.abs(sy)); 1055 | sx *= signx; 1056 | sy *= signy; 1057 | } 1058 | 1059 | restoreSelectionState(this.originalContent); 1060 | 1061 | var selected = paper.project.selectedItems; 1062 | for (var i = 0; i < selected.length; i++) { 1063 | var item = selected[i]; 1064 | if (item.guide) continue; 1065 | item.scale(sx, sy, pivot); 1066 | } 1067 | updateSelectionState(); 1068 | this.changed = true; 1069 | } 1070 | }, 1071 | mousemove: function(event) { 1072 | this.hitTest(event); 1073 | } 1074 | }); 1075 | 1076 | 1077 | var toolRotate = new paper.Tool(); 1078 | toolRotate.mouseStartPos = new paper.Point(); 1079 | toolRotate.mode = null; 1080 | toolRotate.hitItem = null; 1081 | toolRotate.originalCenter = null; 1082 | toolRotate.originalAngle = 0; 1083 | toolRotate.originalContent = null; 1084 | toolRotate.originalShape = null; 1085 | toolRotate.cursorDir = null; 1086 | toolRotate.changed = false; 1087 | 1088 | 1089 | toolRotate.resetHot = function(type, event, mode) { 1090 | }; 1091 | toolRotate.testHot = function(type, event, mode) { 1092 | /* if (mode != 'tool-select') 1093 | return false;*/ 1094 | return this.hitTest(event); 1095 | }; 1096 | 1097 | toolRotate.hitTest = function(event) { 1098 | var hitSize = 12.0; // / paper.view.zoom; 1099 | this.hitItem = null; 1100 | 1101 | if (!selectionBoundsShape || !selectionBounds) 1102 | updateSelectionState(); 1103 | 1104 | if (!selectionBoundsShape || !selectionBounds) 1105 | return; 1106 | 1107 | // Hit test selection rectangle 1108 | this.hitItem = null; 1109 | if (event.point && !selectionBounds.contains(event.point)) 1110 | this.hitItem = selectionBoundsShape.hitTest(event.point, { bounds: true, guides: true, tolerance: hitSize }); 1111 | 1112 | if (this.hitItem && this.hitItem.type == 'bounds') { 1113 | // Normalize the direction so that corners are at 45° angles. 1114 | var dir = event.point.subtract(selectionBounds.center); 1115 | dir.x /= selectionBounds.width*0.5; 1116 | dir.y /= selectionBounds.height*0.5; 1117 | setCanvasRotateCursor(dir, 0); 1118 | toolRotate.cursorDir = dir; 1119 | return true; 1120 | } 1121 | 1122 | return false; 1123 | }; 1124 | 1125 | toolRotate.on({ 1126 | activate: function() { 1127 | $("#tools").children().removeClass("selected"); 1128 | $("#tool-select").addClass("selected"); 1129 | setCanvasCursor('cursor-arrow-black'); 1130 | updateSelectionState(); 1131 | showSelectionBounds(); 1132 | }, 1133 | deactivate: function() { 1134 | hideSelectionBounds(); 1135 | }, 1136 | mousedown: function(event) { 1137 | this.mode = null; 1138 | this.changed = false; 1139 | if (this.hitItem) { 1140 | if (this.hitItem.type == 'bounds') { 1141 | this.originalContent = captureSelectionState(); 1142 | this.originalShape = selectionBoundsShape.exportJSON({ asString: false }); 1143 | this.mode = 'rotate'; 1144 | this.originalCenter = selectionBounds.center.clone(); 1145 | var delta = event.point.subtract(this.originalCenter); 1146 | this.originalAngle = Math.atan2(delta.y, delta.x); 1147 | } 1148 | updateSelectionState(); 1149 | } 1150 | }, 1151 | mouseup: function(event) { 1152 | if (this.mode == 'rotate') { 1153 | if (this.changed) { 1154 | clearSelectionBounds(); 1155 | undo.snapshot("Rotate Shapes"); 1156 | } 1157 | } 1158 | updateSelectionState(); 1159 | }, 1160 | mousedrag: function(event) { 1161 | if (this.mode == 'rotate') { 1162 | 1163 | var delta = event.point.subtract(this.originalCenter); 1164 | var angle = Math.atan2(delta.y, delta.x); 1165 | var da = angle - this.originalAngle; 1166 | 1167 | if (event.modifiers.shift) { 1168 | var snapeAngle = Math.PI/4; 1169 | da = Math.round(da / snapeAngle) * snapeAngle; 1170 | } 1171 | 1172 | restoreSelectionState(this.originalContent); 1173 | 1174 | var id = selectionBoundsShape.id; 1175 | selectionBoundsShape.importJSON(this.originalShape); 1176 | selectionBoundsShape._id = id; 1177 | 1178 | var deg = da/Math.PI*180; 1179 | 1180 | selectionBoundsShape.rotate(deg, this.originalCenter); 1181 | 1182 | var selected = paper.project.selectedItems; 1183 | for (var i = 0; i < selected.length; i++) { 1184 | var item = selected[i]; 1185 | if (item.guide) continue; 1186 | item.rotate(deg, this.originalCenter); 1187 | } 1188 | 1189 | setCanvasRotateCursor(toolRotate.cursorDir, da); 1190 | this.changed = true; 1191 | } 1192 | }, 1193 | mousemove: function(event) { 1194 | this.hitTest(event); 1195 | } 1196 | }); 1197 | 1198 | 1199 | var toolZoomPan = new paper.Tool(); 1200 | toolZoomPan.distanceThreshold = 8; 1201 | toolZoomPan.mouseStartPos = new paper.Point(); 1202 | toolZoomPan.mode = 'pan'; 1203 | toolZoomPan.zoomFactor = 1.3; 1204 | toolZoomPan.resetHot = function(type, event, mode) { 1205 | }; 1206 | toolZoomPan.testHot = function(type, event, mode) { 1207 | var spacePressed = event && event.modifiers.space; 1208 | if (mode != 'tool-zoompan' && !spacePressed) 1209 | return false; 1210 | return this.hitTest(event); 1211 | }; 1212 | toolZoomPan.hitTest = function(event) { 1213 | if (event.modifiers.command) { 1214 | if (event.modifiers.command && !event.modifiers.option) { 1215 | setCanvasCursor('cursor-zoom-in'); 1216 | } else if (event.modifiers.command && event.modifiers.option) { 1217 | setCanvasCursor('cursor-zoom-out'); 1218 | } 1219 | } else { 1220 | setCanvasCursor('cursor-hand'); 1221 | } 1222 | return true; 1223 | }; 1224 | toolZoomPan.on({ 1225 | activate: function() { 1226 | $("#tools").children().removeClass("selected"); 1227 | $("#tool-zoompan").addClass("selected"); 1228 | setCanvasCursor('cursor-hand'); 1229 | }, 1230 | deactivate: function() { 1231 | }, 1232 | mousedown: function(event) { 1233 | this.mouseStartPos = event.point.subtract(paper.view.center); 1234 | this.mode = ''; 1235 | if (event.modifiers.command) { 1236 | this.mode = 'zoom'; 1237 | } else { 1238 | setCanvasCursor('cursor-hand-grab'); 1239 | this.mode = 'pan'; 1240 | } 1241 | }, 1242 | mouseup: function(event) { 1243 | if (this.mode == 'zoom') { 1244 | var zoomCenter = event.point.subtract(paper.view.center); 1245 | var moveFactor = this.zoomFactor - 1.0; 1246 | if (event.modifiers.command && !event.modifiers.option) { 1247 | paper.view.zoom *= this.zoomFactor; 1248 | paper.view.center = paper.view.center.add(zoomCenter.multiply(moveFactor / this.zoomFactor)); 1249 | } else if (event.modifiers.command && event.modifiers.option) { 1250 | paper.view.zoom /= this.zoomFactor; 1251 | paper.view.center = paper.view.center.subtract(zoomCenter.multiply(moveFactor)); 1252 | } 1253 | } else if (this.mode == 'zoom-rect') { 1254 | var start = paper.view.center.add(this.mouseStartPos); 1255 | var end = event.point; 1256 | paper.view.center = start.add(end).multiply(0.5); 1257 | var dx = paper.view.bounds.width / Math.abs(end.x - start.x); 1258 | var dy = paper.view.bounds.height / Math.abs(end.y - start.y); 1259 | paper.view.zoom = Math.min(dx, dy) * paper.view.zoom; 1260 | } 1261 | this.hitTest(event); 1262 | this.mode = ''; 1263 | }, 1264 | mousedrag: function(event) { 1265 | if (this.mode == 'zoom') { 1266 | // If dragging mouse while in zoom mode, switch to zoom-rect instead. 1267 | this.mode = 'zoom-rect'; 1268 | } else if (this.mode == 'zoom-rect') { 1269 | // While dragging the zoom rectangle, paint the selected area. 1270 | dragRect(paper.view.center.add(this.mouseStartPos), event.point); 1271 | } else if (this.mode == 'pan') { 1272 | // Handle panning by moving the view center. 1273 | var pt = event.point.subtract(paper.view.center); 1274 | var delta = this.mouseStartPos.subtract(pt); 1275 | paper.view.scrollBy(delta); 1276 | this.mouseStartPos = pt; 1277 | } 1278 | }, 1279 | 1280 | mousemove: function(event) { 1281 | this.hitTest(event); 1282 | }, 1283 | 1284 | keydown: function(event) { 1285 | this.hitTest(event); 1286 | }, 1287 | 1288 | keyup: function(event) { 1289 | this.hitTest(event); 1290 | } 1291 | }); 1292 | 1293 | var toolPen = new paper.Tool(); 1294 | toolPen.pathId = -1; 1295 | toolPen.hitResult = null; 1296 | toolPen.mouseStartPos = null; 1297 | toolPen.originalHandleIn = null; 1298 | toolPen.originalHandleOut = null; 1299 | toolPen.currentSegment = null; 1300 | 1301 | toolPen.closePath = function() { 1302 | if (this.pathId != -1) { 1303 | deselectAllPoints(); 1304 | this.pathId = -1; 1305 | } 1306 | }; 1307 | toolPen.updateTail = function(point) { 1308 | var path = findItemById(this.pathId); 1309 | if (path == null) 1310 | return; 1311 | var nsegs = path.segments.length; 1312 | if (nsegs == 0) 1313 | return; 1314 | 1315 | var color = paper.project.activeLayer.getSelectedColor(); 1316 | var tail = new paper.Path(); 1317 | tail.strokeColor = color ? color : '#009dec'; 1318 | tail.strokeWidth = 1.0 / paper.view.zoom; 1319 | tail.guide = true; 1320 | 1321 | var prevPoint = path.segments[nsegs-1].point; 1322 | var prevHandleOut = path.segments[nsegs-1].point.add(path.segments[nsegs-1].handleOut); 1323 | 1324 | tail.moveTo(prevPoint); 1325 | tail.cubicCurveTo(prevHandleOut, point, point); 1326 | 1327 | tail.removeOn({ 1328 | drag: true, 1329 | up: true, 1330 | down: true, 1331 | move: true 1332 | }); 1333 | } 1334 | toolPen.resetHot = function(type, event, mode) { 1335 | }; 1336 | toolPen.testHot = function(type, event, mode) { 1337 | if (mode != 'tool-pen') 1338 | return false; 1339 | if (event.modifiers.command) 1340 | return false; 1341 | if (type == 'keyup') { 1342 | if (event.key == 'enter' || event.key == 'escape') { 1343 | this.closePath(); 1344 | } 1345 | } 1346 | return this.hitTest(event, type); 1347 | }; 1348 | toolPen.hitTest = function(event, type) { 1349 | var hitSize = 4.0; // / paper.view.zoom; 1350 | var result = null; 1351 | // var isKeyEvent = type == 'mode' || type == 'command' || type == 'keydown' || type == 'keyup'; 1352 | 1353 | this.currentSegment = null; 1354 | this.hitResult = null; 1355 | 1356 | if (event.point) 1357 | result = paper.project.hitTest(event.point, { segments: true, stroke: true, tolerance: hitSize }); 1358 | 1359 | if (result) { 1360 | if (result.type == 'stroke') { 1361 | if (result.item.selected) { 1362 | // Insert point. 1363 | this.mode = 'insert'; 1364 | setCanvasCursor('cursor-pen-add'); 1365 | } else { 1366 | result = null; 1367 | } 1368 | } else if (result.type == 'segment') { 1369 | var last = result.item.segments.length-1; 1370 | if (!result.item.closed && (result.segment.index == 0 || result.segment.index == last)) { 1371 | if (result.item.id == this.pathId) { 1372 | if (result.segment.index == 0) { 1373 | // Close 1374 | this.mode = 'close'; 1375 | setCanvasCursor('cursor-pen-close'); 1376 | this.updateTail(result.segment.point); 1377 | } else { 1378 | // Adjust last handle 1379 | this.mode = 'adjust'; 1380 | setCanvasCursor('cursor-pen-adjust'); 1381 | } 1382 | } else { 1383 | if (this.pathId != -1) { 1384 | this.mode = 'join'; 1385 | setCanvasCursor('cursor-pen-join'); 1386 | this.updateTail(result.segment.point); 1387 | } else { 1388 | this.mode = 'continue'; 1389 | setCanvasCursor('cursor-pen-edit'); 1390 | } 1391 | } 1392 | } else if (result.item.selected) { 1393 | if (event.modifiers.option) { 1394 | this.mode = 'convert'; 1395 | setCanvasCursor('cursor-pen-adjust'); 1396 | } else { 1397 | this.mode = 'remove'; 1398 | setCanvasCursor('cursor-pen-remove'); 1399 | } 1400 | } else { 1401 | result = null; 1402 | } 1403 | } 1404 | } 1405 | 1406 | if (!result) { 1407 | this.mode = 'create'; 1408 | setCanvasCursor('cursor-pen-create'); 1409 | if (event.point) 1410 | this.updateTail(event.point); 1411 | } 1412 | 1413 | this.hitResult = result; 1414 | 1415 | return true; 1416 | }; 1417 | toolPen.on({ 1418 | activate: function() { 1419 | $("#tools").children().removeClass("selected"); 1420 | $("#tool-pen").addClass("selected"); 1421 | setCanvasCursor('cursor-pen-add'); 1422 | }, 1423 | deactivate: function() { 1424 | if (toolStack.mode != 'tool-pen') { 1425 | this.closePath(); 1426 | updateSelectionState(); 1427 | } 1428 | this.currentSegment = null; 1429 | }, 1430 | mousedown: function(event) { 1431 | 1432 | deselectAllPoints(); 1433 | 1434 | if (this.mode == 'create') { 1435 | var path = findItemById(this.pathId); 1436 | if (path == null) { 1437 | deselectAll(); 1438 | path = new paper.Path(); 1439 | path.strokeColor = 'black'; 1440 | this.pathId = path.id; 1441 | } 1442 | this.currentSegment = path.add(event.point); 1443 | 1444 | this.mouseStartPos = event.point.clone(); 1445 | this.originalHandleIn = this.currentSegment.handleIn.clone(); 1446 | this.originalHandleOut = this.currentSegment.handleOut.clone(); 1447 | 1448 | } else if (this.mode == 'insert') { 1449 | if (this.hitResult != null) { 1450 | var location = this.hitResult.location; 1451 | 1452 | var values = location.curve.getValues(); 1453 | var isLinear = location.curve.isLinear(); 1454 | var parts = paper.Curve.subdivide(values, location.parameter); 1455 | var left = parts[0]; 1456 | var right = parts[1]; 1457 | 1458 | var x = left[6], y = left[7]; 1459 | var segment = new paper.Segment(new paper.Point(x, y), 1460 | !isLinear && new paper.Point(left[4] - x, left[5] - y), 1461 | !isLinear && new paper.Point(right[2] - x, right[3] - y)); 1462 | 1463 | var seg = this.hitResult.item.insert(location.index + 1, segment); 1464 | 1465 | if (!isLinear) { 1466 | seg.previous.handleOut.set(left[2] - left[0], left[3] - left[1]); 1467 | seg.next.handleIn.set(right[4] - right[6], right[5] - right[7]); 1468 | } 1469 | 1470 | deselectAllPoints(); 1471 | seg.selected = true; 1472 | 1473 | this.hitResult = null; 1474 | } 1475 | 1476 | } else if (this.mode == 'close') { 1477 | 1478 | if (this.pathId != -1) { 1479 | var path = findItemById(this.pathId); 1480 | path.closed = true; 1481 | } 1482 | 1483 | this.currentSegment = this.hitResult.segment; 1484 | this.currentSegment.handleIn.set(0,0); 1485 | 1486 | this.mouseStartPos = event.point.clone(); 1487 | this.originalHandleIn = this.currentSegment.handleIn.clone(); 1488 | this.originalHandleOut = this.currentSegment.handleOut.clone(); 1489 | 1490 | } else if (this.mode == 'adjust') { 1491 | 1492 | this.currentSegment = this.hitResult.segment; 1493 | this.currentSegment.handleOut.set(0,0); 1494 | 1495 | this.mouseStartPos = event.point.clone(); 1496 | this.originalHandleIn = this.currentSegment.handleIn.clone(); 1497 | this.originalHandleOut = this.currentSegment.handleOut.clone(); 1498 | 1499 | } else if (this.mode == 'continue') { 1500 | 1501 | if (this.hitResult.segment.index == 0) 1502 | this.hitResult.item.reverse(); 1503 | 1504 | this.pathId = this.hitResult.item.id; 1505 | this.currentSegment = this.hitResult.segment; 1506 | this.currentSegment.handleOut.set(0,0); 1507 | 1508 | this.mouseStartPos = event.point.clone(); 1509 | this.originalHandleIn = this.currentSegment.handleIn.clone(); 1510 | this.originalHandleOut = this.currentSegment.handleOut.clone(); 1511 | 1512 | } else if (this.mode == 'convert') { 1513 | 1514 | this.pathId = this.hitResult.item.id; 1515 | this.currentSegment = this.hitResult.segment; 1516 | this.currentSegment.handleIn.set(0,0); 1517 | this.currentSegment.handleOut.set(0,0); 1518 | 1519 | this.mouseStartPos = event.point.clone(); 1520 | this.originalHandleIn = this.currentSegment.handleIn.clone(); 1521 | this.originalHandleOut = this.currentSegment.handleOut.clone(); 1522 | 1523 | } else if (this.mode == 'join') { 1524 | 1525 | var path = findItemById(this.pathId); 1526 | if (path != null) { 1527 | var oldPoint = this.hitResult.segment.point.clone(); 1528 | if (this.hitResult.segment.index != 0) 1529 | this.hitResult.item.reverse(); 1530 | path.join(this.hitResult.item); 1531 | // Find nearest point to the hit point. 1532 | var imin = -1; 1533 | var dmin = 0; 1534 | for (var i = 0; i < path.segments.length; i++) { 1535 | var d = oldPoint.getDistance(path.segments[i].point); 1536 | if (imin == -1 || d < dmin) { 1537 | dmin = d; 1538 | imin = i; 1539 | } 1540 | } 1541 | this.currentSegment = path.segments[imin]; 1542 | this.currentSegment.handleIn.set(0,0); 1543 | 1544 | this.mouseStartPos = event.point.clone(); 1545 | this.originalHandleIn = this.currentSegment.handleIn.clone(); 1546 | this.originalHandleOut = this.currentSegment.handleOut.clone(); 1547 | } else { 1548 | this.currentSegment = -1; 1549 | } 1550 | 1551 | } else if (this.mode == 'remove') { 1552 | if (this.hitResult != null) { 1553 | this.hitResult.item.removeSegment(this.hitResult.segment.index); 1554 | this.hitResult = null; 1555 | } 1556 | } 1557 | 1558 | if (this.currentSegment) 1559 | this.currentSegment.selected = true; 1560 | }, 1561 | mouseup: function(event) { 1562 | if (this.mode == 'close') { 1563 | this.closePath(); 1564 | } else if (this.mode == 'join') { 1565 | this.closePath(); 1566 | } else if (this.mode == 'convert') { 1567 | this.closePath(); 1568 | } 1569 | undo.snapshot("Pen"); 1570 | this.mode = null; 1571 | this.currentSegment = null; 1572 | }, 1573 | mousedrag: function(event) { 1574 | if (this.currentSegment == null) 1575 | return; 1576 | var path = findItemById(this.pathId); 1577 | if (path == null) 1578 | return; 1579 | 1580 | var dragIn = false; 1581 | var dragOut = false; 1582 | var invert = false; 1583 | 1584 | if (this.mode == 'create') { 1585 | dragOut = true; 1586 | if (this.currentSegment.index > 0) 1587 | dragIn = true; 1588 | } else if (this.mode == 'close') { 1589 | dragIn = true; 1590 | invert = true; 1591 | } else if (this.mode == 'continue') { 1592 | dragOut = true; 1593 | } else if (this.mode == 'adjust') { 1594 | dragOut = true; 1595 | } else if (this.mode == 'join') { 1596 | dragIn = true; 1597 | invert = true; 1598 | } else if (this.mode == 'convert') { 1599 | dragIn = true; 1600 | dragOut = true; 1601 | } 1602 | 1603 | if (dragIn || dragOut) { 1604 | var delta = event.point.subtract(this.mouseStartPos); 1605 | if (invert) 1606 | delta = delta.negate(); 1607 | if (dragIn && dragOut) { 1608 | var handlePos = this.originalHandleOut.add(delta); 1609 | if (event.modifiers.shift) 1610 | handlePos = snapDeltaToAngle(handlePos, Math.PI*2/8); 1611 | this.currentSegment.handleOut = handlePos; 1612 | this.currentSegment.handleIn = handlePos.negate(); 1613 | } else if (dragOut) { 1614 | var handlePos = this.originalHandleOut.add(delta); 1615 | if (event.modifiers.shift) 1616 | handlePos = snapDeltaToAngle(handlePos, Math.PI*2/8); 1617 | this.currentSegment.handleOut = handlePos; 1618 | this.currentSegment.handleIn = handlePos.normalize(-this.originalHandleIn.length); 1619 | } else { 1620 | var handlePos = this.originalHandleIn.add(delta); 1621 | if (event.modifiers.shift) 1622 | handlePos = snapDeltaToAngle(handlePos, Math.PI*2/8); 1623 | this.currentSegment.handleIn = handlePos; 1624 | this.currentSegment.handleOut = handlePos.normalize(-this.originalHandleOut.length); 1625 | } 1626 | } 1627 | 1628 | }, 1629 | mousemove: function(event) { 1630 | this.hitTest(event); 1631 | } 1632 | }); 1633 | 1634 | 1635 | var toolStack = new paper.Tool(); 1636 | toolStack.stack = [ 1637 | toolZoomPan, 1638 | toolPen, 1639 | toolScale, 1640 | toolRotate, 1641 | toolDirectSelect, 1642 | toolSelect 1643 | ]; 1644 | toolStack.hotTool = null; 1645 | toolStack.activeTool = null; 1646 | toolStack.lastPoint = new paper.Point(); 1647 | toolStack.command = function(cb) { 1648 | if (this.activeTool != null) 1649 | return; 1650 | /* if (this.hotTool) { 1651 | this.hotTool.fire('deactivate'); 1652 | this.hotTool = null; 1653 | }*/ 1654 | if (cb) cb(); 1655 | var event = new paper.Event(); 1656 | event.point = this.lastPoint.clone(); 1657 | this.testHot('command', event); 1658 | }; 1659 | toolStack.setToolMode = function(mode) { 1660 | this.mode = mode; 1661 | var event = new paper.Event(); 1662 | event.point = this.lastPoint.clone(); 1663 | this.testHot('mode', event); 1664 | }; 1665 | toolStack.testHot = function(type, event) { 1666 | // Reset the state of the tool before testing. 1667 | var prev = this.hotTool; 1668 | this.hotTool = null; 1669 | for (var i = 0; i < this.stack.length; i++) 1670 | this.stack[i].resetHot(type, event, this.mode); 1671 | // Pick the first hot tool. 1672 | for (var i = 0; i < this.stack.length; i++) { 1673 | if (this.stack[i].testHot(type, event, this.mode)) { 1674 | this.hotTool = this.stack[i]; 1675 | break; 1676 | } 1677 | } 1678 | if (prev != this.hotTool) { 1679 | if (prev) 1680 | prev.fire('deactivate'); 1681 | if (this.hotTool) 1682 | this.hotTool.fire('activate'); 1683 | } 1684 | }; 1685 | toolStack.on({ 1686 | activate: function() { 1687 | this.activeTool = null; 1688 | this.hotTool = null; 1689 | }, 1690 | 1691 | deactivate: function() { 1692 | this.activeTool = null; 1693 | this.hotTool = null; 1694 | }, 1695 | 1696 | mousedown: function(event) { 1697 | this.lastPoint = event.point.clone(); 1698 | if (this.hotTool) { 1699 | this.activeTool = this.hotTool; 1700 | this.activeTool.fire('mousedown', event); 1701 | } 1702 | }, 1703 | 1704 | mouseup: function(event) { 1705 | this.lastPoint = event.point.clone(); 1706 | if (this.activeTool) 1707 | this.activeTool.fire('mouseup', event); 1708 | this.activeTool = null; 1709 | this.testHot('mouseup', event); 1710 | }, 1711 | 1712 | mousedrag: function(event) { 1713 | this.lastPoint = event.point.clone(); 1714 | if (this.activeTool) 1715 | this.activeTool.fire('mousedrag', event); 1716 | }, 1717 | 1718 | mousemove: function(event) { 1719 | this.lastPoint = event.point.clone(); 1720 | this.testHot('mousemove', event); 1721 | }, 1722 | 1723 | keydown: function(event) { 1724 | event.point = this.lastPoint.clone(); 1725 | if (this.activeTool) 1726 | this.activeTool.fire('keydown', event); 1727 | else 1728 | this.testHot('keydown', event); 1729 | }, 1730 | 1731 | keyup: function(event) { 1732 | event.point = this.lastPoint.clone(); 1733 | if (this.activeTool) 1734 | this.activeTool.fire('keyup', event); 1735 | else 1736 | this.testHot('keyup', event); 1737 | } 1738 | }); 1739 | 1740 | 1741 | $(document).ready(function() { 1742 | var $canvas = $('#canvas'); 1743 | paper.setup($canvas[0]); 1744 | 1745 | // HACK: Do not select the children of layers, or else 1746 | // the layers of selected objects will become selected 1747 | // after importJSON(). 1748 | paper.Layer.inject({ 1749 | _selectChildren: false 1750 | }); 1751 | 1752 | undo = new Undo(20); 1753 | 1754 | var path1 = new paper.Path.Circle(new paper.Point(180, 50), 30); 1755 | path1.strokeColor = 'black'; 1756 | var path2 = new paper.Path.Circle(new paper.Point(180, 150), 20); 1757 | path2.fillColor = 'grey'; 1758 | 1759 | undo.snapshot("Init"); 1760 | 1761 | $("#tool-select").click(function() { 1762 | toolStack.setToolMode('tool-select'); 1763 | }); 1764 | $("#tool-direct-select").click(function() { 1765 | toolStack.setToolMode('tool-direct-select'); 1766 | }); 1767 | $("#tool-pen").click(function() { 1768 | toolStack.setToolMode('tool-pen'); 1769 | }); 1770 | $("#tool-zoompan").click(function() { 1771 | toolStack.setToolMode('tool-zoompan'); 1772 | }); 1773 | 1774 | $("#undo").click(function() { 1775 | toolStack.command(function() { 1776 | if (undo.canUndo()) 1777 | undo.undo(); 1778 | }); 1779 | }); 1780 | $("#redo").click(function() { 1781 | toolStack.command(function() { 1782 | if (undo.canRedo()) 1783 | undo.redo(); 1784 | }); 1785 | }); 1786 | 1787 | $("#cut").click(function() { 1788 | cutSelection(); 1789 | }); 1790 | $("#copy").click(function() { 1791 | copySelection(); 1792 | }); 1793 | $("#paste").click(function() { 1794 | pasteSelection(); 1795 | }); 1796 | 1797 | $("#delete").click(function() { 1798 | deleteSelection(); 1799 | }); 1800 | 1801 | toolStack.activate(); 1802 | toolStack.setToolMode('tool-select'); 1803 | 1804 | paper.view.draw(); 1805 | }); 1806 | -------------------------------------------------------------------------------- /js/jquery.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * jQuery JavaScript Library v1.4.2 3 | * http://jquery.com/ 4 | * 5 | * Copyright 2010, John Resig 6 | * Dual licensed under the MIT or GPL Version 2 licenses. 7 | * http://jquery.org/license 8 | * 9 | * Includes Sizzle.js 10 | * http://sizzlejs.com/ 11 | * Copyright 2010, The Dojo Foundation 12 | * Released under the MIT, BSD, and GPL Licenses. 13 | * 14 | * Date: Sat Feb 13 22:33:48 2010 -0500 15 | */ 16 | (function(A,w){function ma(){if(!c.isReady){try{s.documentElement.doScroll("left")}catch(a){setTimeout(ma,1);return}c.ready()}}function Qa(a,b){b.src?c.ajax({url:b.src,async:false,dataType:"script"}):c.globalEval(b.text||b.textContent||b.innerHTML||"");b.parentNode&&b.parentNode.removeChild(b)}function X(a,b,d,f,e,j){var i=a.length;if(typeof b==="object"){for(var o in b)X(a,o,b[o],f,e,d);return a}if(d!==w){f=!j&&f&&c.isFunction(d);for(o=0;o)[^>]*$|^#([\w-]+)$/,Ua=/^.[^:#\[\.,]*$/,Va=/\S/, 21 | Wa=/^(\s|\u00A0)+|(\s|\u00A0)+$/g,Xa=/^<(\w+)\s*\/?>(?:<\/\1>)?$/,P=navigator.userAgent,xa=false,Q=[],L,$=Object.prototype.toString,aa=Object.prototype.hasOwnProperty,ba=Array.prototype.push,R=Array.prototype.slice,ya=Array.prototype.indexOf;c.fn=c.prototype={init:function(a,b){var d,f;if(!a)return this;if(a.nodeType){this.context=this[0]=a;this.length=1;return this}if(a==="body"&&!b){this.context=s;this[0]=s.body;this.selector="body";this.length=1;return this}if(typeof a==="string")if((d=Ta.exec(a))&& 22 | (d[1]||!b))if(d[1]){f=b?b.ownerDocument||b:s;if(a=Xa.exec(a))if(c.isPlainObject(b)){a=[s.createElement(a[1])];c.fn.attr.call(a,b,true)}else a=[f.createElement(a[1])];else{a=sa([d[1]],[f]);a=(a.cacheable?a.fragment.cloneNode(true):a.fragment).childNodes}return c.merge(this,a)}else{if(b=s.getElementById(d[2])){if(b.id!==d[2])return T.find(a);this.length=1;this[0]=b}this.context=s;this.selector=a;return this}else if(!b&&/^\w+$/.test(a)){this.selector=a;this.context=s;a=s.getElementsByTagName(a);return c.merge(this, 23 | a)}else return!b||b.jquery?(b||T).find(a):c(b).find(a);else if(c.isFunction(a))return T.ready(a);if(a.selector!==w){this.selector=a.selector;this.context=a.context}return c.makeArray(a,this)},selector:"",jquery:"1.4.2",length:0,size:function(){return this.length},toArray:function(){return R.call(this,0)},get:function(a){return a==null?this.toArray():a<0?this.slice(a)[0]:this[a]},pushStack:function(a,b,d){var f=c();c.isArray(a)?ba.apply(f,a):c.merge(f,a);f.prevObject=this;f.context=this.context;if(b=== 24 | "find")f.selector=this.selector+(this.selector?" ":"")+d;else if(b)f.selector=this.selector+"."+b+"("+d+")";return f},each:function(a,b){return c.each(this,a,b)},ready:function(a){c.bindReady();if(c.isReady)a.call(s,c);else Q&&Q.push(a);return this},eq:function(a){return a===-1?this.slice(a):this.slice(a,+a+1)},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},slice:function(){return this.pushStack(R.apply(this,arguments),"slice",R.call(arguments).join(","))},map:function(a){return this.pushStack(c.map(this, 25 | function(b,d){return a.call(b,d,b)}))},end:function(){return this.prevObject||c(null)},push:ba,sort:[].sort,splice:[].splice};c.fn.init.prototype=c.fn;c.extend=c.fn.extend=function(){var a=arguments[0]||{},b=1,d=arguments.length,f=false,e,j,i,o;if(typeof a==="boolean"){f=a;a=arguments[1]||{};b=2}if(typeof a!=="object"&&!c.isFunction(a))a={};if(d===b){a=this;--b}for(;b
a"; 34 | var e=d.getElementsByTagName("*"),j=d.getElementsByTagName("a")[0];if(!(!e||!e.length||!j)){c.support={leadingWhitespace:d.firstChild.nodeType===3,tbody:!d.getElementsByTagName("tbody").length,htmlSerialize:!!d.getElementsByTagName("link").length,style:/red/.test(j.getAttribute("style")),hrefNormalized:j.getAttribute("href")==="/a",opacity:/^0.55$/.test(j.style.opacity),cssFloat:!!j.style.cssFloat,checkOn:d.getElementsByTagName("input")[0].value==="on",optSelected:s.createElement("select").appendChild(s.createElement("option")).selected, 35 | parentNode:d.removeChild(d.appendChild(s.createElement("div"))).parentNode===null,deleteExpando:true,checkClone:false,scriptEval:false,noCloneEvent:true,boxModel:null};b.type="text/javascript";try{b.appendChild(s.createTextNode("window."+f+"=1;"))}catch(i){}a.insertBefore(b,a.firstChild);if(A[f]){c.support.scriptEval=true;delete A[f]}try{delete b.test}catch(o){c.support.deleteExpando=false}a.removeChild(b);if(d.attachEvent&&d.fireEvent){d.attachEvent("onclick",function k(){c.support.noCloneEvent= 36 | false;d.detachEvent("onclick",k)});d.cloneNode(true).fireEvent("onclick")}d=s.createElement("div");d.innerHTML="";a=s.createDocumentFragment();a.appendChild(d.firstChild);c.support.checkClone=a.cloneNode(true).cloneNode(true).lastChild.checked;c(function(){var k=s.createElement("div");k.style.width=k.style.paddingLeft="1px";s.body.appendChild(k);c.boxModel=c.support.boxModel=k.offsetWidth===2;s.body.removeChild(k).style.display="none"});a=function(k){var n= 37 | s.createElement("div");k="on"+k;var r=k in n;if(!r){n.setAttribute(k,"return;");r=typeof n[k]==="function"}return r};c.support.submitBubbles=a("submit");c.support.changeBubbles=a("change");a=b=d=e=j=null}})();c.props={"for":"htmlFor","class":"className",readonly:"readOnly",maxlength:"maxLength",cellspacing:"cellSpacing",rowspan:"rowSpan",colspan:"colSpan",tabindex:"tabIndex",usemap:"useMap",frameborder:"frameBorder"};var G="jQuery"+J(),Ya=0,za={};c.extend({cache:{},expando:G,noData:{embed:true,object:true, 38 | applet:true},data:function(a,b,d){if(!(a.nodeName&&c.noData[a.nodeName.toLowerCase()])){a=a==A?za:a;var f=a[G],e=c.cache;if(!f&&typeof b==="string"&&d===w)return null;f||(f=++Ya);if(typeof b==="object"){a[G]=f;e[f]=c.extend(true,{},b)}else if(!e[f]){a[G]=f;e[f]={}}a=e[f];if(d!==w)a[b]=d;return typeof b==="string"?a[b]:a}},removeData:function(a,b){if(!(a.nodeName&&c.noData[a.nodeName.toLowerCase()])){a=a==A?za:a;var d=a[G],f=c.cache,e=f[d];if(b){if(e){delete e[b];c.isEmptyObject(e)&&c.removeData(a)}}else{if(c.support.deleteExpando)delete a[c.expando]; 39 | else a.removeAttribute&&a.removeAttribute(c.expando);delete f[d]}}}});c.fn.extend({data:function(a,b){if(typeof a==="undefined"&&this.length)return c.data(this[0]);else if(typeof a==="object")return this.each(function(){c.data(this,a)});var d=a.split(".");d[1]=d[1]?"."+d[1]:"";if(b===w){var f=this.triggerHandler("getData"+d[1]+"!",[d[0]]);if(f===w&&this.length)f=c.data(this[0],a);return f===w&&d[1]?this.data(d[0]):f}else return this.trigger("setData"+d[1]+"!",[d[0],b]).each(function(){c.data(this, 40 | a,b)})},removeData:function(a){return this.each(function(){c.removeData(this,a)})}});c.extend({queue:function(a,b,d){if(a){b=(b||"fx")+"queue";var f=c.data(a,b);if(!d)return f||[];if(!f||c.isArray(d))f=c.data(a,b,c.makeArray(d));else f.push(d);return f}},dequeue:function(a,b){b=b||"fx";var d=c.queue(a,b),f=d.shift();if(f==="inprogress")f=d.shift();if(f){b==="fx"&&d.unshift("inprogress");f.call(a,function(){c.dequeue(a,b)})}}});c.fn.extend({queue:function(a,b){if(typeof a!=="string"){b=a;a="fx"}if(b=== 41 | w)return c.queue(this[0],a);return this.each(function(){var d=c.queue(this,a,b);a==="fx"&&d[0]!=="inprogress"&&c.dequeue(this,a)})},dequeue:function(a){return this.each(function(){c.dequeue(this,a)})},delay:function(a,b){a=c.fx?c.fx.speeds[a]||a:a;b=b||"fx";return this.queue(b,function(){var d=this;setTimeout(function(){c.dequeue(d,b)},a)})},clearQueue:function(a){return this.queue(a||"fx",[])}});var Aa=/[\n\t]/g,ca=/\s+/,Za=/\r/g,$a=/href|src|style/,ab=/(button|input)/i,bb=/(button|input|object|select|textarea)/i, 42 | cb=/^(a|area)$/i,Ba=/radio|checkbox/;c.fn.extend({attr:function(a,b){return X(this,a,b,true,c.attr)},removeAttr:function(a){return this.each(function(){c.attr(this,a,"");this.nodeType===1&&this.removeAttribute(a)})},addClass:function(a){if(c.isFunction(a))return this.each(function(n){var r=c(this);r.addClass(a.call(this,n,r.attr("class")))});if(a&&typeof a==="string")for(var b=(a||"").split(ca),d=0,f=this.length;d-1)return true;return false},val:function(a){if(a===w){var b=this[0];if(b){if(c.nodeName(b,"option"))return(b.attributes.value||{}).specified?b.value:b.text;if(c.nodeName(b,"select")){var d=b.selectedIndex,f=[],e=b.options;b=b.type==="select-one";if(d<0)return null;var j=b?d:0;for(d=b?d+1:e.length;j=0;else if(c.nodeName(this,"select")){var u=c.makeArray(r);c("option",this).each(function(){this.selected= 47 | c.inArray(c(this).val(),u)>=0});if(!u.length)this.selectedIndex=-1}else this.value=r}})}});c.extend({attrFn:{val:true,css:true,html:true,text:true,data:true,width:true,height:true,offset:true},attr:function(a,b,d,f){if(!a||a.nodeType===3||a.nodeType===8)return w;if(f&&b in c.attrFn)return c(a)[b](d);f=a.nodeType!==1||!c.isXMLDoc(a);var e=d!==w;b=f&&c.props[b]||b;if(a.nodeType===1){var j=$a.test(b);if(b in a&&f&&!j){if(e){b==="type"&&ab.test(a.nodeName)&&a.parentNode&&c.error("type property can't be changed"); 48 | a[b]=d}if(c.nodeName(a,"form")&&a.getAttributeNode(b))return a.getAttributeNode(b).nodeValue;if(b==="tabIndex")return(b=a.getAttributeNode("tabIndex"))&&b.specified?b.value:bb.test(a.nodeName)||cb.test(a.nodeName)&&a.href?0:w;return a[b]}if(!c.support.style&&f&&b==="style"){if(e)a.style.cssText=""+d;return a.style.cssText}e&&a.setAttribute(b,""+d);a=!c.support.hrefNormalized&&f&&j?a.getAttribute(b,2):a.getAttribute(b);return a===null?w:a}return c.style(a,b,d)}});var O=/\.(.*)$/,db=function(a){return a.replace(/[^\w\s\.\|`]/g, 49 | function(b){return"\\"+b})};c.event={add:function(a,b,d,f){if(!(a.nodeType===3||a.nodeType===8)){if(a.setInterval&&a!==A&&!a.frameElement)a=A;var e,j;if(d.handler){e=d;d=e.handler}if(!d.guid)d.guid=c.guid++;if(j=c.data(a)){var i=j.events=j.events||{},o=j.handle;if(!o)j.handle=o=function(){return typeof c!=="undefined"&&!c.event.triggered?c.event.handle.apply(o.elem,arguments):w};o.elem=a;b=b.split(" ");for(var k,n=0,r;k=b[n++];){j=e?c.extend({},e):{handler:d,data:f};if(k.indexOf(".")>-1){r=k.split("."); 50 | k=r.shift();j.namespace=r.slice(0).sort().join(".")}else{r=[];j.namespace=""}j.type=k;j.guid=d.guid;var u=i[k],z=c.event.special[k]||{};if(!u){u=i[k]=[];if(!z.setup||z.setup.call(a,f,r,o)===false)if(a.addEventListener)a.addEventListener(k,o,false);else a.attachEvent&&a.attachEvent("on"+k,o)}if(z.add){z.add.call(a,j);if(!j.handler.guid)j.handler.guid=d.guid}u.push(j);c.event.global[k]=true}a=null}}},global:{},remove:function(a,b,d,f){if(!(a.nodeType===3||a.nodeType===8)){var e,j=0,i,o,k,n,r,u,z=c.data(a), 51 | C=z&&z.events;if(z&&C){if(b&&b.type){d=b.handler;b=b.type}if(!b||typeof b==="string"&&b.charAt(0)==="."){b=b||"";for(e in C)c.event.remove(a,e+b)}else{for(b=b.split(" ");e=b[j++];){n=e;i=e.indexOf(".")<0;o=[];if(!i){o=e.split(".");e=o.shift();k=new RegExp("(^|\\.)"+c.map(o.slice(0).sort(),db).join("\\.(?:.*\\.)?")+"(\\.|$)")}if(r=C[e])if(d){n=c.event.special[e]||{};for(B=f||0;B=0){a.type= 53 | e=e.slice(0,-1);a.exclusive=true}if(!d){a.stopPropagation();c.event.global[e]&&c.each(c.cache,function(){this.events&&this.events[e]&&c.event.trigger(a,b,this.handle.elem)})}if(!d||d.nodeType===3||d.nodeType===8)return w;a.result=w;a.target=d;b=c.makeArray(b);b.unshift(a)}a.currentTarget=d;(f=c.data(d,"handle"))&&f.apply(d,b);f=d.parentNode||d.ownerDocument;try{if(!(d&&d.nodeName&&c.noData[d.nodeName.toLowerCase()]))if(d["on"+e]&&d["on"+e].apply(d,b)===false)a.result=false}catch(j){}if(!a.isPropagationStopped()&& 54 | f)c.event.trigger(a,b,f,true);else if(!a.isDefaultPrevented()){f=a.target;var i,o=c.nodeName(f,"a")&&e==="click",k=c.event.special[e]||{};if((!k._default||k._default.call(d,a)===false)&&!o&&!(f&&f.nodeName&&c.noData[f.nodeName.toLowerCase()])){try{if(f[e]){if(i=f["on"+e])f["on"+e]=null;c.event.triggered=true;f[e]()}}catch(n){}if(i)f["on"+e]=i;c.event.triggered=false}}},handle:function(a){var b,d,f,e;a=arguments[0]=c.event.fix(a||A.event);a.currentTarget=this;b=a.type.indexOf(".")<0&&!a.exclusive; 55 | if(!b){d=a.type.split(".");a.type=d.shift();f=new RegExp("(^|\\.)"+d.slice(0).sort().join("\\.(?:.*\\.)?")+"(\\.|$)")}e=c.data(this,"events");d=e[a.type];if(e&&d){d=d.slice(0);e=0;for(var j=d.length;e-1?c.map(a.options,function(f){return f.selected}).join("-"):"";else if(a.nodeName.toLowerCase()==="select")d=a.selectedIndex;return d},fa=function(a,b){var d=a.target,f,e;if(!(!da.test(d.nodeName)||d.readOnly)){f=c.data(d,"_change_data");e=Fa(d);if(a.type!=="focusout"||d.type!=="radio")c.data(d,"_change_data", 63 | e);if(!(f===w||e===f))if(f!=null||e){a.type="change";return c.event.trigger(a,b,d)}}};c.event.special.change={filters:{focusout:fa,click:function(a){var b=a.target,d=b.type;if(d==="radio"||d==="checkbox"||b.nodeName.toLowerCase()==="select")return fa.call(this,a)},keydown:function(a){var b=a.target,d=b.type;if(a.keyCode===13&&b.nodeName.toLowerCase()!=="textarea"||a.keyCode===32&&(d==="checkbox"||d==="radio")||d==="select-multiple")return fa.call(this,a)},beforeactivate:function(a){a=a.target;c.data(a, 64 | "_change_data",Fa(a))}},setup:function(){if(this.type==="file")return false;for(var a in ea)c.event.add(this,a+".specialChange",ea[a]);return da.test(this.nodeName)},teardown:function(){c.event.remove(this,".specialChange");return da.test(this.nodeName)}};ea=c.event.special.change.filters}s.addEventListener&&c.each({focus:"focusin",blur:"focusout"},function(a,b){function d(f){f=c.event.fix(f);f.type=b;return c.event.handle.call(this,f)}c.event.special[b]={setup:function(){this.addEventListener(a, 65 | d,true)},teardown:function(){this.removeEventListener(a,d,true)}}});c.each(["bind","one"],function(a,b){c.fn[b]=function(d,f,e){if(typeof d==="object"){for(var j in d)this[b](j,f,d[j],e);return this}if(c.isFunction(f)){e=f;f=w}var i=b==="one"?c.proxy(e,function(k){c(this).unbind(k,i);return e.apply(this,arguments)}):e;if(d==="unload"&&b!=="one")this.one(d,f,e);else{j=0;for(var o=this.length;j0){y=t;break}}t=t[g]}m[q]=y}}}var f=/((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^[\]]*\]|['"][^'"]*['"]|[^[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g, 71 | e=0,j=Object.prototype.toString,i=false,o=true;[0,0].sort(function(){o=false;return 0});var k=function(g,h,l,m){l=l||[];var q=h=h||s;if(h.nodeType!==1&&h.nodeType!==9)return[];if(!g||typeof g!=="string")return l;for(var p=[],v,t,y,S,H=true,M=x(h),I=g;(f.exec(""),v=f.exec(I))!==null;){I=v[3];p.push(v[1]);if(v[2]){S=v[3];break}}if(p.length>1&&r.exec(g))if(p.length===2&&n.relative[p[0]])t=ga(p[0]+p[1],h);else for(t=n.relative[p[0]]?[h]:k(p.shift(),h);p.length;){g=p.shift();if(n.relative[g])g+=p.shift(); 72 | t=ga(g,t)}else{if(!m&&p.length>1&&h.nodeType===9&&!M&&n.match.ID.test(p[0])&&!n.match.ID.test(p[p.length-1])){v=k.find(p.shift(),h,M);h=v.expr?k.filter(v.expr,v.set)[0]:v.set[0]}if(h){v=m?{expr:p.pop(),set:z(m)}:k.find(p.pop(),p.length===1&&(p[0]==="~"||p[0]==="+")&&h.parentNode?h.parentNode:h,M);t=v.expr?k.filter(v.expr,v.set):v.set;if(p.length>0)y=z(t);else H=false;for(;p.length;){var D=p.pop();v=D;if(n.relative[D])v=p.pop();else D="";if(v==null)v=h;n.relative[D](y,v,M)}}else y=[]}y||(y=t);y||k.error(D|| 73 | g);if(j.call(y)==="[object Array]")if(H)if(h&&h.nodeType===1)for(g=0;y[g]!=null;g++){if(y[g]&&(y[g]===true||y[g].nodeType===1&&E(h,y[g])))l.push(t[g])}else for(g=0;y[g]!=null;g++)y[g]&&y[g].nodeType===1&&l.push(t[g]);else l.push.apply(l,y);else z(y,l);if(S){k(S,q,l,m);k.uniqueSort(l)}return l};k.uniqueSort=function(g){if(B){i=o;g.sort(B);if(i)for(var h=1;h":function(g,h){var l=typeof h==="string";if(l&&!/\W/.test(h)){h=h.toLowerCase();for(var m=0,q=g.length;m=0))l||m.push(v);else if(l)h[p]=false;return false},ID:function(g){return g[1].replace(/\\/g,"")},TAG:function(g){return g[1].toLowerCase()}, 80 | CHILD:function(g){if(g[1]==="nth"){var h=/(-?)(\d*)n((?:\+|-)?\d*)/.exec(g[2]==="even"&&"2n"||g[2]==="odd"&&"2n+1"||!/\D/.test(g[2])&&"0n+"+g[2]||g[2]);g[2]=h[1]+(h[2]||1)-0;g[3]=h[3]-0}g[0]=e++;return g},ATTR:function(g,h,l,m,q,p){h=g[1].replace(/\\/g,"");if(!p&&n.attrMap[h])g[1]=n.attrMap[h];if(g[2]==="~=")g[4]=" "+g[4]+" ";return g},PSEUDO:function(g,h,l,m,q){if(g[1]==="not")if((f.exec(g[3])||"").length>1||/^\w/.test(g[3]))g[3]=k(g[3],null,null,h);else{g=k.filter(g[3],h,l,true^q);l||m.push.apply(m, 81 | g);return false}else if(n.match.POS.test(g[0])||n.match.CHILD.test(g[0]))return true;return g},POS:function(g){g.unshift(true);return g}},filters:{enabled:function(g){return g.disabled===false&&g.type!=="hidden"},disabled:function(g){return g.disabled===true},checked:function(g){return g.checked===true},selected:function(g){return g.selected===true},parent:function(g){return!!g.firstChild},empty:function(g){return!g.firstChild},has:function(g,h,l){return!!k(l[3],g).length},header:function(g){return/h\d/i.test(g.nodeName)}, 82 | text:function(g){return"text"===g.type},radio:function(g){return"radio"===g.type},checkbox:function(g){return"checkbox"===g.type},file:function(g){return"file"===g.type},password:function(g){return"password"===g.type},submit:function(g){return"submit"===g.type},image:function(g){return"image"===g.type},reset:function(g){return"reset"===g.type},button:function(g){return"button"===g.type||g.nodeName.toLowerCase()==="button"},input:function(g){return/input|select|textarea|button/i.test(g.nodeName)}}, 83 | setFilters:{first:function(g,h){return h===0},last:function(g,h,l,m){return h===m.length-1},even:function(g,h){return h%2===0},odd:function(g,h){return h%2===1},lt:function(g,h,l){return hl[3]-0},nth:function(g,h,l){return l[3]-0===h},eq:function(g,h,l){return l[3]-0===h}},filter:{PSEUDO:function(g,h,l,m){var q=h[1],p=n.filters[q];if(p)return p(g,l,h,m);else if(q==="contains")return(g.textContent||g.innerText||a([g])||"").indexOf(h[3])>=0;else if(q==="not"){h= 84 | h[3];l=0;for(m=h.length;l=0}},ID:function(g,h){return g.nodeType===1&&g.getAttribute("id")===h},TAG:function(g,h){return h==="*"&&g.nodeType===1||g.nodeName.toLowerCase()===h},CLASS:function(g,h){return(" "+(g.className||g.getAttribute("class"))+" ").indexOf(h)>-1},ATTR:function(g,h){var l=h[1];g=n.attrHandle[l]?n.attrHandle[l](g):g[l]!=null?g[l]:g.getAttribute(l);l=g+"";var m=h[2];h=h[4];return g==null?m==="!=":m=== 86 | "="?l===h:m==="*="?l.indexOf(h)>=0:m==="~="?(" "+l+" ").indexOf(h)>=0:!h?l&&g!==false:m==="!="?l!==h:m==="^="?l.indexOf(h)===0:m==="$="?l.substr(l.length-h.length)===h:m==="|="?l===h||l.substr(0,h.length+1)===h+"-":false},POS:function(g,h,l,m){var q=n.setFilters[h[2]];if(q)return q(g,l,h,m)}}},r=n.match.POS;for(var u in n.match){n.match[u]=new RegExp(n.match[u].source+/(?![^\[]*\])(?![^\(]*\))/.source);n.leftMatch[u]=new RegExp(/(^(?:.|\r|\n)*?)/.source+n.match[u].source.replace(/\\(\d+)/g,function(g, 87 | h){return"\\"+(h-0+1)}))}var z=function(g,h){g=Array.prototype.slice.call(g,0);if(h){h.push.apply(h,g);return h}return g};try{Array.prototype.slice.call(s.documentElement.childNodes,0)}catch(C){z=function(g,h){h=h||[];if(j.call(g)==="[object Array]")Array.prototype.push.apply(h,g);else if(typeof g.length==="number")for(var l=0,m=g.length;l";var l=s.documentElement;l.insertBefore(g,l.firstChild);if(s.getElementById(h)){n.find.ID=function(m,q,p){if(typeof q.getElementById!=="undefined"&&!p)return(q=q.getElementById(m[1]))?q.id===m[1]||typeof q.getAttributeNode!=="undefined"&& 90 | q.getAttributeNode("id").nodeValue===m[1]?[q]:w:[]};n.filter.ID=function(m,q){var p=typeof m.getAttributeNode!=="undefined"&&m.getAttributeNode("id");return m.nodeType===1&&p&&p.nodeValue===q}}l.removeChild(g);l=g=null})();(function(){var g=s.createElement("div");g.appendChild(s.createComment(""));if(g.getElementsByTagName("*").length>0)n.find.TAG=function(h,l){l=l.getElementsByTagName(h[1]);if(h[1]==="*"){h=[];for(var m=0;l[m];m++)l[m].nodeType===1&&h.push(l[m]);l=h}return l};g.innerHTML=""; 91 | if(g.firstChild&&typeof g.firstChild.getAttribute!=="undefined"&&g.firstChild.getAttribute("href")!=="#")n.attrHandle.href=function(h){return h.getAttribute("href",2)};g=null})();s.querySelectorAll&&function(){var g=k,h=s.createElement("div");h.innerHTML="

";if(!(h.querySelectorAll&&h.querySelectorAll(".TEST").length===0)){k=function(m,q,p,v){q=q||s;if(!v&&q.nodeType===9&&!x(q))try{return z(q.querySelectorAll(m),p)}catch(t){}return g(m,q,p,v)};for(var l in g)k[l]=g[l];h=null}}(); 92 | (function(){var g=s.createElement("div");g.innerHTML="
";if(!(!g.getElementsByClassName||g.getElementsByClassName("e").length===0)){g.lastChild.className="e";if(g.getElementsByClassName("e").length!==1){n.order.splice(1,0,"CLASS");n.find.CLASS=function(h,l,m){if(typeof l.getElementsByClassName!=="undefined"&&!m)return l.getElementsByClassName(h[1])};g=null}}})();var E=s.compareDocumentPosition?function(g,h){return!!(g.compareDocumentPosition(h)&16)}: 93 | function(g,h){return g!==h&&(g.contains?g.contains(h):true)},x=function(g){return(g=(g?g.ownerDocument||g:0).documentElement)?g.nodeName!=="HTML":false},ga=function(g,h){var l=[],m="",q;for(h=h.nodeType?[h]:h;q=n.match.PSEUDO.exec(g);){m+=q[0];g=g.replace(n.match.PSEUDO,"")}g=n.relative[g]?g+"*":g;q=0;for(var p=h.length;q=0===d})};c.fn.extend({find:function(a){for(var b=this.pushStack("","find",a),d=0,f=0,e=this.length;f0)for(var j=d;j0},closest:function(a,b){if(c.isArray(a)){var d=[],f=this[0],e,j= 96 | {},i;if(f&&a.length){e=0;for(var o=a.length;e-1:c(f).is(e)){d.push({selector:i,elem:f});delete j[i]}}f=f.parentNode}}return d}var k=c.expr.match.POS.test(a)?c(a,b||this.context):null;return this.map(function(n,r){for(;r&&r.ownerDocument&&r!==b;){if(k?k.index(r)>-1:c(r).is(a))return r;r=r.parentNode}return null})},index:function(a){if(!a||typeof a=== 97 | "string")return c.inArray(this[0],a?c(a):this.parent().children());return c.inArray(a.jquery?a[0]:a,this)},add:function(a,b){a=typeof a==="string"?c(a,b||this.context):c.makeArray(a);b=c.merge(this.get(),a);return this.pushStack(qa(a[0])||qa(b[0])?b:c.unique(b))},andSelf:function(){return this.add(this.prevObject)}});c.each({parent:function(a){return(a=a.parentNode)&&a.nodeType!==11?a:null},parents:function(a){return c.dir(a,"parentNode")},parentsUntil:function(a,b,d){return c.dir(a,"parentNode", 98 | d)},next:function(a){return c.nth(a,2,"nextSibling")},prev:function(a){return c.nth(a,2,"previousSibling")},nextAll:function(a){return c.dir(a,"nextSibling")},prevAll:function(a){return c.dir(a,"previousSibling")},nextUntil:function(a,b,d){return c.dir(a,"nextSibling",d)},prevUntil:function(a,b,d){return c.dir(a,"previousSibling",d)},siblings:function(a){return c.sibling(a.parentNode.firstChild,a)},children:function(a){return c.sibling(a.firstChild)},contents:function(a){return c.nodeName(a,"iframe")? 99 | a.contentDocument||a.contentWindow.document:c.makeArray(a.childNodes)}},function(a,b){c.fn[a]=function(d,f){var e=c.map(this,b,d);eb.test(a)||(f=d);if(f&&typeof f==="string")e=c.filter(f,e);e=this.length>1?c.unique(e):e;if((this.length>1||gb.test(f))&&fb.test(a))e=e.reverse();return this.pushStack(e,a,R.call(arguments).join(","))}});c.extend({filter:function(a,b,d){if(d)a=":not("+a+")";return c.find.matches(a,b)},dir:function(a,b,d){var f=[];for(a=a[b];a&&a.nodeType!==9&&(d===w||a.nodeType!==1||!c(a).is(d));){a.nodeType=== 100 | 1&&f.push(a);a=a[b]}return f},nth:function(a,b,d){b=b||1;for(var f=0;a;a=a[d])if(a.nodeType===1&&++f===b)break;return a},sibling:function(a,b){for(var d=[];a;a=a.nextSibling)a.nodeType===1&&a!==b&&d.push(a);return d}});var Ja=/ jQuery\d+="(?:\d+|null)"/g,V=/^\s+/,Ka=/(<([\w:]+)[^>]*?)\/>/g,hb=/^(?:area|br|col|embed|hr|img|input|link|meta|param)$/i,La=/<([\w:]+)/,ib=/"},F={option:[1,""],legend:[1,"
","
"],thead:[1,"","
"],tr:[2,"","
"],td:[3,"","
"],col:[2,"","
"],area:[1,"",""],_default:[0,"",""]};F.optgroup=F.option;F.tbody=F.tfoot=F.colgroup=F.caption=F.thead;F.th=F.td;if(!c.support.htmlSerialize)F._default=[1,"div
","
"];c.fn.extend({text:function(a){if(c.isFunction(a))return this.each(function(b){var d= 102 | c(this);d.text(a.call(this,b,d.text()))});if(typeof a!=="object"&&a!==w)return this.empty().append((this[0]&&this[0].ownerDocument||s).createTextNode(a));return c.text(this)},wrapAll:function(a){if(c.isFunction(a))return this.each(function(d){c(this).wrapAll(a.call(this,d))});if(this[0]){var b=c(a,this[0].ownerDocument).eq(0).clone(true);this[0].parentNode&&b.insertBefore(this[0]);b.map(function(){for(var d=this;d.firstChild&&d.firstChild.nodeType===1;)d=d.firstChild;return d}).append(this)}return this}, 103 | wrapInner:function(a){if(c.isFunction(a))return this.each(function(b){c(this).wrapInner(a.call(this,b))});return this.each(function(){var b=c(this),d=b.contents();d.length?d.wrapAll(a):b.append(a)})},wrap:function(a){return this.each(function(){c(this).wrapAll(a)})},unwrap:function(){return this.parent().each(function(){c.nodeName(this,"body")||c(this).replaceWith(this.childNodes)}).end()},append:function(){return this.domManip(arguments,true,function(a){this.nodeType===1&&this.appendChild(a)})}, 104 | prepend:function(){return this.domManip(arguments,true,function(a){this.nodeType===1&&this.insertBefore(a,this.firstChild)})},before:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,false,function(b){this.parentNode.insertBefore(b,this)});else if(arguments.length){var a=c(arguments[0]);a.push.apply(a,this.toArray());return this.pushStack(a,"before",arguments)}},after:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,false,function(b){this.parentNode.insertBefore(b, 105 | this.nextSibling)});else if(arguments.length){var a=this.pushStack(this,"after",arguments);a.push.apply(a,c(arguments[0]).toArray());return a}},remove:function(a,b){for(var d=0,f;(f=this[d])!=null;d++)if(!a||c.filter(a,[f]).length){if(!b&&f.nodeType===1){c.cleanData(f.getElementsByTagName("*"));c.cleanData([f])}f.parentNode&&f.parentNode.removeChild(f)}return this},empty:function(){for(var a=0,b;(b=this[a])!=null;a++)for(b.nodeType===1&&c.cleanData(b.getElementsByTagName("*"));b.firstChild;)b.removeChild(b.firstChild); 106 | return this},clone:function(a){var b=this.map(function(){if(!c.support.noCloneEvent&&!c.isXMLDoc(this)){var d=this.outerHTML,f=this.ownerDocument;if(!d){d=f.createElement("div");d.appendChild(this.cloneNode(true));d=d.innerHTML}return c.clean([d.replace(Ja,"").replace(/=([^="'>\s]+\/)>/g,'="$1">').replace(V,"")],f)[0]}else return this.cloneNode(true)});if(a===true){ra(this,b);ra(this.find("*"),b.find("*"))}return b},html:function(a){if(a===w)return this[0]&&this[0].nodeType===1?this[0].innerHTML.replace(Ja, 107 | ""):null;else if(typeof a==="string"&&!ta.test(a)&&(c.support.leadingWhitespace||!V.test(a))&&!F[(La.exec(a)||["",""])[1].toLowerCase()]){a=a.replace(Ka,Ma);try{for(var b=0,d=this.length;b0||e.cacheable||this.length>1?k.cloneNode(true):k)}o.length&&c.each(o,Qa)}return this}});c.fragments={};c.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(a,b){c.fn[a]=function(d){var f=[];d=c(d);var e=this.length===1&&this[0].parentNode;if(e&&e.nodeType===11&&e.childNodes.length===1&&d.length===1){d[b](this[0]); 111 | return this}else{e=0;for(var j=d.length;e0?this.clone(true):this).get();c.fn[b].apply(c(d[e]),i);f=f.concat(i)}return this.pushStack(f,a,d.selector)}}});c.extend({clean:function(a,b,d,f){b=b||s;if(typeof b.createElement==="undefined")b=b.ownerDocument||b[0]&&b[0].ownerDocument||s;for(var e=[],j=0,i;(i=a[j])!=null;j++){if(typeof i==="number")i+="";if(i){if(typeof i==="string"&&!jb.test(i))i=b.createTextNode(i);else if(typeof i==="string"){i=i.replace(Ka,Ma);var o=(La.exec(i)||["", 112 | ""])[1].toLowerCase(),k=F[o]||F._default,n=k[0],r=b.createElement("div");for(r.innerHTML=k[1]+i+k[2];n--;)r=r.lastChild;if(!c.support.tbody){n=ib.test(i);o=o==="table"&&!n?r.firstChild&&r.firstChild.childNodes:k[1]===""&&!n?r.childNodes:[];for(k=o.length-1;k>=0;--k)c.nodeName(o[k],"tbody")&&!o[k].childNodes.length&&o[k].parentNode.removeChild(o[k])}!c.support.leadingWhitespace&&V.test(i)&&r.insertBefore(b.createTextNode(V.exec(i)[0]),r.firstChild);i=r.childNodes}if(i.nodeType)e.push(i);else e= 113 | c.merge(e,i)}}if(d)for(j=0;e[j];j++)if(f&&c.nodeName(e[j],"script")&&(!e[j].type||e[j].type.toLowerCase()==="text/javascript"))f.push(e[j].parentNode?e[j].parentNode.removeChild(e[j]):e[j]);else{e[j].nodeType===1&&e.splice.apply(e,[j+1,0].concat(c.makeArray(e[j].getElementsByTagName("script"))));d.appendChild(e[j])}return e},cleanData:function(a){for(var b,d,f=c.cache,e=c.event.special,j=c.support.deleteExpando,i=0,o;(o=a[i])!=null;i++)if(d=o[c.expando]){b=f[d];if(b.events)for(var k in b.events)e[k]? 114 | c.event.remove(o,k):Ca(o,k,b.handle);if(j)delete o[c.expando];else o.removeAttribute&&o.removeAttribute(c.expando);delete f[d]}}});var kb=/z-?index|font-?weight|opacity|zoom|line-?height/i,Na=/alpha\([^)]*\)/,Oa=/opacity=([^)]*)/,ha=/float/i,ia=/-([a-z])/ig,lb=/([A-Z])/g,mb=/^-?\d+(?:px)?$/i,nb=/^-?\d/,ob={position:"absolute",visibility:"hidden",display:"block"},pb=["Left","Right"],qb=["Top","Bottom"],rb=s.defaultView&&s.defaultView.getComputedStyle,Pa=c.support.cssFloat?"cssFloat":"styleFloat",ja= 115 | function(a,b){return b.toUpperCase()};c.fn.css=function(a,b){return X(this,a,b,true,function(d,f,e){if(e===w)return c.curCSS(d,f);if(typeof e==="number"&&!kb.test(f))e+="px";c.style(d,f,e)})};c.extend({style:function(a,b,d){if(!a||a.nodeType===3||a.nodeType===8)return w;if((b==="width"||b==="height")&&parseFloat(d)<0)d=w;var f=a.style||a,e=d!==w;if(!c.support.opacity&&b==="opacity"){if(e){f.zoom=1;b=parseInt(d,10)+""==="NaN"?"":"alpha(opacity="+d*100+")";a=f.filter||c.curCSS(a,"filter")||"";f.filter= 116 | Na.test(a)?a.replace(Na,b):b}return f.filter&&f.filter.indexOf("opacity=")>=0?parseFloat(Oa.exec(f.filter)[1])/100+"":""}if(ha.test(b))b=Pa;b=b.replace(ia,ja);if(e)f[b]=d;return f[b]},css:function(a,b,d,f){if(b==="width"||b==="height"){var e,j=b==="width"?pb:qb;function i(){e=b==="width"?a.offsetWidth:a.offsetHeight;f!=="border"&&c.each(j,function(){f||(e-=parseFloat(c.curCSS(a,"padding"+this,true))||0);if(f==="margin")e+=parseFloat(c.curCSS(a,"margin"+this,true))||0;else e-=parseFloat(c.curCSS(a, 117 | "border"+this+"Width",true))||0})}a.offsetWidth!==0?i():c.swap(a,ob,i);return Math.max(0,Math.round(e))}return c.curCSS(a,b,d)},curCSS:function(a,b,d){var f,e=a.style;if(!c.support.opacity&&b==="opacity"&&a.currentStyle){f=Oa.test(a.currentStyle.filter||"")?parseFloat(RegExp.$1)/100+"":"";return f===""?"1":f}if(ha.test(b))b=Pa;if(!d&&e&&e[b])f=e[b];else if(rb){if(ha.test(b))b="float";b=b.replace(lb,"-$1").toLowerCase();e=a.ownerDocument.defaultView;if(!e)return null;if(a=e.getComputedStyle(a,null))f= 118 | a.getPropertyValue(b);if(b==="opacity"&&f==="")f="1"}else if(a.currentStyle){d=b.replace(ia,ja);f=a.currentStyle[b]||a.currentStyle[d];if(!mb.test(f)&&nb.test(f)){b=e.left;var j=a.runtimeStyle.left;a.runtimeStyle.left=a.currentStyle.left;e.left=d==="fontSize"?"1em":f||0;f=e.pixelLeft+"px";e.left=b;a.runtimeStyle.left=j}}return f},swap:function(a,b,d){var f={};for(var e in b){f[e]=a.style[e];a.style[e]=b[e]}d.call(a);for(e in b)a.style[e]=f[e]}});if(c.expr&&c.expr.filters){c.expr.filters.hidden=function(a){var b= 119 | a.offsetWidth,d=a.offsetHeight,f=a.nodeName.toLowerCase()==="tr";return b===0&&d===0&&!f?true:b>0&&d>0&&!f?false:c.curCSS(a,"display")==="none"};c.expr.filters.visible=function(a){return!c.expr.filters.hidden(a)}}var sb=J(),tb=//gi,ub=/select|textarea/i,vb=/color|date|datetime|email|hidden|month|number|password|range|search|tel|text|time|url|week/i,N=/=\?(&|$)/,ka=/\?/,wb=/(\?|&)_=.*?(&|$)/,xb=/^(\w+:)?\/\/([^\/?#]+)/,yb=/%20/g,zb=c.fn.load;c.fn.extend({load:function(a,b,d){if(typeof a!== 120 | "string")return zb.call(this,a);else if(!this.length)return this;var f=a.indexOf(" ");if(f>=0){var e=a.slice(f,a.length);a=a.slice(0,f)}f="GET";if(b)if(c.isFunction(b)){d=b;b=null}else if(typeof b==="object"){b=c.param(b,c.ajaxSettings.traditional);f="POST"}var j=this;c.ajax({url:a,type:f,dataType:"html",data:b,complete:function(i,o){if(o==="success"||o==="notmodified")j.html(e?c("
").append(i.responseText.replace(tb,"")).find(e):i.responseText);d&&j.each(d,[i.responseText,o,i])}});return this}, 121 | serialize:function(){return c.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?c.makeArray(this.elements):this}).filter(function(){return this.name&&!this.disabled&&(this.checked||ub.test(this.nodeName)||vb.test(this.type))}).map(function(a,b){a=c(this).val();return a==null?null:c.isArray(a)?c.map(a,function(d){return{name:b.name,value:d}}):{name:b.name,value:a}}).get()}});c.each("ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split(" "), 122 | function(a,b){c.fn[b]=function(d){return this.bind(b,d)}});c.extend({get:function(a,b,d,f){if(c.isFunction(b)){f=f||d;d=b;b=null}return c.ajax({type:"GET",url:a,data:b,success:d,dataType:f})},getScript:function(a,b){return c.get(a,null,b,"script")},getJSON:function(a,b,d){return c.get(a,b,d,"json")},post:function(a,b,d,f){if(c.isFunction(b)){f=f||d;d=b;b={}}return c.ajax({type:"POST",url:a,data:b,success:d,dataType:f})},ajaxSetup:function(a){c.extend(c.ajaxSettings,a)},ajaxSettings:{url:location.href, 123 | global:true,type:"GET",contentType:"application/x-www-form-urlencoded",processData:true,async:true,xhr:A.XMLHttpRequest&&(A.location.protocol!=="file:"||!A.ActiveXObject)?function(){return new A.XMLHttpRequest}:function(){try{return new A.ActiveXObject("Microsoft.XMLHTTP")}catch(a){}},accepts:{xml:"application/xml, text/xml",html:"text/html",script:"text/javascript, application/javascript",json:"application/json, text/javascript",text:"text/plain",_default:"*/*"}},lastModified:{},etag:{},ajax:function(a){function b(){e.success&& 124 | e.success.call(k,o,i,x);e.global&&f("ajaxSuccess",[x,e])}function d(){e.complete&&e.complete.call(k,x,i);e.global&&f("ajaxComplete",[x,e]);e.global&&!--c.active&&c.event.trigger("ajaxStop")}function f(q,p){(e.context?c(e.context):c.event).trigger(q,p)}var e=c.extend(true,{},c.ajaxSettings,a),j,i,o,k=a&&a.context||e,n=e.type.toUpperCase();if(e.data&&e.processData&&typeof e.data!=="string")e.data=c.param(e.data,e.traditional);if(e.dataType==="jsonp"){if(n==="GET")N.test(e.url)||(e.url+=(ka.test(e.url)? 125 | "&":"?")+(e.jsonp||"callback")+"=?");else if(!e.data||!N.test(e.data))e.data=(e.data?e.data+"&":"")+(e.jsonp||"callback")+"=?";e.dataType="json"}if(e.dataType==="json"&&(e.data&&N.test(e.data)||N.test(e.url))){j=e.jsonpCallback||"jsonp"+sb++;if(e.data)e.data=(e.data+"").replace(N,"="+j+"$1");e.url=e.url.replace(N,"="+j+"$1");e.dataType="script";A[j]=A[j]||function(q){o=q;b();d();A[j]=w;try{delete A[j]}catch(p){}z&&z.removeChild(C)}}if(e.dataType==="script"&&e.cache===null)e.cache=false;if(e.cache=== 126 | false&&n==="GET"){var r=J(),u=e.url.replace(wb,"$1_="+r+"$2");e.url=u+(u===e.url?(ka.test(e.url)?"&":"?")+"_="+r:"")}if(e.data&&n==="GET")e.url+=(ka.test(e.url)?"&":"?")+e.data;e.global&&!c.active++&&c.event.trigger("ajaxStart");r=(r=xb.exec(e.url))&&(r[1]&&r[1]!==location.protocol||r[2]!==location.host);if(e.dataType==="script"&&n==="GET"&&r){var z=s.getElementsByTagName("head")[0]||s.documentElement,C=s.createElement("script");C.src=e.url;if(e.scriptCharset)C.charset=e.scriptCharset;if(!j){var B= 127 | false;C.onload=C.onreadystatechange=function(){if(!B&&(!this.readyState||this.readyState==="loaded"||this.readyState==="complete")){B=true;b();d();C.onload=C.onreadystatechange=null;z&&C.parentNode&&z.removeChild(C)}}}z.insertBefore(C,z.firstChild);return w}var E=false,x=e.xhr();if(x){e.username?x.open(n,e.url,e.async,e.username,e.password):x.open(n,e.url,e.async);try{if(e.data||a&&a.contentType)x.setRequestHeader("Content-Type",e.contentType);if(e.ifModified){c.lastModified[e.url]&&x.setRequestHeader("If-Modified-Since", 128 | c.lastModified[e.url]);c.etag[e.url]&&x.setRequestHeader("If-None-Match",c.etag[e.url])}r||x.setRequestHeader("X-Requested-With","XMLHttpRequest");x.setRequestHeader("Accept",e.dataType&&e.accepts[e.dataType]?e.accepts[e.dataType]+", */*":e.accepts._default)}catch(ga){}if(e.beforeSend&&e.beforeSend.call(k,x,e)===false){e.global&&!--c.active&&c.event.trigger("ajaxStop");x.abort();return false}e.global&&f("ajaxSend",[x,e]);var g=x.onreadystatechange=function(q){if(!x||x.readyState===0||q==="abort"){E|| 129 | d();E=true;if(x)x.onreadystatechange=c.noop}else if(!E&&x&&(x.readyState===4||q==="timeout")){E=true;x.onreadystatechange=c.noop;i=q==="timeout"?"timeout":!c.httpSuccess(x)?"error":e.ifModified&&c.httpNotModified(x,e.url)?"notmodified":"success";var p;if(i==="success")try{o=c.httpData(x,e.dataType,e)}catch(v){i="parsererror";p=v}if(i==="success"||i==="notmodified")j||b();else c.handleError(e,x,i,p);d();q==="timeout"&&x.abort();if(e.async)x=null}};try{var h=x.abort;x.abort=function(){x&&h.call(x); 130 | g("abort")}}catch(l){}e.async&&e.timeout>0&&setTimeout(function(){x&&!E&&g("timeout")},e.timeout);try{x.send(n==="POST"||n==="PUT"||n==="DELETE"?e.data:null)}catch(m){c.handleError(e,x,null,m);d()}e.async||g();return x}},handleError:function(a,b,d,f){if(a.error)a.error.call(a.context||a,b,d,f);if(a.global)(a.context?c(a.context):c.event).trigger("ajaxError",[b,a,f])},active:0,httpSuccess:function(a){try{return!a.status&&location.protocol==="file:"||a.status>=200&&a.status<300||a.status===304||a.status=== 131 | 1223||a.status===0}catch(b){}return false},httpNotModified:function(a,b){var d=a.getResponseHeader("Last-Modified"),f=a.getResponseHeader("Etag");if(d)c.lastModified[b]=d;if(f)c.etag[b]=f;return a.status===304||a.status===0},httpData:function(a,b,d){var f=a.getResponseHeader("content-type")||"",e=b==="xml"||!b&&f.indexOf("xml")>=0;a=e?a.responseXML:a.responseText;e&&a.documentElement.nodeName==="parsererror"&&c.error("parsererror");if(d&&d.dataFilter)a=d.dataFilter(a,b);if(typeof a==="string")if(b=== 132 | "json"||!b&&f.indexOf("json")>=0)a=c.parseJSON(a);else if(b==="script"||!b&&f.indexOf("javascript")>=0)c.globalEval(a);return a},param:function(a,b){function d(i,o){if(c.isArray(o))c.each(o,function(k,n){b||/\[\]$/.test(i)?f(i,n):d(i+"["+(typeof n==="object"||c.isArray(n)?k:"")+"]",n)});else!b&&o!=null&&typeof o==="object"?c.each(o,function(k,n){d(i+"["+k+"]",n)}):f(i,o)}function f(i,o){o=c.isFunction(o)?o():o;e[e.length]=encodeURIComponent(i)+"="+encodeURIComponent(o)}var e=[];if(b===w)b=c.ajaxSettings.traditional; 133 | if(c.isArray(a)||a.jquery)c.each(a,function(){f(this.name,this.value)});else for(var j in a)d(j,a[j]);return e.join("&").replace(yb,"+")}});var la={},Ab=/toggle|show|hide/,Bb=/^([+-]=)?([\d+-.]+)(.*)$/,W,va=[["height","marginTop","marginBottom","paddingTop","paddingBottom"],["width","marginLeft","marginRight","paddingLeft","paddingRight"],["opacity"]];c.fn.extend({show:function(a,b){if(a||a===0)return this.animate(K("show",3),a,b);else{a=0;for(b=this.length;a").appendTo("body");f=e.css("display");if(f==="none")f="block";e.remove();la[d]=f}c.data(this[a],"olddisplay",f)}}a=0;for(b=this.length;a=0;f--)if(d[f].elem===this){b&&d[f](true);d.splice(f,1)}});b||this.dequeue();return this}});c.each({slideDown:K("show",1),slideUp:K("hide",1),slideToggle:K("toggle",1),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"}},function(a,b){c.fn[a]=function(d,f){return this.animate(b,d,f)}});c.extend({speed:function(a,b,d){var f=a&&typeof a==="object"?a:{complete:d||!d&&b||c.isFunction(a)&&a,duration:a,easing:d&&b||b&&!c.isFunction(b)&&b};f.duration=c.fx.off?0:typeof f.duration=== 139 | "number"?f.duration:c.fx.speeds[f.duration]||c.fx.speeds._default;f.old=f.complete;f.complete=function(){f.queue!==false&&c(this).dequeue();c.isFunction(f.old)&&f.old.call(this)};return f},easing:{linear:function(a,b,d,f){return d+f*a},swing:function(a,b,d,f){return(-Math.cos(a*Math.PI)/2+0.5)*f+d}},timers:[],fx:function(a,b,d){this.options=b;this.elem=a;this.prop=d;if(!b.orig)b.orig={}}});c.fx.prototype={update:function(){this.options.step&&this.options.step.call(this.elem,this.now,this);(c.fx.step[this.prop]|| 140 | c.fx.step._default)(this);if((this.prop==="height"||this.prop==="width")&&this.elem.style)this.elem.style.display="block"},cur:function(a){if(this.elem[this.prop]!=null&&(!this.elem.style||this.elem.style[this.prop]==null))return this.elem[this.prop];return(a=parseFloat(c.css(this.elem,this.prop,a)))&&a>-10000?a:parseFloat(c.curCSS(this.elem,this.prop))||0},custom:function(a,b,d){function f(j){return e.step(j)}this.startTime=J();this.start=a;this.end=b;this.unit=d||this.unit||"px";this.now=this.start; 141 | this.pos=this.state=0;var e=this;f.elem=this.elem;if(f()&&c.timers.push(f)&&!W)W=setInterval(c.fx.tick,13)},show:function(){this.options.orig[this.prop]=c.style(this.elem,this.prop);this.options.show=true;this.custom(this.prop==="width"||this.prop==="height"?1:0,this.cur());c(this.elem).show()},hide:function(){this.options.orig[this.prop]=c.style(this.elem,this.prop);this.options.hide=true;this.custom(this.cur(),0)},step:function(a){var b=J(),d=true;if(a||b>=this.options.duration+this.startTime){this.now= 142 | this.end;this.pos=this.state=1;this.update();this.options.curAnim[this.prop]=true;for(var f in this.options.curAnim)if(this.options.curAnim[f]!==true)d=false;if(d){if(this.options.display!=null){this.elem.style.overflow=this.options.overflow;a=c.data(this.elem,"olddisplay");this.elem.style.display=a?a:this.options.display;if(c.css(this.elem,"display")==="none")this.elem.style.display="block"}this.options.hide&&c(this.elem).hide();if(this.options.hide||this.options.show)for(var e in this.options.curAnim)c.style(this.elem, 143 | e,this.options.orig[e]);this.options.complete.call(this.elem)}return false}else{e=b-this.startTime;this.state=e/this.options.duration;a=this.options.easing||(c.easing.swing?"swing":"linear");this.pos=c.easing[this.options.specialEasing&&this.options.specialEasing[this.prop]||a](this.state,e,0,1,this.options.duration);this.now=this.start+(this.end-this.start)*this.pos;this.update()}return true}};c.extend(c.fx,{tick:function(){for(var a=c.timers,b=0;b
"; 149 | a.insertBefore(b,a.firstChild);d=b.firstChild;f=d.firstChild;e=d.nextSibling.firstChild.firstChild;this.doesNotAddBorder=f.offsetTop!==5;this.doesAddBorderForTableAndCells=e.offsetTop===5;f.style.position="fixed";f.style.top="20px";this.supportsFixedPosition=f.offsetTop===20||f.offsetTop===15;f.style.position=f.style.top="";d.style.overflow="hidden";d.style.position="relative";this.subtractsBorderForOverflowNotVisible=f.offsetTop===-5;this.doesNotIncludeMarginInBodyOffset=a.offsetTop!==j;a.removeChild(b); 150 | c.offset.initialize=c.noop},bodyOffset:function(a){var b=a.offsetTop,d=a.offsetLeft;c.offset.initialize();if(c.offset.doesNotIncludeMarginInBodyOffset){b+=parseFloat(c.curCSS(a,"marginTop",true))||0;d+=parseFloat(c.curCSS(a,"marginLeft",true))||0}return{top:b,left:d}},setOffset:function(a,b,d){if(/static/.test(c.curCSS(a,"position")))a.style.position="relative";var f=c(a),e=f.offset(),j=parseInt(c.curCSS(a,"top",true),10)||0,i=parseInt(c.curCSS(a,"left",true),10)||0;if(c.isFunction(b))b=b.call(a, 151 | d,e);d={top:b.top-e.top+j,left:b.left-e.left+i};"using"in b?b.using.call(a,d):f.css(d)}};c.fn.extend({position:function(){if(!this[0])return null;var a=this[0],b=this.offsetParent(),d=this.offset(),f=/^body|html$/i.test(b[0].nodeName)?{top:0,left:0}:b.offset();d.top-=parseFloat(c.curCSS(a,"marginTop",true))||0;d.left-=parseFloat(c.curCSS(a,"marginLeft",true))||0;f.top+=parseFloat(c.curCSS(b[0],"borderTopWidth",true))||0;f.left+=parseFloat(c.curCSS(b[0],"borderLeftWidth",true))||0;return{top:d.top- 152 | f.top,left:d.left-f.left}},offsetParent:function(){return this.map(function(){for(var a=this.offsetParent||s.body;a&&!/^body|html$/i.test(a.nodeName)&&c.css(a,"position")==="static";)a=a.offsetParent;return a})}});c.each(["Left","Top"],function(a,b){var d="scroll"+b;c.fn[d]=function(f){var e=this[0],j;if(!e)return null;if(f!==w)return this.each(function(){if(j=wa(this))j.scrollTo(!a?f:c(j).scrollLeft(),a?f:c(j).scrollTop());else this[d]=f});else return(j=wa(e))?"pageXOffset"in j?j[a?"pageYOffset": 153 | "pageXOffset"]:c.support.boxModel&&j.document.documentElement[d]||j.document.body[d]:e[d]}});c.each(["Height","Width"],function(a,b){var d=b.toLowerCase();c.fn["inner"+b]=function(){return this[0]?c.css(this[0],d,false,"padding"):null};c.fn["outer"+b]=function(f){return this[0]?c.css(this[0],d,false,f?"margin":"border"):null};c.fn[d]=function(f){var e=this[0];if(!e)return f==null?null:this;if(c.isFunction(f))return this.each(function(j){var i=c(this);i[d](f.call(this,j,i[d]()))});return"scrollTo"in 154 | e&&e.document?e.document.compatMode==="CSS1Compat"&&e.document.documentElement["client"+b]||e.document.body["client"+b]:e.nodeType===9?Math.max(e.documentElement["client"+b],e.body["scroll"+b],e.documentElement["scroll"+b],e.body["offset"+b],e.documentElement["offset"+b]):f===w?c.css(e,d):this.css(d,typeof f==="string"?f:f+"px")}});A.jQuery=A.$=c})(window); 155 | -------------------------------------------------------------------------------- /todo.md: -------------------------------------------------------------------------------- 1 | #TODO 2 | 3 | [] Preserve IDs for undo/redo 4 | [] Implement common shortcut keys 5 | [] Reorganize editor code (tools, undo, copy/paste, selection) 6 | 7 | [] Outline styling 8 | [] Fill styling 9 | [] Color swatches 10 | 11 | [] Smart shapes (rect, oval/circle, round rect, triangle, polygon, etc) 12 | [] Text (opentype.js?) 13 | [] Group 14 | 15 | [] Arrange (Move to Front, Move to back, Move up 1, Move down 1) 16 | [] Align & distribute 17 | [] Transform (pivot, position, rotation, scale, mirror) 18 | [] Inspector bar 19 | 20 | [] Guides (horiz/vert, snap, sketchup style offset) 21 | [] Snap to grid 22 | [] Smart snap 23 | --------------------------------------------------------------------------------