├── css ├── annotorious.css ├── delete.png ├── feather_icon.png ├── pencil.png └── theme-dark │ ├── DarkSprite.png │ ├── Dividor.png │ ├── Indicator.png │ ├── annotorious-dark.css │ └── feather_icon.png ├── externs ├── api.externs.js ├── jquery.externs.js ├── okfn_annotator.externs.js ├── openlayers.externs.js └── openseadragon.externs.js ├── mit-license.txt ├── okfn_plugin.json ├── plovr ├── plovr-license-apache2.txt └── plovr.jar ├── readme.md ├── src ├── annotation.js ├── annotorious.js ├── api.js ├── dom.js ├── editor.js ├── events.js ├── events.ui.js ├── hint.js ├── mediatypes │ ├── annotator.js │ ├── image │ │ ├── image.annotator.js │ │ ├── image.module.js │ │ └── image.viewer.js │ ├── module.js │ ├── openlayers │ │ ├── openlayers.annotator.js │ │ ├── openlayers.module.js │ │ └── openlayers.viewer.js │ └── openseadragon │ │ ├── openseadragon.annotator.js │ │ ├── openseadragon.module.js │ │ └── openseadragon.viewer.js ├── okfn │ ├── okfn_image_plugin.js │ └── okfn_popup.js ├── popup.js ├── selection │ └── rect_drag_selector.js └── shape │ ├── point.js │ ├── polygon.js │ ├── rectangle.js │ └── shape.js ├── standalone.json ├── templates ├── core_elements.soy ├── image_elements.soy └── openlayers_elements.soy └── test ├── image ├── 371px-Claudius-Ptolemaeus.jpg ├── 630px-Ptolemaic-Map.jpg ├── 640px-Hallstatt.jpg ├── index.html └── qunit │ ├── image-test-suite.css │ ├── image-test-suite.html │ └── image-test-suite.js ├── jquery-1.10.2.min.js ├── jquery.simulate.js ├── okfn └── image_okfn.html ├── openlayers ├── OpenLayers.js ├── OpenLayers_LICENSE.txt ├── map_muenster │ ├── 0 │ │ └── 0 │ │ │ └── 0.jpg │ ├── 1 │ │ ├── 0 │ │ │ ├── 0.jpg │ │ │ └── 1.jpg │ │ └── 1 │ │ │ ├── 0.jpg │ │ │ └── 1.jpg │ ├── 2 │ │ ├── 0 │ │ │ ├── 0.jpg │ │ │ ├── 1.jpg │ │ │ └── 2.jpg │ │ ├── 1 │ │ │ ├── 0.jpg │ │ │ ├── 1.jpg │ │ │ └── 2.jpg │ │ ├── 2 │ │ │ ├── 0.jpg │ │ │ ├── 1.jpg │ │ │ └── 2.jpg │ │ └── 3 │ │ │ ├── 0.jpg │ │ │ ├── 1.jpg │ │ │ └── 2.jpg │ └── tilemapresource.xml ├── openlayers_fullscreen.html ├── openlayers_standalone.html └── theme │ └── default │ ├── google.css │ ├── google.tidy.css │ ├── ie6-style.css │ ├── ie6-style.tidy.css │ ├── img │ ├── add_point_off.png │ ├── add_point_on.png │ ├── blank.gif │ ├── close.gif │ ├── drag-rectangle-off.png │ ├── drag-rectangle-on.png │ ├── draw_line_off.png │ ├── draw_line_on.png │ ├── draw_point_off.png │ ├── draw_point_on.png │ ├── draw_polygon_off.png │ ├── draw_polygon_on.png │ ├── editing_tool_bar.png │ ├── move_feature_off.png │ ├── move_feature_on.png │ ├── navigation_history.png │ ├── overview_replacement.gif │ ├── pan-panel-NOALPHA.png │ ├── pan-panel.png │ ├── pan_off.png │ ├── pan_on.png │ ├── panning-hand-off.png │ ├── panning-hand-on.png │ ├── remove_point_off.png │ ├── remove_point_on.png │ ├── ruler.png │ ├── save_features_off.png │ ├── save_features_on.png │ ├── view_next_off.png │ ├── view_next_on.png │ ├── view_previous_off.png │ ├── view_previous_on.png │ ├── zoom-panel-NOALPHA.png │ └── zoom-panel.png │ ├── style.css │ ├── style.mobile.css │ ├── style.mobile.tidy.css │ └── style.tidy.css ├── openseadragon ├── 2003rosen1799 │ ├── 0001q.jpg │ ├── 0001r.jpg │ └── 0001v.jpg ├── openseadragon │ ├── images │ │ ├── fullpage_grouphover.png │ │ ├── fullpage_hover.png │ │ ├── fullpage_pressed.png │ │ ├── fullpage_rest.png │ │ ├── home_grouphover.png │ │ ├── home_hover.png │ │ ├── home_pressed.png │ │ ├── home_rest.png │ │ ├── next_grouphover.png │ │ ├── next_hover.png │ │ ├── next_pressed.png │ │ ├── next_rest.png │ │ ├── previous_grouphover.png │ │ ├── previous_hover.png │ │ ├── previous_pressed.png │ │ ├── previous_rest.png │ │ ├── zoomin_grouphover.png │ │ ├── zoomin_hover.png │ │ ├── zoomin_pressed.png │ │ ├── zoomin_rest.png │ │ ├── zoomout_grouphover.png │ │ ├── zoomout_hover.png │ │ ├── zoomout_pressed.png │ │ └── zoomout_rest.png │ └── openseadragon.min.js └── test.html ├── plugins └── plugin-hello-world.html ├── qunit-1.12.0.css └── qunit-1.12.0.js /css/annotorious.css: -------------------------------------------------------------------------------- 1 | /** Global item styles **/ 2 | 3 | .annotorious-opacity-fade { 4 | -moz-transition-property: opacity; 5 | -moz-transition-duration: 0.5s; 6 | -moz-transition-delay: 0s; 7 | -webkit-transition-property: opacity; 8 | -webkit-transition-duration: 0.5s; 9 | -webkit-transition-delay: 0s; 10 | -o-transition-property: opacity; 11 | -o-transition-duration: 0.5s; 12 | -o-transition-delay: 0s; 13 | transition-property: opacity; 14 | transition-duration: 0.5s; 15 | transition-delay: 0s; 16 | } 17 | 18 | .annotorious-item-focus { 19 | opacity:1.0; 20 | } 21 | 22 | .annotorious-item-unfocus { 23 | opacity:0.4; 24 | } 25 | 26 | /** Hint/help popup **/ 27 | 28 | .annotorious-hint-msg { 29 | background-color:rgba(0,0,0,0.5); 30 | margin:4px; 31 | padding:8px 15px 8px 30px; 32 | font-family: 'lucida grande',tahoma,verdana,arial,sans-serif; 33 | line-height: normal; 34 | font-size:12px; 35 | color:#fff; 36 | border-radius:4px; 37 | -moz-border-radius:4px; 38 | -webkit-border-radius:4px; 39 | -khtml-border-radius:4px; 40 | } 41 | 42 | .annotorious-hint-icon { 43 | position:absolute; 44 | top:6px; 45 | left: 5px; 46 | background:url('feather_icon.png'); 47 | background-repeat:no-repeat; 48 | width:19px; 49 | height:22px; 50 | margin:2px 4px 0px 6px; 51 | } 52 | 53 | /** Popup **/ 54 | 55 | .annotorious-popup { 56 | line-height:135%; 57 | font-family:Arial, Verdana, Sans; 58 | font-size:12px; 59 | color:#000; 60 | background-color:#fff; 61 | border:1px solid #ccc; 62 | padding:9px 8px; 63 | word-wrap:break-word; 64 | width:180px; 65 | border-radius: 3px; 66 | -moz-border-radius: 3px; 67 | -webkit-border-radius: 3px; 68 | -khtml-border-radius: 3px; 69 | -moz-box-shadow:0px 5px 15px #111; 70 | -webkit-box-shadow:0px 5px 15px #111; 71 | box-shadow:0px 5px 15px #111; 72 | 73 | -moz-transition-property: opacity; 74 | -moz-transition-duration: 0.5s; 75 | -moz-transition-delay: 0s; 76 | -webkit-transition-property: opacity; 77 | -webkit-transition-duration: 0.5s; 78 | -webkit-transition-delay: 0s; 79 | -o-transition-property: opacity; 80 | -o-transition-duration: 0.5s; 81 | -o-transition-delay: 0s; 82 | transition-property: opacity; 83 | transition-duration: 0.5s; 84 | transition-delay: 0s; 85 | } 86 | 87 | .annotorious-popup-empty { 88 | color:#999; 89 | font-style:italic; 90 | } 91 | 92 | .annotorious-popup-buttons { 93 | float:right; 94 | margin:0px 0px 1px 10px; 95 | height:16px; 96 | 97 | -moz-transition-property: opacity; 98 | -moz-transition-duration: 1s; 99 | -moz-transition-delay: 0s; 100 | -webkit-transition-property: opacity; 101 | -webkit-transition-duration: 1s; 102 | -webkit-transition-delay: 0s; 103 | -o-transition-property: opacity; 104 | -o-transition-duration: 1s; 105 | -o-transition-delay: 0s; 106 | transition-property: opacity; 107 | transition-duration: 1s; 108 | transition-delay: 0s; 109 | } 110 | 111 | .annotorious-popup-button { 112 | font-size:10px; 113 | text-decoration:none; 114 | display:inline-block; 115 | color:#000; 116 | font-weight:bold; 117 | margin-left:5px; 118 | opacity:0.4; 119 | 120 | -moz-transition-property: opacity; 121 | -moz-transition-duration: 0.5s; 122 | -moz-transition-delay: 0s; 123 | -webkit-transition-property: opacity; 124 | -webkit-transition-duration: 0.5s; 125 | -webkit-transition-delay: 0s; 126 | -o-transition-property: opacity; 127 | -o-transition-duration: 0.5s; 128 | -o-transition-delay: 0s; 129 | transition-property: opacity; 130 | transition-duration: 0.5s; 131 | transition-delay: 0s; 132 | } 133 | 134 | .annotorious-popup-button:hover { 135 | background-color:transparent; 136 | } 137 | 138 | .annotorious-popup-button-active { 139 | opacity:0.9; 140 | } 141 | 142 | .annotorious-popup-button-edit { 143 | background:url(pencil.png); 144 | width:16px; 145 | height:16px; 146 | text-indent:100px; 147 | overflow:hidden; 148 | } 149 | 150 | .annotorious-popup-button-delete { 151 | background:url(delete.png); 152 | width:16px; 153 | height:16px; 154 | text-indent:100px; 155 | overflow:hidden; 156 | float:right; 157 | } 158 | 159 | .annotorious-popup-field { 160 | border-top:1px solid #ccc; 161 | margin:6px 0px 0px 0px; 162 | padding-top:2px; 163 | } 164 | 165 | /** Editor **/ 166 | 167 | .annotorious-editor { 168 | line-height: normal; 169 | padding:0px 0px 2px 0px; 170 | background-color:#f2f2f2; 171 | color:#000; 172 | opacity:0.97; 173 | border:1px solid #ccc; 174 | border-radius: 3px; 175 | -moz-border-radius: 3px; 176 | -webkit-border-radius: 3px; 177 | -khtml-border-radius: 3px; 178 | -moz-box-shadow:0px 5px 15px #111; 179 | -webkit-box-shadow:0px 5px 15px #111; 180 | box-shadow:0px 5px 15px #111; 181 | } 182 | 183 | .annotorious-editor-text { 184 | border-width:0px 0px 1px 0px; 185 | border-style:solid; 186 | border-color:#ccc; 187 | line-height: normal; 188 | background-color:#fff; 189 | width:240px; 190 | height:50px; 191 | outline:none; 192 | font-family:Verdana, Arial; 193 | font-size:11px; 194 | padding:4px; 195 | margin:0px; 196 | color:#000; 197 | text-shadow:none; 198 | overflow-y:auto; 199 | display:block; 200 | } 201 | 202 | .annotorious-editor-button-container { 203 | padding-top:2px; 204 | } 205 | 206 | .annotorious-editor-button { 207 | float:right; 208 | line-height: normal; 209 | display:inline-block; 210 | text-align:center; 211 | text-decoration:none; 212 | font-family:Verdana, Arial; 213 | font-size:10px; 214 | border:1px solid #777; 215 | color:#ddd; 216 | padding:3px 8px; 217 | margin:1px 2px 0px 1px; 218 | cursor:pointer; 219 | cursor:hand; 220 | background:-webkit-gradient(linear, left top, left bottom, from(#888), to(#555)); 221 | background:-moz-linear-gradient(top, #888, #555); 222 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#888888', endColorstr='#555555'); 223 | -moz-border-radius:2px; 224 | -webkit-border-radius:2px; 225 | -khtml-border-radius:2px; 226 | border-radius:2px; 227 | } 228 | 229 | .annotorious-editor-button:hover { 230 | background:#999; 231 | } 232 | 233 | .annotorious-editor-field { 234 | border-bottom:1px solid #ccc; 235 | margin:0px; 236 | background-color:#fff; 237 | padding:3px; 238 | font-family:Verdana, Arial; 239 | font-size:12px; 240 | } 241 | 242 | /** OpenLayers module **/ 243 | .annotorious-ol-boxmarker-outer { 244 | border:1px solid #000; 245 | } 246 | 247 | .annotorious-ol-boxmarker-inner { 248 | border:1px solid #fff; 249 | -webkit-box-sizing: border-box; 250 | -moz-box-sizing: border-box; 251 | -ms-box-sizing: border-box; 252 | box-sizing: border-box; 253 | } 254 | 255 | .annotorious-ol-hint { 256 | line-height: normal; 257 | font-family:Arial, Verdana, Sans; 258 | font-size:16px; 259 | color:#000; 260 | background-color:#fff; 261 | margin:0px; 262 | padding:9px; 263 | border-radius: 5px; 264 | -moz-border-radius: 5px; 265 | -webkit-border-radius: 5px; 266 | -khtml-border-radius: 5px; 267 | } 268 | 269 | .annotorious-ol-hint-secondary { 270 | background-color:#fff000; 271 | } 272 | 273 | canvas { 274 | z-index: 2; 275 | } 276 | 277 | canvas.hidden { 278 | z-index: -1; 279 | visibility: hidden; 280 | } 281 | 282 | html.hasTouch .annotator-viewer li .annotator-controls, 283 | html.hasTouch .annotator-viewer li .annotator-controls { 284 | opacity: 1; 285 | } -------------------------------------------------------------------------------- /css/delete.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/annotorious/annotorious-v1/3fb5a931428a3bc9e3f48bc905ab042a393d0907/css/delete.png -------------------------------------------------------------------------------- /css/feather_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/annotorious/annotorious-v1/3fb5a931428a3bc9e3f48bc905ab042a393d0907/css/feather_icon.png -------------------------------------------------------------------------------- /css/pencil.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/annotorious/annotorious-v1/3fb5a931428a3bc9e3f48bc905ab042a393d0907/css/pencil.png -------------------------------------------------------------------------------- /css/theme-dark/DarkSprite.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/annotorious/annotorious-v1/3fb5a931428a3bc9e3f48bc905ab042a393d0907/css/theme-dark/DarkSprite.png -------------------------------------------------------------------------------- /css/theme-dark/Dividor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/annotorious/annotorious-v1/3fb5a931428a3bc9e3f48bc905ab042a393d0907/css/theme-dark/Dividor.png -------------------------------------------------------------------------------- /css/theme-dark/Indicator.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/annotorious/annotorious-v1/3fb5a931428a3bc9e3f48bc905ab042a393d0907/css/theme-dark/Indicator.png -------------------------------------------------------------------------------- /css/theme-dark/annotorious-dark.css: -------------------------------------------------------------------------------- 1 | .annotorious-hint-msg { 2 | background-color:rgba(0,0,0,0.6); 3 | margin:4px; 4 | padding:8px 15px 8px 30px; 5 | font-family:'lucida grande',tahoma,verdana,arial,sans-serif; 6 | line-height:normal; 7 | font-size:12px; 8 | color:#fff; 9 | border-radius:4px; 10 | -moz-border-radius:4px; 11 | -webkit-border-radius:4px; 12 | -khtml-border-radius:4px; 13 | } 14 | 15 | .annotorious-hint-icon { 16 | position:absolute; 17 | top:6px; 18 | left:5px; 19 | background:url(feather_icon.png); 20 | background-repeat:no-repeat; 21 | width:19px; 22 | height:22px; 23 | margin:2px 4px 0 6px; 24 | } 25 | 26 | .annotorious-opacity-fade { 27 | -moz-transition-property:opacity; 28 | -moz-transition-duration:.5s; 29 | -moz-transition-delay:0; 30 | -webkit-transition-property:opacity; 31 | -webkit-transition-duration:.5s; 32 | -webkit-transition-delay:0; 33 | -o-transition-property:opacity; 34 | -o-transition-duration:.5s; 35 | -o-transition-delay:0; 36 | transition-property:opacity; 37 | transition-duration:.5s; 38 | transition-delay:0; 39 | } 40 | 41 | .annotorious-item-focus { 42 | opacity:1.0; 43 | } 44 | 45 | .annotorious-item-unfocus { 46 | opacity:0.4; 47 | } 48 | 49 | .annotorious-popup { 50 | position:absolute; 51 | top:0; 52 | left:0; 53 | width:220px; 54 | min-height:26px; 55 | margin-top:12px; 56 | background-color:#383838; 57 | border:1px solid #000; 58 | outline:none; 59 | padding:5px; 60 | -moz-transition-property:opacity; 61 | -moz-transition-duration:.5s; 62 | -moz-transition-delay:0; 63 | -ms-transition-property:opacity; 64 | -ms-transition-duration:.5s; 65 | -ms-transition-delay:0; 66 | -webkit-transition-property:opacity; 67 | -webkit-transition-duration:.5s; 68 | -webkit-transition-delay:0; 69 | -o-transition-property:opacity; 70 | -o-transition-duration:.5s; 71 | -o-transition-delay:0; 72 | transition-property:opacity; 73 | transition-duration:.5s; 74 | transition-delay:0; 75 | -moz-border-radius:8px; 76 | -webkit-border-radius:8px; 77 | -khtml-border-radius:8px; 78 | border-radius:8px; 79 | -o-box-shadow:0 5px 5px rgba(0,0,0,0.7),inset 0 1px 1px rgba(255,255,255,0.25); 80 | -ms-box-shadow:0 5px 5px rgba(0,0,0,0.7),inset 0 1px 1px rgba(255,255,255,0.25); 81 | -moz-box-shadow:0 5px 5px rgba(0,0,0,0.7),inset 0 1px 1px rgba(255,255,255,0.25); 82 | -webkit-box-shadow:0 5px 5px rgba(0,0,0,0.7),inset 0 1px 1px rgba(255,255,255,0.25); 83 | box-shadow:0 5px 53px rgba(0,0,0,0.7),inset 0 1px 1px rgba(255,255,255,0.25); 84 | } 85 | 86 | .annotorious-popup-text { 87 | display:block; 88 | padding:5px; 89 | font-family:Verdana,Arial; 90 | font-size:12px; 91 | color:#eee; 92 | text-shadow:none; 93 | line-height:150%; 94 | } 95 | 96 | .annotorious-popup-text a { 97 | text-decoration:underline; 98 | color:#eee; 99 | background-color:transparent; 100 | } 101 | 102 | .top-left:after, .annotorious-editor:after { 103 | content:url(Indicator.png); 104 | position:absolute; 105 | left:15px; 106 | top:-14px; 107 | height:18px; 108 | width:19px; 109 | display:block; 110 | } 111 | 112 | .top-right:after { 113 | content:url(Indicator.png); 114 | position:absolute; 115 | right:15px; 116 | top:-14px; 117 | height:18px; 118 | width:19px; 119 | display:block; 120 | } 121 | 122 | 123 | .annotorious-popup-buttons { 124 | float:right; 125 | height:26px; 126 | width:56px; 127 | display:block; 128 | white-space:nowrap; 129 | padding-left:4px; 130 | -moz-transition-property:opacity; 131 | -moz-transition-duration:1s; 132 | -moz-transition-delay:0; 133 | -webkit-transition-property:opacity; 134 | -webkit-transition-duration:1s; 135 | -webkit-transition-delay:0; 136 | -o-transition-property:opacity; 137 | -o-transition-duration:1s; 138 | -o-transition-delay:0; 139 | transition-property:opacity; 140 | transition-duration:1s; 141 | transition-delay:0; 142 | } 143 | 144 | .annotorious-popup-button { 145 | font-size:10px; 146 | text-decoration:none; 147 | display:inline-block; 148 | color:#000; 149 | font-weight:700; 150 | width:26px; 151 | height:26px; 152 | text-indent:100%; 153 | white-space:nowrap; 154 | overflow:hidden; 155 | -moz-transition-property:opacity; 156 | -moz-transition-duration:1s; 157 | -moz-transition-delay:0; 158 | -webkit-transition-property:opacity; 159 | -webkit-transition-duration:1s; 160 | -webkit-transition-delay:0; 161 | -ms-transition-property:opacity; 162 | -ms-transition-duration:1s; 163 | -ms-transition-delay:0; 164 | -o-transition-property:opacity; 165 | -o-transition-duration:1s; 166 | -o-transition-delay:0; 167 | transition-property:opacity; 168 | transition-duration:1s; 169 | transition-delay:0; 170 | } 171 | 172 | .annotorious-popup-button-delete:hover { 173 | background-color:transparent; 174 | background:url(DarkSprite.png) no-repeat; 175 | background-position:0 -40px; 176 | } 177 | 178 | .annotorious-popup-button-delete { 179 | background:url(DarkSprite.png) no-repeat; 180 | background-position:0 -8px; 181 | outline:none; 182 | } 183 | 184 | .annotorious-popup-button-edit { 185 | background:url(DarkSprite.png) no-repeat; 186 | background-position:0 -70px; 187 | outline:none; 188 | } 189 | 190 | .annotorious-popup-button-edit:hover { 191 | background-color:transparent; 192 | background:url(DarkSprite.png) no-repeat; 193 | background-position:0 -99px; 194 | } 195 | 196 | .annotorious-popup-field { 197 | margin:0px; 198 | padding:6px; 199 | font-family:'lucida grande',tahoma,verdana,arial,sans-serif; 200 | font-size:12px; 201 | } 202 | 203 | .annotorious-editor { 204 | position:absolute; 205 | top:0; 206 | left:0; 207 | margin-top:12px; 208 | background-color:#383838; 209 | color:#F2F2F2; 210 | border:1px solid #000; 211 | border-radius:8px; 212 | -o-border-radius:8px; 213 | -moz-border-radius:8px; 214 | -webkit-border-radius:8px; 215 | -khtml-border-radius:8px; 216 | -o-box-shadow:0 5px 5px rgba(0,0,0,0.7),inset 0 1px 1px rgba(255,255,255,0.25); 217 | -ms-box-shadow:0 5px 5px rgba(0,0,0,0.7),inset 0 1px 1px rgba(255,255,255,0.25); 218 | -moz-box-shadow:0 5px 5px rgba(0,0,0,0.7),inset 0 1px 1px rgba(255,255,255,0.25); 219 | -webkit-box-shadow:0 5px 5px rgba(0,0,0,0.7),inset 0 1px 1px rgba(255,255,255,0.25); 220 | box-shadow:0 5px 53px rgba(0,0,0,0.7),inset 0 1px 1px rgba(255,255,255,0.25); 221 | z-index:2; 222 | } 223 | 224 | .annotorious-editor-button-container { 225 | background:url(DarkSprite.png); 226 | background-position:0 -1px; 227 | margin:0 10px 10px; 228 | background-repeat:repeat-x; 229 | height:2px; 230 | display:block; 231 | } 232 | 233 | .annotorious-editor-text { 234 | border:none; 235 | background-color:#383838; 236 | line-height:150%; 237 | margin:10px 0px 4px 0px; 238 | padding:0px 10px; 239 | min-height:50px; 240 | width:100%; 241 | min-width:200px; 242 | outline:none; 243 | font-family:Verdana,Arial; 244 | font-weight:400; 245 | font-size:12px; 246 | color:#eee; 247 | text-shadow:none; 248 | overflow-y:auto; 249 | display:block; 250 | resize:none; 251 | -moz-box-shadow:none !important; 252 | -webkit-box-shadow:none !important; 253 | box-shadow:none !important; 254 | } 255 | 256 | .annotorious-editor-text a:hover { 257 | color:#eee; 258 | background-color:transparent; 259 | } 260 | 261 | .annotorious-editor-button { 262 | float:right; 263 | line-height:normal; 264 | display:inline-block; 265 | text-align:center; 266 | text-decoration:none; 267 | font-family:Verdana,Arial; 268 | font-size:.625em; 269 | border:1px solid #000; 270 | color:#f2f2f2; 271 | padding-top:5px; 272 | padding-bottom:5px; 273 | margin:7px 2px 10px 0px; 274 | cursor:pointer; 275 | width:60px; 276 | -moz-box-shadow:inset 0 1px 1px rgba(255,255,255,0.25),0 1px 1px rgba(255,255,255,0.25); 277 | -webkit-box-shadow:inset 0 1px 1px rgba(255,255,255,0.25),0 1px 1px rgba(255,255,255,0.25); 278 | box-shadow:inset 0 1px 1px rgba(255,255,255,0.25),0 1px 1px rgba(255,255,255,0.25); 279 | -moz-border-radius:3px; 280 | -webkit-border-radius:3px; 281 | -khtml-border-radius:3px; 282 | border-radius:3px; 283 | opacity:1; 284 | } 285 | 286 | .annotorious-editor-button-save { 287 | margin-left:5px; 288 | background:-webkit-gradient(linear,left top,left bottom,from(#FFA52C),to(#FB7B28)); 289 | background:-moz-linear-gradient(top,#FFA52C,#FB7B28); 290 | filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#FFA52C',endColorstr='#FB7B28'); 291 | } 292 | 293 | .annotorious-editor-button-cancel { 294 | background:-webkit-gradient(linear,left top,left bottom,from(#656565),to(#2C2C2C)); 295 | background:-moz-linear-gradient(top,#656565,#2C2C2C); 296 | filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#656565',endColorstr='#2C2C2C'); 297 | } 298 | 299 | .annotorious-editor-button:hover { 300 | color:rgba(242,2423,242,1); 301 | text-shadow:0 0 6px rgba(242,242,242,0.6); 302 | -o-text-shadow:0 0 6px rgba(242,242,242,0.6); 303 | -moz-text-shadow:0 0 6px rgba(242,242,242,0.6); 304 | -webkit-text-shadow:0 0 6px rgba(242,242,242,0.6); 305 | } 306 | 307 | .annotorious-editor-button:active { 308 | -moz-box-shadow:inset 0 3px 3px rgba(0,0,0,0.7),0 1px 1px rgba(255,255,255,0.25); 309 | -webkit-box-shadow:inset 0 3px 3px rgba(0,0,0,0.7),0 1px 1px rgba(255,255,255,0.25); 310 | box-shadow:inset 0 3px 3px rgba(0,0,0,0.7),0 1px 1px rgba(255,255,255,0.25); 311 | } 312 | 313 | .annotorious-editor-field { 314 | margin:0px; 315 | padding:6px 0px; 316 | font-family:'lucida grande',tahoma,verdana,arial,sans-serif; 317 | font-size:12px; 318 | } 319 | 320 | /** OpenLayers module **/ 321 | .annotorious-ol-boxmarker-outer { 322 | border:1px solid #000; 323 | } 324 | 325 | .annotorious-ol-boxmarker-inner { 326 | border:1px solid #fff; 327 | -webkit-box-sizing: border-box; 328 | -moz-box-sizing: border-box; 329 | -ms-box-sizing: border-box; 330 | box-sizing: border-box; 331 | } 332 | 333 | .annotorious-ol-hint { 334 | line-height: normal; 335 | font-family:Arial, Verdana, Sans; 336 | font-size:16px; 337 | color:#fff; 338 | background-color:rgba(0,0,0,0.7); 339 | margin:0px; 340 | padding:9px; 341 | border-radius: 5px; 342 | -moz-border-radius: 5px; 343 | -webkit-border-radius: 5px; 344 | -khtml-border-radius: 5px; 345 | } 346 | -------------------------------------------------------------------------------- /css/theme-dark/feather_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/annotorious/annotorious-v1/3fb5a931428a3bc9e3f48bc905ab042a393d0907/css/theme-dark/feather_icon.png -------------------------------------------------------------------------------- /externs/api.externs.js: -------------------------------------------------------------------------------- 1 | /** Annotorious API Interface declarations **/ 2 | 3 | /** 4 | * Annotorious annotation interface. 5 | */ 6 | var Annotation = { 7 | 8 | /** @type {string} source URL of the annotated object (e.g. image) **/ 9 | src : {}, 10 | 11 | /** @type {string} source URL of the HTML document containing the annotated object **/ 12 | context : {}, 13 | 14 | /** @type {string} annotation text **/ 15 | text : {}, 16 | 17 | /** @type {boolean} flag indicating whether the anntotation is edit-/deletable **/ 18 | editable : {}, 19 | 20 | /** @type {Object} the annotation shape **/ 21 | shapes : [{ 22 | 23 | /** @type {string} the annotation shape type (e.g. rect, point, polygon) **/ 24 | type : {}, 25 | 26 | /** @type {string} measurement units used for the geometry (e.g. 'pixel', 'fraction') **/ 27 | units : {}, 28 | 29 | /** @type {Object} the shape geometry **/ 30 | geometry : {}, 31 | 32 | /** @type {Object} the shape style **/ 33 | style: { 34 | 35 | /** @type {string} outline color **/ 36 | outline: {}, 37 | 38 | /** @type {number} outline width **/ 39 | outline_width: {}, 40 | 41 | /** @type {string} outline color when highlighted **/ 42 | hi_outline: {}, 43 | 44 | /** @type {number} outline width when hightlighted **/ 45 | hi_outline_width: {}, 46 | 47 | /** @type {string} stroke color **/ 48 | stroke: {}, 49 | 50 | /** @type {number} stroke width **/ 51 | stroke_width: {}, 52 | 53 | /** @type {string} stroke color when highlighted **/ 54 | hi_stroke: {}, 55 | 56 | /** @type {number} stroke width when highlighted **/ 57 | hi_stroke_width: {}, 58 | 59 | /** @type {string} fill color **/ 60 | fill: {}, 61 | 62 | /** @type {string} fill color when highlighted **/ 63 | hi_fill: {} 64 | 65 | } 66 | 67 | }] 68 | 69 | }; 70 | 71 | /** 72 | * Annotation shape type: Rectangle 73 | */ 74 | var Rectangle = { 75 | 76 | x : {}, 77 | 78 | y : {}, 79 | 80 | width : {}, 81 | 82 | height : {} 83 | 84 | } 85 | 86 | /** 87 | * Annotation shape type: Polygon 88 | */ 89 | var Polygon = { 90 | 91 | points : {} 92 | 93 | } 94 | 95 | /** 96 | * Annotorious Plugin interface. 97 | */ 98 | var AnnotoriousPlugin = { 99 | 100 | /** @type {Function} called on plugin initialization **/ 101 | initPlugin : function(anno) {}, 102 | 103 | /** @type {Function} called on initialization of a Popup element **/ 104 | onInitAnnotator : function(annotator) {} 105 | 106 | }; 107 | 108 | /** 109 | * Annotator interface 110 | */ 111 | var Annotator = { 112 | 113 | /** @type {Element} the annotator DOM element **/ 114 | element : {}, 115 | 116 | /** @type {Object} the popup used by this annotator **/ 117 | popup : {}, 118 | 119 | /** @type {Object} the editor used by this annotator **/ 120 | editor : {} 121 | 122 | }; 123 | 124 | /** 125 | * Selector interface 126 | */ 127 | var Selector = { 128 | 129 | init : function() {}, 130 | 131 | getName : function() {}, 132 | 133 | getSupportedShapeType : function() {}, 134 | 135 | startSelection : function() {}, 136 | 137 | stopSelection : function() {}, 138 | 139 | getShape : function() {}, 140 | 141 | getViewportBounds : function() {}, 142 | 143 | drawShape : function() {} 144 | 145 | } 146 | 147 | /** 148 | * Selection event 149 | */ 150 | var SelectionEvent = { 151 | 152 | mouseEvent : {}, 153 | 154 | shape : {}, 155 | 156 | viewportBounds : {} 157 | 158 | } 159 | 160 | /** 161 | * Popup API 162 | */ 163 | var Popup = { 164 | 165 | startHideTimer : function() {}, 166 | 167 | clearHideTimer : function() {}, 168 | 169 | show : function() {}, 170 | 171 | setPosition : function() {}, 172 | 173 | setAnnotation : function() {} 174 | 175 | } -------------------------------------------------------------------------------- /externs/jquery.externs.js: -------------------------------------------------------------------------------- 1 | function $() {} 2 | -------------------------------------------------------------------------------- /externs/okfn_annotator.externs.js: -------------------------------------------------------------------------------- 1 | /** jQuery seems to cause trouble with Closure "occasionally" (really can't see a distinct pattern yet...) **/ 2 | var $ = {} 3 | 4 | /** 5 | * Externs definition for the OKFN Annotator class. 6 | */ 7 | var OKFNAnnotator = { 8 | 9 | /** Publish an event **/ 10 | publish : function(type, event) {}, 11 | 12 | /** Subscribe to an event **/ 13 | subscribe : function(type, handler) {}, 14 | 15 | /** Show the viewer popup **/ 16 | showViewer : function(annotation, position) {}, 17 | 18 | /** Start the viewer hide timer **/ 19 | startViewerHideTimer : function() {}, 20 | 21 | /** Clear/stop the viewer hide timer **/ 22 | clearViewerHideTimer : function() {}, 23 | 24 | /** Viewer object **/ 25 | viewer : { 26 | element : {}, 27 | annotations : [], 28 | load : function(annotations) {}, 29 | on : function() {} 30 | }, 31 | 32 | /** Show the editor widget **/ 33 | showEditor : function(annotation, position) {}, 34 | 35 | /** Editor object **/ 36 | editor : { 37 | element : {}, 38 | annotation : {}, 39 | show : function() {} 40 | } 41 | 42 | } 43 | 44 | -------------------------------------------------------------------------------- /externs/openlayers.externs.js: -------------------------------------------------------------------------------- 1 | /** 2 | * OpenLayers externs definition. 3 | */ 4 | var OpenLayers = { 5 | 6 | /** 7 | * Bounds class 8 | * @constructor 9 | */ 10 | Bounds: function(left, bottom, right, top) {}, 11 | 12 | /** Layer namespace **/ 13 | Layer : { 14 | 15 | /** Boxes layer type **/ 16 | Boxes : { 17 | addMarker : function() {}, 18 | removeMarker : function() {}, 19 | destroy : function() {} 20 | } 21 | }, 22 | 23 | /** LonLat class **/ 24 | LonLat : { 25 | lon : {}, 26 | lat : {} 27 | }, 28 | 29 | /** Map class **/ 30 | Map : { 31 | div : {}, 32 | events : { 33 | register : function() {} 34 | }, 35 | addLayer : function(layer) {}, 36 | getViewPortPxFromLonLat : function() {}, 37 | getLonLatFromPixel : function() {} 38 | }, 39 | 40 | /** Marker namespace **/ 41 | Marker : { 42 | 43 | /** Box marker type **/ 44 | Box : { 45 | div : {} 46 | } 47 | }, 48 | 49 | /** 50 | * Pixel class 51 | * @constructor 52 | */ 53 | Pixel: function(x ,y) {} 54 | 55 | } 56 | -------------------------------------------------------------------------------- /externs/openseadragon.externs.js: -------------------------------------------------------------------------------- 1 | /** 2 | * OpenSeadragon externs definition. 3 | */ 4 | var OpenSeadragon = { 5 | 6 | /** Adds an event handler to OpenSeadragon **/ 7 | addHandler: function() {}, 8 | 9 | /** The drawer **/ 10 | drawer: { 11 | 12 | /** Adds an overlay **/ 13 | addOverlay: function() {}, 14 | 15 | /** Removes overlay **/ 16 | removeOverlay: function() {} 17 | 18 | }, 19 | 20 | /** The DOM element OpenSeadragon attaches to **/ 21 | element: {}, 22 | 23 | /** OpenSeadragon.Point class **/ 24 | Point: function() {}, 25 | 26 | /** OpenSeadragon.Rect class **/ 27 | Rect: function() {}, 28 | 29 | /** The viewport **/ 30 | viewport: { 31 | 32 | /** Function to convert from browser window to OpenSeadragon coordinates **/ 33 | windowToViewportCoordinates: function() {}, 34 | 35 | /** Function to convert from OpenSeadragon to browser window coordinates **/ 36 | viewportToWindowCoordinates: function() {} 37 | 38 | }, 39 | 40 | viewer: { 41 | 42 | isFullPage: function() {} 43 | 44 | } 45 | 46 | } 47 | 48 | -------------------------------------------------------------------------------- /mit-license.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /okfn_plugin.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "annotorious-okfn-plugin", 3 | "define": { 4 | "goog.DEBUG": false, 5 | "goog.dom.ASSUME_STANDARDS_MODE": true 6 | }, 7 | "inputs": [ 8 | "src/shape/point.js", 9 | "src/shape/polygon.js", 10 | "src/shape/rectangle.js", 11 | "src/shape/shape.js", 12 | "src/mediatypes/image/image.annotator.js", 13 | "src/mediatypes/image/image.viewer.js", 14 | "src/okfn/okfn_image_plugin.js", 15 | "src/okfn/okfn_popup.js", 16 | "src/selection/rect_drag_selector.js", 17 | "src/annotation.js", 18 | "src/dom.js", 19 | "src/events.js", 20 | "src/events.ui.js", 21 | "src/hint.js", 22 | 23 | "templates/core_elements.soy", 24 | "templates/image_elements.soy" 25 | ], 26 | "paths": [ 27 | "." 28 | ], 29 | "externs": [ 30 | "externs/api.externs.js", 31 | "externs/okfn_annotator.externs.js", 32 | "//json.js", 33 | "//webkit_console.js" 34 | ], 35 | "mode": "ADVANCED", // "RAW" or "ADVANCED" 36 | "level": "DEFAULT", // "DEFAULT" or "VERBOSE" 37 | "pretty-print": false, 38 | "debug": false 39 | } 40 | -------------------------------------------------------------------------------- /plovr/plovr.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/annotorious/annotorious-v1/3fb5a931428a3bc9e3f48bc905ab042a393d0907/plovr/plovr.jar -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | ## Annotorious is dead - long live Annotorious! 2 | 3 | I'm rebooting this project. The old version hasn't been maintained for years and is hopelessly outdated. I started a complete, modernized rewrite at 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/annotation.js: -------------------------------------------------------------------------------- 1 | goog.provide('annotorious.Annotation'); 2 | 3 | goog.require('annotorious.shape'); 4 | 5 | /** 6 | * A 'domain class' implementation of the external annotation interface. 7 | * @param {string} src the source URL of the annotated object 8 | * @param {string} text the annotation text 9 | * @param {annotorious.shape.Shape} shape the annotated fragment shape 10 | * @constructor 11 | */ 12 | annotorious.Annotation = function(src, text, shape) { 13 | this.src = src; 14 | this.text = text; 15 | this.shapes = [ shape ]; 16 | this['context'] = document.URL; // Prevents dead code removal 17 | } 18 | -------------------------------------------------------------------------------- /src/api.js: -------------------------------------------------------------------------------- 1 | /** API exports **/ 2 | annotorious.Annotorious.prototype['activateSelector'] = annotorious.Annotorious.prototype.activateSelector; 3 | annotorious.Annotorious.prototype['addAnnotation'] = annotorious.Annotorious.prototype.addAnnotation; 4 | annotorious.Annotorious.prototype['addHandler'] = annotorious.Annotorious.prototype.addHandler; 5 | annotorious.Annotorious.prototype['addPlugin'] = annotorious.Annotorious.prototype.addPlugin; 6 | annotorious.Annotorious.prototype['destroy'] = annotorious.Annotorious.prototype.destroy; 7 | annotorious.Annotorious.prototype['getActiveSelector'] = annotorious.Annotorious.prototype.getActiveSelector; 8 | annotorious.Annotorious.prototype['getAnnotations'] = annotorious.Annotorious.prototype.getAnnotations; 9 | annotorious.Annotorious.prototype['getAvailableSelectors'] = annotorious.Annotorious.prototype.getAvailableSelectors; 10 | annotorious.Annotorious.prototype['hideAnnotations'] = annotorious.Annotorious.prototype.hideAnnotations; 11 | annotorious.Annotorious.prototype['hideSelectionWidget'] = annotorious.Annotorious.prototype.hideSelectionWidget; 12 | annotorious.Annotorious.prototype['highlightAnnotation'] = annotorious.Annotorious.prototype.highlightAnnotation; 13 | annotorious.Annotorious.prototype['makeAnnotatable'] = annotorious.Annotorious.prototype.makeAnnotatable; 14 | annotorious.Annotorious.prototype['removeAll'] = annotorious.Annotorious.prototype.removeAll; 15 | annotorious.Annotorious.prototype['removeAnnotation'] = annotorious.Annotorious.prototype.removeAnnotation; 16 | annotorious.Annotorious.prototype['reset'] = annotorious.Annotorious.prototype.reset; 17 | annotorious.Annotorious.prototype['setActiveSelector'] = annotorious.Annotorious.prototype.setActiveSelector; 18 | annotorious.Annotorious.prototype['setProperties'] = annotorious.Annotorious.prototype.setProperties; 19 | annotorious.Annotorious.prototype['showAnnotations'] = annotorious.Annotorious.prototype.showAnnotations; 20 | annotorious.Annotorious.prototype['showSelectionWidget'] = annotorious.Annotorious.prototype.showSelectionWidget; 21 | 22 | /** Sets up the plugin namespace */ 23 | if (!window['annotorious']) 24 | window['annotorious'] = {}; 25 | 26 | if (!window['annotorious']['plugin']) 27 | window['annotorious']['plugin'] = {} 28 | 29 | /** Geometry API exports **/ 30 | if (!window['annotorious']['geometry']) { 31 | window['annotorious']['geometry'] = {}; 32 | window['annotorious']['geometry']['expand'] = annotorious.shape.expand; 33 | window['annotorious']['geometry']['getBoundingRect'] = annotorious.shape.getBoundingRect; 34 | } 35 | 36 | /** @deprecated **/ 37 | annotorious.Annotorious.prototype['setSelectionEnabled'] = annotorious.Annotorious.prototype.setSelectionEnabled; 38 | -------------------------------------------------------------------------------- /src/dom.js: -------------------------------------------------------------------------------- 1 | /** 2 | * A collection of helper functions for general use. 3 | */ 4 | goog.provide('annotorious.dom'); 5 | 6 | goog.require('goog.fx.Dragger'); 7 | 8 | /** 9 | * Computes the absolute top/left offset of a DOM element relative to the document. 10 | * @param {Element} el the DOM element 11 | * @return {Object} an object containing the offset { top, left } 12 | */ 13 | annotorious.dom.getOffset = function(el) { 14 | var _x = 0; 15 | var _y = 0; 16 | 17 | while (el && !isNaN(el.offsetLeft) && !isNaN(el.offsetTop)) { 18 | _x += el.offsetLeft - el.scrollLeft; 19 | _y += el.offsetTop - el.scrollTop; 20 | el = el.offsetParent; 21 | } 22 | return { top: _y, left: _x }; 23 | } 24 | 25 | /** 26 | * Checks whether a certain DOM element is (partly) within the current viewport. 27 | * Cf. http://stackoverflow.com/questions/123999/how-to-tell-if-a-dom-element-is-visible-in-the-current-viewport 28 | * @param {Element} el the DOM element to check for visibility 29 | * @return {boolean} true if the element is within the current viewport 30 | */ 31 | annotorious.dom.isInViewport = function(el) { 32 | var top = el.offsetTop; 33 | var left = el.offsetLeft; 34 | var width = el.offsetWidth; 35 | var height = el.offsetHeight; 36 | 37 | while (el.offsetParent) { 38 | el = el.offsetParent; 39 | top += el.offsetTop; 40 | left += el.offsetLeft; 41 | } 42 | 43 | return ( 44 | top < (window.pageYOffset + window.innerHeight) && 45 | left < (window.pageXOffset + window.innerWidth) && 46 | (top + height) > window.pageYOffset && 47 | (left + width) > window.pageXOffset 48 | ); 49 | } 50 | 51 | /** 52 | * Adds an additional handler function to window.onload, without overwriting existing ones. 53 | * @param {Function} fn the handler function to add 54 | */ 55 | annotorious.dom.addOnLoadHandler = function(fn) { 56 | if (window.addEventListener) 57 | window.addEventListener('load', fn, false); 58 | else if (window.attachEvent) 59 | window.attachEvent('onload', fn); 60 | } 61 | 62 | /** 63 | * Makes a DIV element resizable into horizontal direction. 64 | * @param {Element} div the DIV to make h-resizable 65 | * @param {Function=} opt_callback an optional function to be notified on resize 66 | */ 67 | annotorious.dom.makeHResizable = function(div, opt_callback) { 68 | var handle = goog.dom.createElement('div'); 69 | goog.style.setStyle(handle, 'position', 'absolute'); 70 | goog.style.setStyle(handle, 'top', '0px'); 71 | goog.style.setStyle(handle, 'right', '0px'); 72 | goog.style.setStyle(handle, 'width', '5px'); 73 | goog.style.setStyle(handle, 'height', '100%'); 74 | goog.style.setStyle(handle, 'cursor', 'e-resize'); 75 | goog.dom.appendChild(div, handle); 76 | 77 | var div_border = goog.style.getBorderBox(div); 78 | var width_limit = goog.style.getBounds(div).width - div_border.right - div_border.left; 79 | 80 | var dragger = new goog.fx.Dragger(handle); 81 | dragger.setLimits(new goog.math.Rect(width_limit, 0, 800, 0)); 82 | dragger.defaultAction = function(x) { 83 | goog.style.setStyle(div, 'width', x + 'px'); 84 | if (opt_callback) 85 | opt_callback(); 86 | }; 87 | } 88 | 89 | /** 90 | * A utility method that transforms a relative URL to an absolute URL. 91 | * This function is safe to use with all URLs: if an absolute URL is passed 92 | * as a parameter, it will remain unchanged. 93 | * @param {String} url the URL 94 | */ 95 | annotorious.dom.toAbsoluteURL = function(url) { 96 | if (url.indexOf('://') > 0) { 97 | return url; 98 | } else { 99 | var link = document.createElement('a'); 100 | link.href = url; 101 | return link.protocol + '//' + link.host + link.pathname; 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /src/editor.js: -------------------------------------------------------------------------------- 1 | goog.provide('annotorious.Editor'); 2 | 3 | goog.require('goog.dom'); 4 | goog.require('goog.dom.query'); 5 | goog.require('goog.soy'); 6 | goog.require('goog.string.html.htmlSanitize'); 7 | goog.require('goog.style'); 8 | goog.require('goog.ui.Textarea'); 9 | 10 | goog.require('annotorious.templates'); 11 | 12 | /** 13 | * Annotation edit form. 14 | * @param {Object} annotator reference to the annotator 15 | * @constructor 16 | */ 17 | annotorious.Editor = function(annotator) { 18 | this.element = goog.soy.renderAsElement(annotorious.templates.editform); 19 | 20 | /** @private **/ 21 | this._annotator = annotator; 22 | 23 | /** @private **/ 24 | this._item = annotator.getItem(); 25 | 26 | /** @private **/ 27 | this._original_annotation; 28 | 29 | /** @private **/ 30 | this._current_annotation; 31 | 32 | /** @private **/ 33 | this._textarea = new goog.ui.Textarea(''); 34 | 35 | /** @private **/ 36 | this._btnCancel = goog.dom.query('.annotorious-editor-button-cancel', this.element)[0]; 37 | 38 | /** @private **/ 39 | this._btnSave = goog.dom.query('.annotorious-editor-button-save', this.element)[0]; 40 | 41 | /** @private **/ 42 | this._btnContainer = goog.dom.getParentElement(this._btnSave); 43 | 44 | /** @private **/ 45 | this._extraFields = []; 46 | 47 | var self = this; 48 | goog.events.listen(this._btnCancel, goog.events.EventType.CLICK, function(event) { 49 | event.preventDefault(); 50 | annotator.stopSelection(self._original_annotation); 51 | self.close(); 52 | }); 53 | 54 | goog.events.listen(this._btnSave, goog.events.EventType.CLICK, function(event) { 55 | event.preventDefault(); 56 | var annotation = self.getAnnotation(); 57 | annotator.addAnnotation(annotation); 58 | annotator.stopSelection(); 59 | 60 | if (self._original_annotation) 61 | annotator.fireEvent(annotorious.events.EventType.ANNOTATION_UPDATED, annotation, annotator.getItem()); 62 | else 63 | annotator.fireEvent(annotorious.events.EventType.ANNOTATION_CREATED, annotation, annotator.getItem()); 64 | self.close(); 65 | }); 66 | 67 | goog.style.showElement(this.element, false); 68 | goog.dom.appendChild(annotator.element, this.element); 69 | this._textarea.decorate(goog.dom.query('.annotorious-editor-text', this.element)[0]); 70 | annotorious.dom.makeHResizable(this.element, function() { self._textarea.resize(); }); 71 | } 72 | 73 | /** 74 | * Adds a field to the editor GUI widget. A field can be either an (HTML) string, or 75 | * a function that takes an Annotation as argument and returns an (HTML) string or 76 | * a DOM element. 77 | * @param {string | Function} field the field 78 | */ 79 | annotorious.Editor.prototype.addField = function(field) { 80 | var fieldEl = goog.dom.createDom('div', 'annotorious-editor-field'); 81 | 82 | if (goog.isString(field)) { 83 | fieldEl.innerHTML = field; 84 | } else if (goog.isFunction(field)) { 85 | this._extraFields.push({el: fieldEl, fn: field}); 86 | } else if (goog.dom.isElement(field)) { 87 | goog.dom.appendChild(fieldEl, field); 88 | } 89 | 90 | goog.dom.insertSiblingBefore(fieldEl, this._btnContainer); 91 | } 92 | 93 | /** 94 | * Opens the edit form with an annotation. 95 | * @param {annotorious.Annotation=} opt_annotation the annotation to edit (or undefined) 96 | * @param {Object=} opt_event the event, if any 97 | */ 98 | annotorious.Editor.prototype.open = function(opt_annotation, opt_event) { 99 | this._annotator.fireEvent(annotorious.events.EventType.BEFORE_EDITOR_SHOWN, opt_annotation); 100 | 101 | this._original_annotation = opt_annotation; 102 | this._current_annotation = opt_annotation; 103 | 104 | if (opt_annotation) 105 | this._textarea.setValue(opt_annotation.text); 106 | 107 | goog.style.showElement(this.element, true); 108 | this._textarea.getElement().focus(); 109 | 110 | // Update extra fields (if any) 111 | goog.array.forEach(this._extraFields, function(field) { 112 | var f = field.fn(opt_annotation); 113 | if (goog.isString(f)) { 114 | field.el.innerHTML = f; 115 | } else if (goog.dom.isElement(f)) { 116 | goog.dom.removeChildren(field.el); 117 | goog.dom.appendChild(field.el, f); 118 | } 119 | }); 120 | this._annotator.fireEvent(annotorious.events.EventType.EDITOR_SHOWN, opt_annotation); 121 | } 122 | 123 | /** 124 | * Closes the editor. 125 | */ 126 | annotorious.Editor.prototype.close = function() { 127 | goog.style.showElement(this.element, false); 128 | this._textarea.setValue(''); 129 | } 130 | 131 | /** 132 | * Sets the position (i.e. CSS left/top value) of the editor element. 133 | * @param {annotorious.shape.geom.Point} xy the viewport coordinate 134 | */ 135 | annotorious.Editor.prototype.setPosition = function(xy) { 136 | goog.style.setPosition(this.element, xy.x, xy.y); 137 | } 138 | 139 | /** 140 | * Returns the annotation that is the current state of the editor. 141 | * @return {annotorious.Annotation} the annotation 142 | */ 143 | annotorious.Editor.prototype.getAnnotation = function() { 144 | var sanitized = goog.string.html.htmlSanitize(this._textarea.getValue(), function(url) { 145 | return url; 146 | }); 147 | 148 | if (this._current_annotation) { 149 | this._current_annotation.text = sanitized; 150 | } else { 151 | this._current_annotation = 152 | new annotorious.Annotation(this._item.src, sanitized, this._annotator.getActiveSelector().getShape()); 153 | } 154 | 155 | return this._current_annotation; 156 | } 157 | 158 | /** API exports **/ 159 | annotorious.Editor.prototype['addField'] = annotorious.Editor.prototype.addField; 160 | annotorious.Editor.prototype['getAnnotation'] = annotorious.Editor.prototype.getAnnotation; 161 | -------------------------------------------------------------------------------- /src/events.js: -------------------------------------------------------------------------------- 1 | goog.provide('annotorious.events'); 2 | 3 | goog.require('goog.array'); 4 | goog.require('goog.events'); 5 | 6 | /** 7 | * A central 'event bus' to distribute the annotation lifecycle events. 8 | * @constructor 9 | */ 10 | annotorious.events.EventBroker = function() { 11 | /** @private **/ 12 | this._handlers = []; 13 | } 14 | 15 | /** 16 | * Adds an event handler. 17 | * @param {annotorious.events.EventType} type the event type 18 | * @param {Function} handler the handler function to add 19 | */ 20 | annotorious.events.EventBroker.prototype.addHandler = function(type, handler) { 21 | if (!this._handlers[type]) 22 | this._handlers[type] = []; 23 | 24 | this._handlers[type].push(handler); 25 | } 26 | 27 | /** 28 | * Removes an event handler. 29 | * @param {annotorious.events.EventType} type the event type 30 | * @param {Function} handler the handler function to remove 31 | */ 32 | annotorious.events.EventBroker.prototype.removeHandler = function(type, handler) { 33 | var handlers = this._handlers[type]; 34 | if (handlers) 35 | goog.array.remove(handlers, handler); 36 | } 37 | 38 | /** 39 | * Fires an event, triggering execution of all registered handlers. 40 | * Event handlers may optionally return a boolean value to indicate whether 41 | * further steps following the event should be canceled (e.g. in case of 42 | * annotation removal). If there is no return value (or the return value is 43 | * 'true'), no action will be taken by Annotorious. 44 | * @param {annotorious.events.EventType} type the event type 45 | * @param {Object=} opt_event the event object 46 | * @return {boolean} the 'cancel event' flag 47 | */ 48 | annotorious.events.EventBroker.prototype.fireEvent = function(type, opt_event, opt_extra) { 49 | var cancelEvent = false; 50 | var handlers = this._handlers[type]; 51 | if (handlers) { 52 | goog.array.forEach(handlers, function(handler, idx, array) { 53 | var retVal = handler(opt_event, opt_extra); 54 | if (goog.isDef(retVal) && !retVal) 55 | cancelEvent = true; 56 | }); 57 | } 58 | 59 | return cancelEvent; 60 | } 61 | 62 | /** 63 | * Annotation lifecycle events. 64 | * @enum {string} 65 | */ 66 | annotorious.events.EventType = { 67 | 68 | /** 69 | * The mouse entered the annotatable media area 70 | */ 71 | MOUSE_OVER_ANNOTATABLE_ITEM: 'onMouseOverItem', 72 | 73 | /** 74 | * The mouse moved out of the annotatable media area 75 | */ 76 | MOUSE_OUT_OF_ANNOTATABLE_ITEM: 'onMouseOutOfItem', 77 | 78 | /** 79 | * The mouse entered an annotation 80 | */ 81 | MOUSE_OVER_ANNOTATION: 'onMouseOverAnnotation', 82 | 83 | /** 84 | * The mouse moved out of an annotation 85 | */ 86 | MOUSE_OUT_OF_ANNOTATION: 'onMouseOutOfAnnotation', 87 | 88 | /** 89 | * A new selection was started 90 | */ 91 | SELECTION_STARTED: 'onSelectionStarted', 92 | 93 | /** 94 | * The current selection was canceled 95 | */ 96 | SELECTION_CANCELED: 'onSelectionCanceled', 97 | 98 | /** 99 | * The current selection was completed 100 | */ 101 | SELECTION_COMPLETED: 'onSelectionCompleted', 102 | 103 | /** 104 | * The current selection was changed 105 | */ 106 | SELECTION_CHANGED: 'onSelectionChanged', 107 | 108 | 109 | /** 110 | * The annotation editor is opening. Pass the annotation object if it exists. 111 | */ 112 | BEFORE_EDITOR_SHOWN: 'beforeEditorShown', 113 | 114 | /** 115 | * The annotation editor was opened. Pass the annotation object if it exists. 116 | */ 117 | EDITOR_SHOWN: 'onEditorShown', 118 | 119 | /** 120 | * The annotation popop was opened. Pass the annotation object. 121 | */ 122 | POPUP_SHOWN: 'onPopupShown', 123 | 124 | /** 125 | * The annotation popup widget is about to hide 126 | */ 127 | BEFORE_POPUP_HIDE: 'beforePopupHide', 128 | 129 | /** 130 | * The annotation is about to be removed 131 | */ 132 | BEFORE_ANNOTATION_REMOVED: 'beforeAnnotationRemoved', 133 | 134 | /** 135 | * An annotation was removed 136 | */ 137 | ANNOTATION_REMOVED: 'onAnnotationRemoved', 138 | 139 | /** 140 | * An annotation was created 141 | */ 142 | ANNOTATION_CREATED: 'onAnnotationCreated', 143 | 144 | /** 145 | * An existing annotation was updated 146 | */ 147 | ANNOTATION_UPDATED: 'onAnnotationUpdated', 148 | 149 | /** 150 | * The annotation was clicked. Pass the annotation object. 151 | */ 152 | ANNOTATION_CLICKED: 'onAnnotationClicked' 153 | 154 | }; 155 | -------------------------------------------------------------------------------- /src/events.ui.js: -------------------------------------------------------------------------------- 1 | goog.provide('annotorious.events.ui'); 2 | 3 | goog.require('goog.dom'); 4 | goog.require('goog.dom.classes'); 5 | goog.require('goog.events.EventType'); 6 | 7 | annotorious.events.ui.hasTouch = 'ontouchstart' in window; 8 | 9 | annotorious.events.ui.hasMouse = !annotorious.events.ui.hasTouch; // Just for readability 10 | 11 | /** 12 | * Human interface events. 13 | * @enum {string} 14 | */ 15 | annotorious.events.ui.EventType = { 16 | 17 | DOWN: (annotorious.events.ui.hasTouch) ? goog.events.EventType.TOUCHSTART : goog.events.EventType.MOUSEDOWN, 18 | 19 | OVER: (annotorious.events.ui.hasTouch) ? "touchenter" : goog.events.EventType.MOUSEOVER, 20 | 21 | MOVE: (annotorious.events.ui.hasTouch) ? goog.events.EventType.TOUCHMOVE : goog.events.EventType.MOUSEMOVE, 22 | 23 | UP: (annotorious.events.ui.hasTouch) ? goog.events.EventType.TOUCHEND : goog.events.EventType.MOUSEUP, 24 | 25 | OUT: (annotorious.events.ui.hasTouch) ? "touchleave" : goog.events.EventType.MOUSEOUT, 26 | 27 | CLICK: (annotorious.events.ui.hasTouch) ? goog.events.EventType.TOUCHEND : goog.events.EventType.CLICK 28 | 29 | } 30 | 31 | /** 32 | * To get screen coordinates while taking into consideration mobile and the offset of the screen 33 | * @param {Object} event the DOM Event object 34 | * @param {Element} parent the parent element that triggers the event 35 | */ 36 | annotorious.events.ui.sanitizeCoordinates = function(event, parent) { 37 | var points = false; 38 | var offset = annotorious.dom.getOffset; 39 | 40 | // Dirty hack - Google Maps? 41 | event.offsetX = (event.offsetX) ? event.offsetX : false; 42 | event.offsetY = (event.offsetY) ? event.offsetY : false; 43 | 44 | if ((!event.offsetX || !event.offsetY) && event.event_.changedTouches) { 45 | points = { 46 | x: event.event_.changedTouches[0].clientX - offset(parent).left, 47 | y: event.event_.changedTouches[0].clientY - offset(parent).top 48 | }; 49 | } else { 50 | points = { 51 | x: event.offsetX, 52 | y: event.offsetY 53 | }; 54 | } 55 | 56 | return points; 57 | }; 58 | -------------------------------------------------------------------------------- /src/hint.js: -------------------------------------------------------------------------------- 1 | goog.provide('annotorious.Hint'); 2 | 3 | goog.require('goog.dom.query'); 4 | goog.require('goog.events'); 5 | goog.require('goog.soy'); 6 | goog.require('goog.style'); 7 | 8 | /** 9 | * The 'hint' GUI element. 10 | * @param {Object} annotator the annotator 11 | * @param {Element} parent the parent DOM element 12 | * @param {string=} opt_msg the message to display as hint 13 | * @constructor 14 | */ 15 | annotorious.Hint = function(annotator, parent, opt_msg) { 16 | var self = this; 17 | 18 | if (!opt_msg) 19 | opt_msg = 'Click and Drag to Annotate'; 20 | 21 | this.element = goog.soy.renderAsElement(annotorious.templates.image.hint, { msg: opt_msg }); 22 | 23 | /** @private **/ 24 | this._annotator = annotator; 25 | 26 | /** @private **/ 27 | this._message = goog.dom.query('.annotorious-hint-msg', this.element)[0]; 28 | 29 | /** @private **/ 30 | this._icon = goog.dom.query('.annotorious-hint-icon', this.element)[0]; 31 | 32 | /** @private **/ 33 | this._hideTimer; 34 | 35 | /** @private **/ 36 | this._mouseOverListener; 37 | 38 | /** @private **/ 39 | this._mouseOutListener; 40 | 41 | /** @private **/ 42 | this._overItemHandler = function() { 43 | self.show(); 44 | }; 45 | 46 | /** @private **/ 47 | this._outOfItemHandler = function() { 48 | self.hide(); 49 | }; 50 | 51 | this._attachListeners(); 52 | this.hide(); 53 | goog.dom.appendChild(parent, this.element); 54 | } 55 | 56 | /** 57 | * Attaches MOUSEOVER and MOUSEOUT listeners to the icon, and MOUSE_OVER_ANNOTATABLE_ITEM 58 | * and MOUSE_OUT_OF_ANNOTATABLE_ITEM handlers to the annototator instance. 59 | * @private 60 | */ 61 | annotorious.Hint.prototype._attachListeners = function() { 62 | var self = this; 63 | 64 | this._mouseOverListener = goog.events.listen(this._icon, goog.events.EventType.MOUSEOVER, function(event) { 65 | self.show(); 66 | window.clearTimeout(self._hideTimer); 67 | }); 68 | 69 | this._mouseOutListener = goog.events.listen(this._icon, goog.events.EventType.MOUSEOUT, function(event) { 70 | self.hide(); 71 | }); 72 | 73 | this._annotator.addHandler(annotorious.events.EventType.MOUSE_OVER_ANNOTATABLE_ITEM, this._overItemHandler); 74 | 75 | this._annotator.addHandler(annotorious.events.EventType.MOUSE_OUT_OF_ANNOTATABLE_ITEM, this._outOfItemHandler); 76 | } 77 | 78 | /** 79 | * Detaches MOUSEUP and MOUSEMOVE listeners from the editing canvas. 80 | * @private 81 | */ 82 | annotorious.Hint.prototype._detachListeners = function() { 83 | goog.events.unlistenByKey(this._mouseOverListener); 84 | goog.events.unlistenByKey(this._mouseOutListener); 85 | this._annotator.removeHandler(annotorious.events.EventType.MOUSE_OVER_ANNOTATABLE_ITEM, this._overItemHandler); 86 | this._annotator.removeHandler(annotorious.events.EventType.MOUSE_OUT_OF_ANNOTATABLE_ITEM, this._outOfItemHandler); 87 | } 88 | 89 | /** 90 | * Shows the hint. 91 | */ 92 | annotorious.Hint.prototype.show = function() { 93 | window.clearTimeout(this._hideTimer); 94 | goog.style.setOpacity(this._message, 0.8); 95 | 96 | var self = this; 97 | this._hideTimer = window.setTimeout(function() { 98 | self.hide(); 99 | }, 3000); 100 | } 101 | 102 | /** 103 | * Hides the hint, leaving only the Annotorious feather icon. 104 | */ 105 | annotorious.Hint.prototype.hide = function() { 106 | window.clearTimeout(this._hideTimer); 107 | goog.style.setOpacity(this._message, 0); 108 | } 109 | 110 | /** 111 | * Destroys the hint element, removing it from the DOM. 112 | */ 113 | annotorious.Hint.prototype.destroy = function() { 114 | this._detachListeners(); 115 | delete this._mouseOverListener; 116 | delete this._mouseOutListener; 117 | delete this._overItemHandler; 118 | delete this._outOfItemHandler; 119 | goog.dom.removeNode(this.element); 120 | } 121 | -------------------------------------------------------------------------------- /src/mediatypes/annotator.js: -------------------------------------------------------------------------------- 1 | goog.provide('annotorious.mediatypes.Annotator'); 2 | 3 | /** 4 | * A base class for Annotorious Annotator implementations. 5 | * @constructor 6 | */ 7 | annotorious.mediatypes.Annotator = function() { } 8 | 9 | annotorious.mediatypes.Annotator.prototype.addAnnotation = function(annotation, opt_replace) { 10 | this._viewer.addAnnotation(annotation, opt_replace); 11 | } 12 | 13 | annotorious.mediatypes.Annotator.prototype.addHandler = function(type, handler) { 14 | this._eventBroker.addHandler(type, handler); 15 | } 16 | 17 | annotorious.mediatypes.Annotator.prototype.removeHandler = function(type) { 18 | this._eventBroker.removeHandler(type); 19 | } 20 | 21 | annotorious.mediatypes.Annotator.prototype.fireEvent = function(type, event, opt_extra) { 22 | return this._eventBroker.fireEvent(type, event, opt_extra); 23 | } 24 | 25 | annotorious.mediatypes.Annotator.prototype.getActiveSelector = function() { 26 | return this._currentSelector; 27 | } 28 | 29 | annotorious.mediatypes.Annotator.prototype.highlightAnnotation = function(annotation) { 30 | this._viewer.highlightAnnotation(annotation); 31 | } 32 | 33 | annotorious.mediatypes.Annotator.prototype.removeAnnotation = function(annotation) { 34 | this._viewer.removeAnnotation(annotation); 35 | } 36 | 37 | annotorious.mediatypes.Annotator.prototype.removeHandler = function(type, handler) { 38 | this._eventBroker.removeHandler(type, handler); 39 | } 40 | 41 | annotorious.mediatypes.Annotator.prototype.stopSelection = function(original_annotation) { 42 | if (annotorious.events.ui.hasMouse) 43 | goog.style.showElement(this._editCanvas, false); 44 | 45 | if (this._stop_selection_callback) { 46 | this._stop_selection_callback(); 47 | delete this._stop_selection_callback; 48 | } 49 | 50 | this._currentSelector.stopSelection(); 51 | 52 | // If this was an edit of an annotation (rather than creation of a new one) re-add to viewer! 53 | if (original_annotation) 54 | this._viewer.addAnnotation(original_annotation); 55 | } 56 | 57 | annotorious.mediatypes.Annotator.prototype._attachListener = function(activeCanvas) { 58 | var self = this; 59 | goog.events.listen(activeCanvas, annotorious.events.ui.EventType.DOWN, function(event) { 60 | console.log('start selection event'); 61 | console.log(event); 62 | var coords = annotorious.events.ui.sanitizeCoordinates(event, activeCanvas); 63 | self._viewer.highlightAnnotation(false); 64 | if (self._selectionEnabled) { 65 | goog.style.showElement(self._editCanvas, true); 66 | self._currentSelector.startSelection(coords.x, coords.y); 67 | } else { 68 | var annotations = self._viewer.getAnnotationsAt(coords.x, coords.y); 69 | if (annotations.length > 0) 70 | self._viewer.highlightAnnotation(annotations[0]); 71 | } 72 | }); 73 | } 74 | -------------------------------------------------------------------------------- /src/mediatypes/image/image.module.js: -------------------------------------------------------------------------------- 1 | goog.provide('annotorious.mediatypes.image.ImageModule'); 2 | 3 | goog.require('annotorious.mediatypes.Module'); 4 | goog.require('annotorious.mediatypes.image.ImageAnnotator'); 5 | 6 | /** 7 | * The Image Module implements annotation functionality for 8 | * elements. On page load, it also scans the page for images marked 9 | * with an 'annotatable' CSS class, and makes those annotatable automatically. 10 | * @constructor 11 | * @extends annotorious.mediatypes.Module 12 | */ 13 | annotorious.mediatypes.image.ImageModule = function() { 14 | annotorious.mediatypes.Module.call(); 15 | this._initFields(function() { 16 | return goog.dom.query('img.annotatable', document); 17 | }); 18 | } 19 | goog.inherits(annotorious.mediatypes.image.ImageModule, annotorious.mediatypes.Module); 20 | 21 | /** @inheritDoc **/ 22 | annotorious.mediatypes.image.ImageModule.prototype.getItemURL = function(item) { 23 | return annotorious.mediatypes.image.ImageAnnotator.getItemURL(item); 24 | } 25 | 26 | /** @inheritDoc **/ 27 | annotorious.mediatypes.image.ImageModule.prototype.newAnnotator = function(item) { 28 | return new annotorious.mediatypes.image.ImageAnnotator(item); 29 | } 30 | 31 | /** @inheritDoc **/ 32 | annotorious.mediatypes.image.ImageModule.prototype.supports = function(item) { 33 | if (goog.dom.isElement(item)) 34 | return (item.tagName == 'IMG'); 35 | else 36 | return false; 37 | } 38 | -------------------------------------------------------------------------------- /src/mediatypes/openlayers/openlayers.annotator.js: -------------------------------------------------------------------------------- 1 | goog.provide('annotorious.mediatypes.openlayers.OpenLayersAnnotator'); 2 | 3 | goog.require('annotorious.mediatypes.Annotator'); 4 | goog.require('annotorious.templates.openlayers'); 5 | goog.require('annotorious.mediatypes.openlayers.Viewer'); 6 | 7 | /** 8 | * The OpenLayersAnnotator is responsible for handling annotation functionality 9 | * on one OpenLayers map in the page. 10 | * @param {OpenLayers.Map} map the OpenLayers map 11 | * @constructor 12 | */ 13 | annotorious.mediatypes.openlayers.OpenLayersAnnotator = function(map) { 14 | annotorious.mediatypes.Annotator.call(); 15 | 16 | /** @private **/ 17 | this._map = map; 18 | 19 | /** @private **/ 20 | this.element = map.div; 21 | 22 | // We need to constrain the dimension of the canvas by the size of the map. 23 | // Therefore the map enclosing DIV needs to have position 'absolute' or 24 | // 'relative' set! 25 | var pos = goog.style.getStyle(this.element, 'position'); 26 | if (pos != 'absolute' && pos != 'relative') 27 | goog.style.setStyle(this.element, 'position', 'relative'); 28 | 29 | /** @private **/ 30 | this._eventBroker = new annotorious.events.EventBroker(); 31 | 32 | /** @private **/ 33 | this._secondaryHint = goog.soy.renderAsElement(annotorious.templates.openlayers.secondaryHint, {msg: 'Click and Drag'}); 34 | goog.style.setStyle(this._secondaryHint, 'z-index', 9998); 35 | goog.style.setOpacity(this._secondaryHint, 0); 36 | goog.dom.appendChild(this.element, this._secondaryHint); 37 | 38 | /** @private **/ 39 | this.popup = new annotorious.Popup(this); 40 | 41 | /** @private **/ 42 | this._viewer = new annotorious.mediatypes.openlayers.Viewer(map, this); 43 | 44 | /** @private **/ 45 | this._editCanvas = goog.soy.renderAsElement(annotorious.templates.image.canvas, 46 | { width:'0', height:'0' }); 47 | goog.style.showElement(this._editCanvas, false); 48 | goog.style.setStyle(this._editCanvas, 'position', 'absolute'); 49 | goog.style.setStyle(this._editCanvas, 'top', '0px'); 50 | goog.style.setStyle(this._editCanvas, 'z-index', 9999); 51 | 52 | goog.dom.appendChild(this.element, this._editCanvas); 53 | 54 | var self = this, 55 | updateCanvasSize = function() { 56 | var width = parseInt(goog.style.getComputedStyle(self.element, 'width'), 10), 57 | height = parseInt(goog.style.getComputedStyle(self.element, 'height'), 10); 58 | 59 | goog.style.setSize(self._editCanvas, width, height); 60 | self._editCanvas.width = width; 61 | self._editCanvas.height = height; 62 | }; 63 | 64 | updateCanvasSize(); 65 | 66 | /** @private **/ 67 | this._currentSelector = new annotorious.plugins.selection.RectDragSelector(); 68 | this._currentSelector.init(this, this._editCanvas); 69 | 70 | /** @private **/ 71 | this._stop_selection_callback = undefined; 72 | 73 | /** @private **/ 74 | this.editor = new annotorious.Editor(this); 75 | goog.style.setStyle(this.editor.element, 'z-index', 10000); 76 | 77 | if (window.addEventListener) 78 | window.addEventListener('resize', updateCanvasSize, false); 79 | else if (window.attachEvent) 80 | window.attachEvent('onresize', updateCanvasSize); 81 | 82 | goog.events.listen(this.element, goog.events.EventType.MOUSEOVER, function(event) { 83 | var relatedTarget = event.relatedTarget; 84 | if (!relatedTarget || !goog.dom.contains(self.element, relatedTarget)) 85 | self._eventBroker.fireEvent(annotorious.events.EventType.MOUSE_OVER_ANNOTATABLE_ITEM); 86 | }); 87 | 88 | goog.events.listen(this.element, goog.events.EventType.MOUSEOUT, function(event) { 89 | var relatedTarget = event.relatedTarget; 90 | if (!relatedTarget || !goog.dom.contains(self.element, relatedTarget)) 91 | self._eventBroker.fireEvent(annotorious.events.EventType.MOUSE_OUT_OF_ANNOTATABLE_ITEM); 92 | }); 93 | 94 | goog.events.listen(this._editCanvas, goog.events.EventType.MOUSEDOWN, function(event) { 95 | var offset = goog.style.getClientPosition(self.element); 96 | self._currentSelector.startSelection(event.clientX - offset.x, event.clientY - offset.y); 97 | }); 98 | 99 | this._eventBroker.addHandler(annotorious.events.EventType.SELECTION_COMPLETED, function(event) { 100 | goog.style.setStyle(self._editCanvas, 'pointer-events', 'none'); 101 | 102 | var bounds = event.viewportBounds; 103 | self.editor.setPosition(new annotorious.shape.geom.Point(bounds.left /* + self.element.offsetLeft */, 104 | bounds.bottom + 4 /* + self.element.offsetTop */)); 105 | self.editor.open(); 106 | }); 107 | 108 | this._eventBroker.addHandler(annotorious.events.EventType.SELECTION_CANCELED, function(event) { 109 | self.stopSelection(); 110 | }); 111 | } 112 | goog.inherits(annotorious.mediatypes.openlayers.OpenLayersAnnotator, annotorious.mediatypes.Annotator); 113 | 114 | annotorious.mediatypes.openlayers.OpenLayersAnnotator.prototype.showSelectionWidget = function() { 115 | // Does not have any effect at the moment 116 | } 117 | 118 | annotorious.mediatypes.openlayers.OpenLayersAnnotator.prototype.hideSelectionWidget = function() { 119 | // Does not have any effect at the moment 120 | } 121 | 122 | annotorious.mediatypes.openlayers.OpenLayersAnnotator.prototype.activateSelector = function(callback) { 123 | goog.style.setStyle(this._editCanvas, 'pointer-events', 'auto'); 124 | 125 | var self = this; 126 | goog.style.showElement(this._editCanvas, true); 127 | goog.style.setOpacity(this._secondaryHint, 0.8); 128 | window.setTimeout(function() { 129 | goog.style.setOpacity(self._secondaryHint, 0); 130 | }, 2000); 131 | 132 | if (callback) 133 | this._stop_selection_callback = callback; 134 | } 135 | 136 | annotorious.mediatypes.openlayers.OpenLayersAnnotator.prototype.destroy = function() { 137 | this._viewer.destroy(); 138 | goog.dom.removeNode(this._secondaryHint); 139 | goog.dom.removeNode(this._editCanvas); 140 | } 141 | 142 | /** 143 | * Standard Annotator method: addSelector 144 | */ 145 | annotorious.mediatypes.openlayers.OpenLayersAnnotator.prototype.addSelector = function(selector) { 146 | 147 | } 148 | 149 | annotorious.mediatypes.openlayers.OpenLayersAnnotator.prototype.editAnnotation = function(annotation) { 150 | // Step 1 - remove from viewer 151 | this._viewer.removeAnnotation(annotation); 152 | 153 | // Step 2 - TODO find a suitable selector for the shape 154 | var selector = this._currentSelector; 155 | 156 | // Step 3 - open annotation in editor 157 | var self = this; 158 | if (selector) { 159 | goog.style.showElement(this._editCanvas, true); 160 | this._viewer.highlightAnnotation(undefined); 161 | 162 | // TODO make editable - not just draw (selector implementation required) 163 | var g2d = this._editCanvas.getContext('2d'); 164 | var shape = annotation.shapes[0]; 165 | var viewportShape = annotorious.shape.transform(shape, function(xy) { return self.fromItemCoordinates(xy); }); 166 | console.log(viewportShape); 167 | selector.drawShape(g2d, viewportShape); 168 | 169 | var viewportBounds = annotorious.shape.getBoundingRect(viewportShape).geometry; 170 | this.editor.setPosition(new annotorious.shape.geom.Point(viewportBounds.x, viewportBounds.y + viewportBounds.height)); 171 | this.editor.open(annotation); 172 | } 173 | } 174 | 175 | /** 176 | * Standard Annotator method: fromItemCoordinates 177 | */ 178 | annotorious.mediatypes.openlayers.OpenLayersAnnotator.prototype.fromItemCoordinates = function(itemCoords) { 179 | var pxCoords = this._map.getViewPortPxFromLonLat(new OpenLayers.LonLat(itemCoords.x, itemCoords.y)); 180 | var pxOpposite = (itemCoords.width) ? 181 | this._map.getViewPortPxFromLonLat(new OpenLayers.LonLat(itemCoords.x + itemCoords.width, itemCoords.y + itemCoords.height)) : 182 | false; 183 | 184 | if (pxOpposite) { 185 | return { x: pxCoords.x, y: pxOpposite.y, width: pxOpposite.x - pxCoords.x + 2, height: pxCoords.y - pxOpposite.y + 2}; 186 | } else { 187 | return { x: pxCoords.x, y: pxCoords.y }; 188 | } 189 | } 190 | 191 | /** 192 | * Standard Annotator method: getAnnotations 193 | */ 194 | annotorious.mediatypes.openlayers.OpenLayersAnnotator.prototype.getAnnotations = function() { 195 | return this._viewer.getAnnotations(); 196 | } 197 | 198 | /** 199 | * Returns the annotations at the specified client X/Y coordinates. 200 | * @param {number} cx the client X coordinate 201 | * @param {number} cy the client Y coordinate 202 | * @return {Array.} the annotations sorted by size, smallest first 203 | */ 204 | annotorious.mediatypes.openlayers.OpenLayersAnnotator.prototype.getAnnotationsAt = function(cx, cy) { 205 | return goog.array.clone(this._viewer.getAnnotationsAt(cx, cy)); 206 | } 207 | 208 | /** 209 | * Standard Annotator method: getAvailableSelectors 210 | */ 211 | annotorious.mediatypes.openlayers.OpenLayersAnnotator.prototype.getAvailableSelectors = function() { 212 | 213 | } 214 | 215 | /** 216 | * Standard Annotator method: getItem 217 | */ 218 | annotorious.mediatypes.openlayers.OpenLayersAnnotator.prototype.getItem = function() { 219 | // TODO implement something decent! 220 | return {src: "map://openlayers/something"}; 221 | } 222 | 223 | /** 224 | * Standard Annotator method: setActiveSelector 225 | */ 226 | annotorious.mediatypes.openlayers.OpenLayersAnnotator.prototype.setActiveSelector = function(selector) { 227 | 228 | } 229 | 230 | /** 231 | * Standard Annotator method: toItemCoordinates 232 | */ 233 | annotorious.mediatypes.openlayers.OpenLayersAnnotator.prototype.toItemCoordinates = function(xy) { 234 | var itemCoords = this._map.getLonLatFromPixel(new OpenLayers.Pixel(xy.x, xy.y)); 235 | var opposite = (xy.width) ? new OpenLayers.Pixel(xy.x + xy.width - 2, xy.y + xy.height - 2) : false; 236 | 237 | if (opposite) { 238 | var itemOpposite = this._map.getLonLatFromPixel(opposite); 239 | var foo = { x: itemCoords.lon, y: itemOpposite.lat, 240 | width: itemOpposite.lon - itemCoords.lon, 241 | height: itemCoords.lat - itemOpposite.lat }; 242 | 243 | console.log(foo); 244 | return foo; 245 | } else { 246 | return { x: itemCoords.lon, y: itemCoords.lat }; 247 | } 248 | } 249 | -------------------------------------------------------------------------------- /src/mediatypes/openlayers/openlayers.module.js: -------------------------------------------------------------------------------- 1 | goog.provide('annotorious.mediatypes.openlayers.OpenLayersModule'); 2 | 3 | goog.require('annotorious.mediatypes.Module'); 4 | goog.require('annotorious.mediatypes.openlayers.OpenLayersAnnotator'); 5 | 6 | /** 7 | * The OpenLayers Module provides annotation functionality for embedded 8 | * Web maps built with the OpenLayers Web mapping framework. 9 | * @constructor 10 | * @extends annotorious.mediatypes.Module 11 | */ 12 | annotorious.mediatypes.openlayers.OpenLayersModule = function() { 13 | annotorious.mediatypes.Module.call(); 14 | this._initFields(); 15 | } 16 | goog.inherits(annotorious.mediatypes.openlayers.OpenLayersModule, annotorious.mediatypes.Module); 17 | 18 | /** @inheritDoc **/ 19 | annotorious.mediatypes.openlayers.OpenLayersModule.prototype.getItemURL = function(item) { 20 | // TODO implement something decent! 21 | return 'map://openlayers/something'; 22 | } 23 | 24 | /** @inheritDoc **/ 25 | annotorious.mediatypes.openlayers.OpenLayersModule.prototype.newAnnotator = function(item) { 26 | return new annotorious.mediatypes.openlayers.OpenLayersAnnotator(item); 27 | } 28 | 29 | /** @inheritDoc **/ 30 | annotorious.mediatypes.openlayers.OpenLayersModule.prototype.supports = function(item) { 31 | return (item instanceof OpenLayers.Map); 32 | } 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /src/mediatypes/openlayers/openlayers.viewer.js: -------------------------------------------------------------------------------- 1 | goog.provide('annotorious.mediatypes.openlayers.Viewer'); 2 | 3 | goog.require('goog.events.MouseWheelHandler'); 4 | 5 | /** 6 | * The OpenLayers viewer wraps an OpenLayers Box Marker layer to display annotations inside 7 | * of Box markers. 8 | * @param {Object} map the OpenLayers map 9 | * @param {annotorious.mediatypes.openlayers.OpenLayersAnnotator} annotator reference to the annotator 10 | * @constructor 11 | */ 12 | annotorious.mediatypes.openlayers.Viewer = function(map, annotator) { 13 | /** @private **/ 14 | this._map = map; 15 | 16 | /** @private **/ 17 | this._annotator = annotator; 18 | 19 | /** @private **/ 20 | this._map_bounds = goog.style.getBounds(annotator.element); 21 | 22 | /** @private **/ 23 | this._popup = annotator.popup; 24 | goog.style.setStyle(this._popup.element, 'z-index', 99000); 25 | 26 | /** @private **/ 27 | this._overlays = []; 28 | 29 | /** @private **/ 30 | this._shapes = []; 31 | 32 | /** @private **/ 33 | this._currentlyHighlightedOverlay; 34 | 35 | /** @private **/ 36 | this._lastHoveredOverlay; 37 | 38 | /** @private **/ 39 | this._boxesLayer = new OpenLayers.Layer.Boxes('Annotorious'); // TODO make configurable 40 | this._map.addLayer(this._boxesLayer); 41 | 42 | var self = this; 43 | this._map.events.register('move', this._map, function() { 44 | if (self._currentlyHighlightedOverlay) 45 | self._place_popup(); 46 | }); 47 | 48 | annotator.addHandler(annotorious.events.EventType.BEFORE_POPUP_HIDE, function() { 49 | if (self._lastHoveredOverlay == self._currentlyHighlightedOverlay) 50 | self._popup.clearHideTimer(); 51 | else 52 | self._updateHighlight(self._lastHoveredOverlay, self._currentlyHighlightedOverlay); 53 | }); 54 | } 55 | 56 | annotorious.mediatypes.openlayers.Viewer.prototype.destroy = function() { 57 | this._boxesLayer.destroy(); 58 | } 59 | 60 | /** 61 | * Resets the position of the popup, without changing the annotation. 62 | */ 63 | annotorious.mediatypes.openlayers.Viewer.prototype._place_popup = function() { 64 | // Compute correct annotation bounds, relative to map 65 | var annotation_div = this._currentlyHighlightedOverlay.marker.div; 66 | var annotation_dim = goog.style.getBounds(annotation_div); 67 | var annotation_pos = goog.style.getRelativePosition(annotation_div, this._map.div); 68 | var annotation_bounds = { top: annotation_pos.y, 69 | left: annotation_pos.x, 70 | width: annotation_dim.width, 71 | height: annotation_dim.height }; 72 | 73 | // Popup width & height 74 | var popup_bounds = goog.style.getBounds(this._popup.element); 75 | 76 | var popup_pos = { y: annotation_bounds.top + annotation_bounds.height + 5 }; 77 | if (annotation_bounds.left + popup_bounds.width > this._map_bounds.width) { 78 | goog.dom.classes.addRemove(this._popup.element, 'top-left', 'top-right'); 79 | popup_pos.x = (annotation_bounds.left + annotation_bounds.width) - popup_bounds.width; 80 | } else { 81 | goog.dom.classes.addRemove(this._popup.element, 'top-right', 'top-left'); 82 | popup_pos.x = annotation_bounds.left; 83 | } 84 | 85 | if (popup_pos.x < 0) 86 | popup_pos.x = 0; 87 | 88 | if (popup_pos.x + popup_bounds.width > this._map_bounds.width) 89 | popup_pos.x = this._map_bounds.width - popup_bounds.width; 90 | 91 | if (popup_pos.y + popup_bounds.height > this._map_bounds.height) 92 | popup_pos.y = this._map_bounds.height - popup_bounds.height; 93 | 94 | this._popup.setPosition(popup_pos); 95 | } 96 | 97 | /** 98 | * Shows the popup with a new annotation. 99 | * @param {annotorious.Annotation} annotation the annotation 100 | */ 101 | annotorious.mediatypes.openlayers.Viewer.prototype._show_popup = function(annotation) { 102 | this._popup.setAnnotation(annotation); 103 | this._place_popup(); 104 | this._popup.show(); 105 | } 106 | 107 | /** 108 | * @param {Object=} new_highlight the overlay to highlight 109 | * @param {Object=} previous_highlight the overlay previously highlighted 110 | */ 111 | annotorious.mediatypes.openlayers.Viewer.prototype._updateHighlight = function(new_highlight, previous_highlight) { 112 | if (new_highlight) { 113 | var pos = goog.style.getRelativePosition(new_highlight.marker.div, this._map.div); 114 | var height = parseInt(goog.style.getStyle(new_highlight.marker.div, 'height'), 10); 115 | goog.style.setStyle(new_highlight.inner, 'border-color', '#fff000'); 116 | this._currentlyHighlightedOverlay = new_highlight; 117 | this._show_popup(new_highlight.annotation); 118 | } else { 119 | delete this._currentlyHighlightedOverlay; 120 | } 121 | 122 | if (previous_highlight) { 123 | goog.style.setStyle(previous_highlight.inner, 'border-color', '#fff'); 124 | } 125 | } 126 | 127 | /** 128 | * Adds an annotation to the viewer. 129 | * @param {annotorious.Annotation} annotation the annotation 130 | */ 131 | annotorious.mediatypes.openlayers.Viewer.prototype.addAnnotation = function(annotation) { 132 | var geometry = annotation.shapes[0].geometry; 133 | var marker = 134 | new OpenLayers.Marker.Box(new OpenLayers.Bounds(geometry.x, geometry.y, geometry.x + geometry.width, geometry.y + geometry.height)); 135 | goog.dom.classes.add(marker.div, 'annotorious-ol-boxmarker-outer'); 136 | goog.style.setStyle(marker.div, 'border', null); 137 | 138 | var inner = goog.dom.createDom('div', 'annotorious-ol-boxmarker-inner'); 139 | goog.style.setSize(inner, '100%', '100%'); 140 | goog.dom.appendChild(marker.div, inner); 141 | 142 | var overlay = {annotation: annotation, marker: marker, inner: inner}; 143 | 144 | var self = this; 145 | goog.events.listen(inner, goog.events.EventType.MOUSEOVER, function(event) { 146 | if (!self._currentlyHighlightedOverlay) 147 | self._updateHighlight(overlay); 148 | 149 | self._lastHoveredOverlay = overlay; 150 | }); 151 | 152 | goog.events.listen(inner, goog.events.EventType.MOUSEOUT, function(event) { 153 | delete self._lastHoveredOverlay; 154 | self._popup.startHideTimer(); 155 | }); 156 | 157 | this._overlays.push(overlay); 158 | 159 | // The viewer always operates in pixel coordinates for efficiency reasons 160 | var shape = annotation.shapes[0]; 161 | if (shape.units == annotorious.shape.Units.PIXEL) { 162 | this._shapes[annotorious.shape.hashCode(annotation.shapes[0])] = shape; 163 | } else { 164 | var viewportShape = annotorious.shape.transform(shape, function(xy) { 165 | return self._annotator.fromItemCoordinates(xy); 166 | }); 167 | this._shapes[annotorious.shape.hashCode(annotation.shapes[0])] = viewportShape; 168 | } 169 | 170 | goog.array.sort(this._overlays, function(a, b) { 171 | var shapeA = a.annotation.shapes[0]; 172 | var shapeB = b.annotation.shapes[0]; 173 | return annotorious.shape.getSize(shapeB) - annotorious.shape.getSize(shapeA); 174 | }); 175 | 176 | var zIndex = 10000; 177 | goog.array.forEach(this._overlays, function(overlay) { 178 | goog.style.setStyle(overlay.marker.div, 'z-index', zIndex); 179 | zIndex++; 180 | }); 181 | 182 | this._boxesLayer.addMarker(marker); 183 | } 184 | 185 | /** 186 | * Removes an annotation from the viewer. 187 | * @param {annotorious.Annotation} annotation the annotation 188 | */ 189 | annotorious.mediatypes.openlayers.Viewer.prototype.removeAnnotation = function(annotation) { 190 | var overlay = goog.array.find(this._overlays, function(overlay) { 191 | return overlay.annotation == annotation; 192 | }); 193 | 194 | if (overlay) { 195 | goog.array.remove(this._overlays, overlay); 196 | this._boxesLayer.removeMarker(overlay.marker); 197 | } 198 | } 199 | 200 | /** 201 | * Returns all annotations in this viewer. 202 | * @return {Array.} the annotations 203 | */ 204 | annotorious.mediatypes.openlayers.Viewer.prototype.getAnnotations = function() { 205 | return goog.array.map(this._overlays, function(overlay) { 206 | return overlay.annotation; 207 | }); 208 | } 209 | 210 | /** 211 | * Highlights a particular annotation in the viewer, or de-highlights (if that's a 212 | * word...) all, if no annotation is passed to the method. 213 | * @param {annotorious.Annotation | undefined} opt_annotation the annotation 214 | */ 215 | annotorious.mediatypes.openlayers.Viewer.prototype.highlightAnnotation = function(opt_annotation) { 216 | if (opt_annotation) { 217 | // TODO 218 | } else { 219 | this._popup.startHideTimer(); 220 | } 221 | } 222 | 223 | /** 224 | * Convenience method returing only the top-most annotation at the specified coordinates. 225 | * @param {number} px the X coordinate 226 | * @param {number} py the Y coordinates 227 | */ 228 | annotorious.mediatypes.openlayers.Viewer.prototype.topAnnotationAt = function(px, py) { 229 | var annotations = this.getAnnotationsAt(px, py); 230 | if (annotations.length > 0) { 231 | return annotations[0]; 232 | } else { 233 | return undefined; 234 | } 235 | } 236 | 237 | /** 238 | * Returns the annotations at the specified X/Y coordinates. 239 | * @param {number} px the X coordinate 240 | * @param {number} py the Y coordinate 241 | * @return {Array.} the annotations sorted by size, smallest first 242 | */ 243 | annotorious.mediatypes.openlayers.Viewer.prototype.getAnnotationsAt = function(px, py) { 244 | // TODO for large numbers of annotations, we can optimize this 245 | // using a tree- or grid-like data structure instead of a list 246 | var intersectedAnnotations = []; 247 | 248 | var self = this; 249 | goog.array.forEach(this._overlays, function(overlay) { 250 | var annotation = overlay.annotation; 251 | if (annotorious.shape.intersects(self._shapes[annotorious.shape.hashCode(annotation.shapes[0])], px, py)) { 252 | intersectedAnnotations.push(annotation); 253 | } 254 | }); 255 | 256 | goog.array.sort(intersectedAnnotations, function(a, b) { 257 | var shape_a = self._shapes[annotorious.shape.hashCode(a.shapes[0])]; 258 | var shape_b = self._shapes[annotorious.shape.hashCode(b.shapes[0])]; 259 | return annotorious.shape.getSize(shape_a) - annotorious.shape.getSize(shape_b); 260 | }); 261 | 262 | return intersectedAnnotations; 263 | } 264 | -------------------------------------------------------------------------------- /src/mediatypes/openseadragon/openseadragon.annotator.js: -------------------------------------------------------------------------------- 1 | goog.provide('annotorious.mediatypes.openseadragon.OpenSeadragonAnnotator'); 2 | 3 | goog.require('annotorious.mediatypes.Annotator'); 4 | goog.require('annotorious.mediatypes.openseadragon.Viewer'); 5 | 6 | /** 7 | * The OpenSeadragonAnnotator is responsible for handling annotation functionality 8 | * on one OpenSeadragon imagein the page. 9 | * 10 | * FIXME there is lots of code duplication in here - refactor into a common annotator base class, 11 | * shared across image & OpenSeadragon 12 | * 13 | * @param {Object} osdViewer the OpenSeadragon viewer 14 | * @constructor 15 | */ 16 | annotorious.mediatypes.openseadragon.OpenSeadragonAnnotator = function(osdViewer) { 17 | annotorious.mediatypes.Annotator.call(); 18 | 19 | /** @private **/ 20 | this.element = osdViewer.element; 21 | // Hacky: Normalize the z-index of openseadragon-container 22 | goog.style.setStyle(goog.dom.getElementByClass("openseadragon-container"), 'z-index', 0); 23 | 24 | /** The editor for this annotator (public for use by plugins) **/ 25 | this.editor; 26 | 27 | /** @private **/ 28 | this._osdViewer = osdViewer; 29 | 30 | /** @private **/ 31 | this._eventBroker = new annotorious.events.EventBroker(); 32 | 33 | /** @private **/ 34 | this._selectors = []; 35 | 36 | /** @private **/ 37 | this._currentSelector; 38 | 39 | /** @private **/ 40 | this._selectionEnabled = true; 41 | 42 | /** @private **/ 43 | this._secondaryHint = goog.soy.renderAsElement(annotorious.templates.openlayers.secondaryHint, {msg: 'Click and Drag'}); 44 | //goog.style.setStyle(this._secondaryHint, 'z-index', 9998); 45 | goog.style.setOpacity(this._secondaryHint, 0); 46 | goog.dom.appendChild(this.element, this._secondaryHint); 47 | 48 | /** @private **/ 49 | this.popup = new annotorious.Popup(this); 50 | 51 | /** @private **/ 52 | this._viewer = new annotorious.mediatypes.openseadragon.Viewer(osdViewer, this); 53 | 54 | /** @private **/ 55 | this._editCanvas = goog.soy.renderAsElement(annotorious.templates.image.canvas, 56 | { width:'0', height:'0' }); 57 | goog.style.showElement(this._editCanvas, false); 58 | 59 | goog.dom.appendChild(this.element, this._editCanvas); 60 | 61 | var self = this, 62 | updateCanvasSize = function() { 63 | var width = parseInt(goog.style.getComputedStyle(self.element, 'width'), 10), 64 | height = parseInt(goog.style.getComputedStyle(self.element, 'height'), 10); 65 | 66 | goog.style.setSize(self._editCanvas, width, height); 67 | self._editCanvas.width = width; 68 | self._editCanvas.height = height; 69 | }; 70 | 71 | updateCanvasSize(); 72 | 73 | var default_selector = new annotorious.plugins.selection.RectDragSelector(); 74 | default_selector.init(this, this._editCanvas); 75 | this._selectors.push(default_selector); 76 | this._currentSelector = default_selector; 77 | 78 | this.editor = new annotorious.Editor(this); 79 | 80 | /** Note - this code is duplicate across image, OpenLayers and OpenSeadragon & really needs to go into its own class **/ 81 | this._attachListener(this._editCanvas); 82 | 83 | this._eventBroker.addHandler(annotorious.events.EventType.SELECTION_COMPLETED, function(event) { 84 | //goog.style.setStyle(self._editCanvas, 'pointer-events', 'none'); 85 | 86 | var bounds = event.viewportBounds; 87 | self.editor.setPosition(new annotorious.shape.geom.Point(bounds.left, bounds.bottom + 4)); 88 | self.editor.open(); 89 | }); 90 | 91 | this._eventBroker.addHandler(annotorious.events.EventType.SELECTION_CANCELED, function(event) { 92 | self.stopSelection(); 93 | }); 94 | 95 | /** End of possible dupplicated code **/ 96 | } 97 | goog.inherits(annotorious.mediatypes.openseadragon.OpenSeadragonAnnotator, annotorious.mediatypes.Annotator); 98 | 99 | annotorious.mediatypes.openseadragon.OpenSeadragonAnnotator.prototype.showSelectionWidget = function() { 100 | // Does not have any effect at the moment 101 | } 102 | 103 | annotorious.mediatypes.openseadragon.OpenSeadragonAnnotator.prototype.hideSelectionWidget = function() { 104 | // Does not have any effect at the moment 105 | } 106 | 107 | annotorious.mediatypes.openseadragon.OpenSeadragonAnnotator.prototype.destroy = function () { 108 | this._viewer.destroy(); 109 | delete this._viewer; 110 | } 111 | 112 | annotorious.mediatypes.openseadragon.OpenSeadragonAnnotator.prototype.activateSelector = function(callback) { 113 | goog.style.setStyle(this._editCanvas, 'pointer-events', 'auto'); 114 | 115 | var self = this; 116 | goog.style.showElement(this._editCanvas, true); 117 | goog.style.setOpacity(this._secondaryHint, 0.8); 118 | window.setTimeout(function() { 119 | goog.style.setOpacity(self._secondaryHint, 0); 120 | }, 2000); 121 | 122 | if (callback) 123 | this._stop_selection_callback = callback; 124 | } 125 | 126 | annotorious.mediatypes.openseadragon.OpenSeadragonAnnotator.prototype.editAnnotation = function(annotation) { 127 | // Step 1 - remove from viewer 128 | this._viewer.removeAnnotation(annotation); 129 | 130 | // Step 2 - TODO find a suitable selector for the shape 131 | var selector = this._currentSelector; 132 | 133 | // Step 3 - open annotation in editor 134 | var self = this; 135 | if (selector) { 136 | goog.style.showElement(this._editCanvas, true); 137 | this._viewer.highlightAnnotation(undefined); 138 | 139 | // TODO make editable - not just draw (selector implementation required) 140 | var g2d = this._editCanvas.getContext('2d'); 141 | var shape = annotation.shapes[0]; 142 | var viewportShape = annotorious.shape.transform(shape, function(xy) { return self.fromItemCoordinates(xy); }); 143 | selector.drawShape(g2d, viewportShape); 144 | 145 | var viewportBounds = annotorious.shape.getBoundingRect(viewportShape).geometry; 146 | this.editor.setPosition(new annotorious.shape.geom.Point(viewportBounds.x, viewportBounds.y + viewportBounds.height + 4)); 147 | this.editor.open(annotation); 148 | } 149 | } 150 | 151 | annotorious.mediatypes.openseadragon.OpenSeadragonAnnotator.prototype.fromItemCoordinates = function(itemCoords) { 152 | var offset = annotorious.dom.getOffset(this.element); 153 | offset.top += window.pageYOffset; 154 | offset.left += window.pageXOffset; 155 | 156 | var viewportPoint = new OpenSeadragon.Point(itemCoords.x, itemCoords.y); 157 | var viewportOpposite = (itemCoords.width) ? new OpenSeadragon.Point(itemCoords.x + itemCoords.width, itemCoords.y + itemCoords.height) : false; 158 | var windowPoint = this._osdViewer.viewport.viewportToWindowCoordinates(viewportPoint); 159 | 160 | if (viewportOpposite) { 161 | var windowOpposite = this._osdViewer.viewport.viewportToWindowCoordinates(viewportOpposite); 162 | return { x: windowPoint.x - offset.left, y: windowPoint.y - offset.top, width: windowOpposite.x - windowPoint.x + 2, height: windowOpposite.y - windowPoint.y + 2 }; 163 | } else { 164 | return windowPoint; 165 | } 166 | } 167 | 168 | annotorious.mediatypes.openseadragon.OpenSeadragonAnnotator.prototype.getAnnotations = function() { 169 | return this._viewer.getAnnotations(); 170 | } 171 | 172 | 173 | annotorious.mediatypes.openseadragon.OpenSeadragonAnnotator.prototype.getAvailableSelectors = function() { 174 | 175 | } 176 | 177 | annotorious.mediatypes.openseadragon.OpenSeadragonAnnotator.prototype.getItem = function() { 178 | // TODO implement something decent! 179 | return { src: "dzi://openseadragon/something" }; 180 | } 181 | 182 | annotorious.mediatypes.openseadragon.OpenSeadragonAnnotator.prototype.setActiveSelector = function(selector) { 183 | 184 | } 185 | 186 | /** 187 | * Returns the currently active selector. 188 | * @returns {Object} the currently active selector 189 | */ 190 | annotorious.mediatypes.openseadragon.OpenSeadragonAnnotator.prototype.getActiveSelector = function() { 191 | return this._currentSelector; 192 | } 193 | 194 | 195 | annotorious.mediatypes.openseadragon.OpenSeadragonAnnotator.prototype.toItemCoordinates = function(xy) { 196 | var offset = annotorious.dom.getOffset(this.element); 197 | offset.top += window.pageYOffset; 198 | offset.left += window.pageXOffset; 199 | 200 | var viewportPoint = new OpenSeadragon.Point(xy.x + offset.left, xy.y + offset.top); 201 | var viewportOpposite = (xy.width) ? new OpenSeadragon.Point(xy.x + offset.left + xy.width - 2, xy.y + offset.top + xy.height - 2) : false; 202 | var viewElementPoint = this._osdViewer.viewport.windowToViewportCoordinates(viewportPoint); 203 | 204 | if (viewportOpposite) { 205 | var viewElementOpposite = this._osdViewer.viewport.windowToViewportCoordinates(viewportOpposite); 206 | return { x: viewElementPoint.x, y: viewElementPoint.y, 207 | width: viewElementOpposite.x - viewElementPoint.x, 208 | height: viewElementOpposite.y - viewElementPoint.y }; 209 | } else { 210 | return viewElementPoint; 211 | } 212 | } 213 | -------------------------------------------------------------------------------- /src/mediatypes/openseadragon/openseadragon.module.js: -------------------------------------------------------------------------------- 1 | goog.provide('annotorious.mediatypes.openseadragon.OpenSeadragonModule'); 2 | 3 | goog.require('annotorious.mediatypes.Module'); 4 | goog.require('annotorious.mediatypes.openseadragon.OpenSeadragonAnnotator'); 5 | 6 | /** 7 | * The OpenSeadragon Module provides annotation functionality for embedded 8 | * zoomable images displayed with the OpenSeadragon viewer. 9 | * @constructor 10 | * @extends annotorious.mediatypes.Module 11 | */ 12 | annotorious.mediatypes.openseadragon.OpenSeadragonModule = function() { 13 | annotorious.mediatypes.Module.call(); 14 | this._initFields(); 15 | } 16 | goog.inherits(annotorious.mediatypes.openseadragon.OpenSeadragonModule, annotorious.mediatypes.Module); 17 | 18 | /** @inheritDoc **/ 19 | annotorious.mediatypes.openseadragon.OpenSeadragonModule.prototype.getItemURL = function(item) { 20 | // TODO implement something decent! 21 | return 'dzi://openseadragon/something'; 22 | } 23 | 24 | /** @inheritDoc **/ 25 | annotorious.mediatypes.openseadragon.OpenSeadragonModule.prototype.newAnnotator = function(item) { 26 | return new annotorious.mediatypes.openseadragon.OpenSeadragonAnnotator(item); 27 | } 28 | 29 | /** @inheritDoc **/ 30 | annotorious.mediatypes.openseadragon.OpenSeadragonModule.prototype.supports = function(item) { 31 | return item instanceof OpenSeadragon.Viewer; 32 | } 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /src/mediatypes/openseadragon/openseadragon.viewer.js: -------------------------------------------------------------------------------- 1 | goog.provide('annotorious.mediatypes.openseadragon.Viewer'); 2 | 3 | goog.require('goog.events.MouseWheelHandler'); 4 | 5 | /** 6 | * The OpenSeadragon viewer uses the OpenSeadragon overlay API to display annotation shapes. 7 | * @param {Object} osdViewer the OpenSeadragon viewer object 8 | * @param {annotorious.mediatypes.openseadragon.OpenSeadragonAnnotator} annotator reference to the annotator 9 | * @constructor 10 | */ 11 | annotorious.mediatypes.openseadragon.Viewer = function(osdViewer, annotator) { 12 | /** @private **/ 13 | this._osdViewer = osdViewer; 14 | 15 | /** @private **/ 16 | this._annotator = annotator; 17 | 18 | /** @private **/ 19 | this._map_bounds = goog.style.getBounds(osdViewer.element); 20 | 21 | /** @private **/ 22 | this._popup = annotator.popup; 23 | goog.style.setStyle(this._popup.element, 'z-index', 99000); 24 | 25 | /** @private **/ 26 | this._overlays = []; 27 | 28 | /** @private **/ 29 | this._currentlyHighlightedOverlay; 30 | 31 | /** @private **/ 32 | this._lastHoveredOverlay; 33 | 34 | var self = this; 35 | 36 | this._osdViewer.addHandler('animation', function() { 37 | if (self._currentlyHighlightedOverlay) 38 | self._place_popup(); 39 | }); 40 | 41 | annotator.addHandler(annotorious.events.EventType.POPUP_SHOWN, function(target) { 42 | if (self._currentlyHighlightedOverlay !== undefined && self._currentlyHighlightedOverlay != false) { 43 | self._annotator.fireEvent(annotorious.events.EventType.MOUSE_OVER_ANNOTATION, self._currentlyHighlightedOverlay.annotation); 44 | } 45 | }); 46 | 47 | annotator.addHandler(annotorious.events.EventType.BEFORE_POPUP_HIDE, function() { 48 | if (self._lastHoveredOverlay == self._currentlyHighlightedOverlay) { 49 | self._popup.clearHideTimer(); 50 | } else { 51 | self._annotator.fireEvent(annotorious.events.EventType.MOUSE_OUT_OF_ANNOTATION, self._currentlyHighlightedOverlay.annotation); 52 | self._updateHighlight(self._lastHoveredOverlay, self._currentlyHighlightedOverlay); 53 | } 54 | }); 55 | } 56 | 57 | /** 58 | * Resets the position of the popup, without changing the annotation. 59 | */ 60 | annotorious.mediatypes.openseadragon.Viewer.prototype._place_popup = function() { 61 | var viewportEl = this._osdViewer['element']; 62 | 63 | // Compute correct annotation bounds, relative to map 64 | var annotation_div = this._currentlyHighlightedOverlay.outer; 65 | var annotation_dim = goog.style.getBounds(annotation_div); 66 | var annotation_pos = goog.style.getRelativePosition(annotation_div, viewportEl); 67 | var annotation_bounds = { top: annotation_pos.y, 68 | left: annotation_pos.x, 69 | width: annotation_dim.width, 70 | height: annotation_dim.height }; 71 | 72 | // Popup width & height 73 | var popup_bounds = goog.style.getBounds(this._popup.element); 74 | var popup_pos = { x: annotation_bounds.left, y: annotation_bounds.top + annotation_bounds.height + 12 }; 75 | goog.dom.classes.addRemove(this._popup.element, 'top-right', 'top-left'); 76 | 77 | // Don't fix position in full-page mode 78 | if (!this._osdViewer.isFullPage()) { 79 | if (annotation_bounds.left + popup_bounds.width > this._map_bounds.width) { 80 | goog.dom.classes.addRemove(this._popup.element, 'top-left', 'top-right'); 81 | popup_pos.x = (annotation_bounds.left + annotation_bounds.width) - popup_bounds.width; 82 | } else { 83 | 84 | } 85 | 86 | if (popup_pos.x < 0) 87 | popup_pos.x = 0; 88 | 89 | if (popup_pos.x + popup_bounds.width > this._map_bounds.width) 90 | popup_pos.x = this._map_bounds.width - popup_bounds.width; 91 | 92 | if (popup_pos.y + popup_bounds.height > this._map_bounds.height) 93 | popup_pos.y = this._map_bounds.height - popup_bounds.height; 94 | } 95 | 96 | this._popup.setPosition(popup_pos); 97 | } 98 | 99 | /** 100 | * Shows the popup with a new annotation. 101 | * @param {annotorious.Annotation} annotation the annotation 102 | */ 103 | annotorious.mediatypes.openseadragon.Viewer.prototype._show_popup = function(annotation) { 104 | this._popup.setAnnotation(annotation); 105 | this._place_popup(); 106 | this._popup.show(); 107 | } 108 | 109 | /** 110 | * @param {Object=} new_highlight the overlay to highlight 111 | * @param {Object=} previous_highlight the overlay previously highlighted 112 | */ 113 | annotorious.mediatypes.openseadragon.Viewer.prototype._updateHighlight = function(new_highlight, previous_highlight) { 114 | if (new_highlight) { 115 | goog.style.setStyle(new_highlight.inner, 'border-color', '#fff000'); 116 | this._currentlyHighlightedOverlay = new_highlight; 117 | this._show_popup(new_highlight.annotation); 118 | } else { 119 | delete this._currentlyHighlightedOverlay; 120 | } 121 | 122 | if (previous_highlight) { 123 | goog.style.setStyle(previous_highlight.inner, 'border-color', '#fff'); 124 | } 125 | } 126 | 127 | /** 128 | * Adds an annotation to the viewer. 129 | * @param {annotorious.Annotation} annotation the annotation 130 | */ 131 | annotorious.mediatypes.openseadragon.Viewer.prototype.addAnnotation = function(annotation) { 132 | var geometry = annotation.shapes[0].geometry; 133 | var outer = goog.dom.createDom('div', 'annotorious-ol-boxmarker-outer'); 134 | var inner = goog.dom.createDom('div', 'annotorious-ol-boxmarker-inner'); 135 | goog.style.setSize(inner, '100%', '100%'); 136 | goog.dom.appendChild(outer, inner); 137 | 138 | var rect = new OpenSeadragon.Rect(geometry.x, geometry.y, geometry.width, geometry.height); 139 | 140 | var overlay = {annotation: annotation, outer: outer, inner: inner}; 141 | 142 | var self = this; 143 | goog.events.listen(inner, goog.events.EventType.MOUSEOVER, function(event) { 144 | if (!self._currentlyHighlightedOverlay) 145 | self._updateHighlight(overlay); 146 | 147 | self._lastHoveredOverlay = overlay; 148 | }); 149 | 150 | goog.events.listen(inner, goog.events.EventType.MOUSEOUT, function(event) { 151 | delete self._lastHoveredOverlay; 152 | self._popup.startHideTimer(); 153 | }); 154 | 155 | this._overlays.push(overlay); 156 | 157 | goog.array.sort(this._overlays, function(a, b) { 158 | var shapeA = a.annotation.shapes[0]; 159 | var shapeB = b.annotation.shapes[0]; 160 | return annotorious.shape.getSize(shapeB) - annotorious.shape.getSize(shapeA); 161 | }); 162 | 163 | var zIndex = 1; 164 | goog.array.forEach(this._overlays, function(overlay) { 165 | goog.style.setStyle(overlay.outer, 'z-index', zIndex); 166 | zIndex++; 167 | }); 168 | 169 | this._osdViewer.addOverlay(outer, rect); 170 | } 171 | 172 | /** 173 | * Removes an annotation from the viewer. 174 | * @param {annotorious.Annotation} annotation the annotation 175 | */ 176 | annotorious.mediatypes.openseadragon.Viewer.prototype.removeAnnotation = function(annotation) { 177 | var overlay = goog.array.find(this._overlays, function(overlay) { 178 | return overlay.annotation == annotation; 179 | }); 180 | 181 | if (overlay) { 182 | goog.array.remove(this._overlays, overlay); 183 | this._osdViewer.removeOverlay(overlay.outer); 184 | } 185 | } 186 | 187 | /** 188 | * Returns all annotations in this viewer. 189 | * @return {Array.} the annotations 190 | */ 191 | annotorious.mediatypes.openseadragon.Viewer.prototype.getAnnotations = function() { 192 | return goog.array.map(this._overlays, function(overlay) { 193 | console.log(overlay); 194 | return overlay.annotation; 195 | }); 196 | } 197 | 198 | /** 199 | * Highlights a particular annotation in the viewer, or de-highlights (if that's a 200 | * word...) all, if no annotation is passed to the method. 201 | * @param {annotorious.Annotation | undefined} opt_annotation the annotation 202 | */ 203 | annotorious.mediatypes.openseadragon.Viewer.prototype.highlightAnnotation = function(opt_annotation) { 204 | var that = this; 205 | if (opt_annotation) { 206 | goog.array.forEach(this._overlays, function(overlay) { 207 | if (overlay.annotation == opt_annotation) { 208 | if (that._currentlyHighlightedOverlay && 209 | that._currentlyHighlightedOverlay != overlay) { 210 | that._updateHighlight(overlay, that._currentlyHighlightedOverlay); 211 | } else { 212 | that._updateHighlight(overlay); 213 | } 214 | } 215 | }); 216 | } else { 217 | this._popup.startHideTimer(); 218 | } 219 | } 220 | 221 | annotorious.mediatypes.openseadragon.Viewer.prototype.destroy = function () { 222 | var that = this; 223 | goog.array.forEach(this._overlays, function (overlay) { 224 | that._osdViewer.removeOverlay(overlay.outer); 225 | }); 226 | this._overlays = []; 227 | } 228 | -------------------------------------------------------------------------------- /src/okfn/okfn_image_plugin.js: -------------------------------------------------------------------------------- 1 | var humanEvents = annotorious.events.ui.EventType; 2 | 3 | goog.provide('annotorious.okfn.ImagePlugin'); 4 | 5 | goog.require('goog.array'); 6 | goog.require('goog.object'); 7 | goog.require('goog.soy'); 8 | goog.require('goog.dom'); 9 | goog.require('goog.dom.classes'); 10 | goog.require('goog.dom.query'); 11 | goog.require('goog.events'); 12 | goog.require('goog.math'); 13 | goog.require('goog.style'); 14 | 15 | /** 16 | * Implementation of the Yuma image plugin for OKFN Annotator. 17 | * @param {Element} image the image to be annotated 18 | * @param {Object} okfnAnnotator reference to the OKFN Annotator instance 19 | * @constructor 20 | */ 21 | annotorious.okfn.ImagePlugin = function(image, okfnAnnotator) { 22 | var baseOffset = annotorious.dom.getOffset(okfnAnnotator.element[0].firstChild); 23 | 24 | var eventBroker = new annotorious.events.EventBroker(); 25 | 26 | var annotationLayer = goog.dom.createDom('div', 'yuma-annotationlayer'); 27 | goog.style.setStyle(annotationLayer, 'position', 'relative'); 28 | goog.style.setSize(annotationLayer, image.width, image.height); 29 | goog.dom.replaceNode(annotationLayer, image); 30 | goog.dom.appendChild(annotationLayer, image); 31 | 32 | var viewCanvas = goog.soy.renderAsElement(annotorious.templates.image.canvas, 33 | { width:image.width, height:image.height }); 34 | goog.dom.appendChild(annotationLayer, viewCanvas); 35 | 36 | var popup = new annotorious.okfn.Popup(image, okfnAnnotator, eventBroker, baseOffset); 37 | 38 | var editCanvas = goog.soy.renderAsElement(annotorious.templates.image.canvas, 39 | { width:image.width, height:image.height }); 40 | 41 | if (!annotorious.events.ui.hasTouch) { 42 | goog.style.showElement(editCanvas, false); 43 | } 44 | 45 | goog.dom.appendChild(annotationLayer, editCanvas); 46 | 47 | var viewer = new annotorious.mediatypes.image.Viewer(viewCanvas, eventBroker); 48 | 49 | var selector = new annotorious.plugins.selection.RectDragSelector(); 50 | selector.init(editCanvas, eventBroker, viewer); 51 | 52 | 53 | var hint = new annotorious.Hint(eventBroker, annotationLayer); 54 | 55 | // TODO clean up this mess 56 | eventBroker.toItemCoordinates = function(coords) { 57 | return coords; 58 | }; 59 | 60 | eventBroker.fromItemCoordinates = function(coords) { 61 | return coords; 62 | }; 63 | 64 | eventBroker.getAvailableSelectors = function() { 65 | return [ selector ]; 66 | }; 67 | 68 | eventBroker.highlightAnnotation = function(annotation) { 69 | viewer.highlightAnnotation(annotation); 70 | } 71 | 72 | eventBroker.popup = popup; 73 | 74 | /** 75 | * Returns the top z-index annotation based on x and y coordinates 76 | * @param x for viewport x axis value 77 | * @param y for viewport y axis value 78 | * @returns annotation object 79 | */ 80 | eventBroker.getAnnotationsAt = function(x, y) { 81 | return viewer.getAnnotationsAt(x, y); 82 | } 83 | 84 | /** 85 | * Checks if the OKFN Editor is currently 'owned' by this image. I.e. whether 86 | * the current annotation in the editor is an image annotation, and the annotation 87 | * 'url' property matches this wrapper's _image.src. 88 | */ 89 | var isEditorCurrentlyOwned = function() { 90 | var annotation = okfnAnnotator.editor.annotation; 91 | 92 | if (!annotation) 93 | return false; 94 | 95 | return annotation.src == image.src; 96 | }; 97 | 98 | /** 99 | * Checks if the mouseover/out event happened inside the annotatable area. 100 | * Unfortunately Annotator makes this task a little complex... 101 | */ 102 | var isMouseEventInside = function(event) { 103 | var isMouseInside = false, relatedTarget = event.relatedTarget || false; 104 | 105 | // No related target - mouse was inside the annotationLayer on page load 106 | if (!relatedTarget) 107 | isMouseInside = true; 108 | 109 | // Related target is a child of the annotation layer - inside 110 | if (goog.dom.contains(annotationLayer, relatedTarget)) 111 | isMouseInside = true; 112 | 113 | // Related target is part of the Annotator editor - inside 114 | if (goog.dom.contains(okfnAnnotator.editor.element[0], relatedTarget) && isEditorCurrentlyOwned()) 115 | isMouseInside = true; 116 | 117 | // Related target is part of the Annotator popup - inside 118 | if (goog.dom.contains(okfnAnnotator.viewer.element[0], relatedTarget) && popup.isViewerCurrentlyOwned()) 119 | isMouseInside = true; 120 | 121 | if (event.event_ && event.event_.touches) { 122 | isMouseInside = false; 123 | } 124 | 125 | return isMouseInside; 126 | }; 127 | 128 | var self = this; 129 | goog.events.listen(annotationLayer, humanEvents.OVER, function(event) { 130 | if (!isMouseEventInside(event)) 131 | eventBroker.fireEvent(annotorious.events.EventType.MOUSE_OVER_ANNOTATABLE_ITEM); 132 | }); 133 | 134 | goog.events.listen(annotationLayer, humanEvents.OUT, function(event) { 135 | if (!isMouseEventInside(event)) 136 | eventBroker.fireEvent(annotorious.events.EventType.MOUSE_OUT_OF_ANNOTATABLE_ITEM); 137 | }); 138 | 139 | popup.addMouseOverHandler(function(event) { 140 | if (!isMouseEventInside(event)) 141 | eventBroker.fireEvent(annotorious.events.EventType.MOUSE_OVER_ANNOTATABLE_ITEM); 142 | }); 143 | 144 | popup.addMouseOutHandler(function(event) { 145 | if (!isMouseEventInside(event)) 146 | eventBroker.fireEvent(annotorious.events.EventType.MOUSE_OUT_OF_ANNOTATABLE_ITEM); 147 | }); 148 | 149 | goog.events.listen(( (annotorious.events.ui.hasTouch) ? editCanvas : viewCanvas ), humanEvents.DOWN, function(event) { 150 | var points = annotorious.events.ui.sanitizeCoordinates(event, viewCanvas); 151 | 152 | event.preventDefault(); 153 | goog.style.showElement(editCanvas, true); 154 | 155 | viewer.highlightAnnotation(undefined); 156 | selector.startSelection(points.x, points.y); 157 | }); 158 | 159 | eventBroker.addHandler(annotorious.events.EventType.MOUSE_OVER_ANNOTATABLE_ITEM, function() { 160 | okfnAnnotator.clearViewerHideTimer(); // In case the mouse arrives (fast) from an HTML annotation 161 | goog.style.setOpacity(viewCanvas, 1.0); 162 | }); 163 | 164 | eventBroker.addHandler(annotorious.events.EventType.MOUSE_OUT_OF_ANNOTATABLE_ITEM, function() { 165 | goog.style.setOpacity(viewCanvas, 0.4); 166 | }); 167 | 168 | /** Communication yuma -> okfn **/ 169 | 170 | eventBroker.addHandler(annotorious.events.EventType.SELECTION_COMPLETED, function(event) { 171 | var annotation = { src: image.src, shapes: [event.shape] }; 172 | okfnAnnotator.publish('beforeAnnotationCreated', annotation); 173 | 174 | var imgOffset = annotorious.dom.getOffset(image); 175 | var geometry = event.shape.geometry; 176 | var x = geometry.x + imgOffset.left - baseOffset.left + window.pageXOffset + 16; 177 | var y = geometry.y + geometry.height + imgOffset.top + window.pageYOffset - baseOffset.top + 5; 178 | 179 | okfnAnnotator.showEditor(annotation, {top: window.pageYOffset - baseOffset.top, left: 0}); 180 | goog.style.setPosition(okfnAnnotator.editor.element[0], x, y); 181 | }); 182 | 183 | eventBroker.addHandler(annotorious.events.EventType.SELECTION_CANCELED, function() { 184 | if (!annotorious.events.ui.hasTouch) { 185 | goog.style.showElement(editCanvas, false); 186 | } 187 | 188 | selector.stopSelection(); 189 | }); 190 | 191 | /** Communication okfn -> yuma **/ 192 | 193 | okfnAnnotator.viewer.on('edit', function(annotation) { 194 | if (annotation.url == image.src) { 195 | goog.style.showElement(editCanvas, true); 196 | viewer.highlightAnnotation(undefined); 197 | 198 | // TODO code duplication -> move into a function 199 | var imgOffset = annotorious.dom.getOffset(image); 200 | var geometry = annotation.shapes[0].geometry; 201 | var x = geometry.x + imgOffset.left - baseOffset.left + 16; 202 | var y = geometry.y + geometry.height + imgOffset.top - baseOffset.top + window.pageYOffset + 5; 203 | 204 | // Use editor.show instead of showEditor to prevent a second annotationEditorShown event 205 | goog.style.setPosition(okfnAnnotator.editor.element[0], 0, window.pageYOffset - baseOffset.top); 206 | okfnAnnotator.editor.show(); 207 | goog.style.setPosition(okfnAnnotator.editor.element[0], x, y); 208 | } 209 | }); 210 | 211 | okfnAnnotator.subscribe('annotationCreated', function(annotation) { 212 | if (annotation.src == image.src) { 213 | selector.stopSelection(); 214 | if(annotation.src == image.src) 215 | viewer.addAnnotation(goog.object.clone(annotation)); 216 | } 217 | }); 218 | 219 | okfnAnnotator.subscribe('annotationsLoaded', function(annotations) { 220 | goog.array.forEach(annotations, function(annotation) { 221 | if(annotation.src == image.src) { 222 | viewer.addAnnotation(annotation); 223 | } 224 | }); 225 | }); 226 | 227 | okfnAnnotator.subscribe('annotationDeleted', function(annotation) { 228 | if(annotation.src == image.src) { 229 | viewer.removeAnnotation(annotation); 230 | } 231 | 232 | // Annotator silently closes the popup - so we need to fire the event manually afterwards 233 | eventBroker.fireEvent(annotorious.events.EventType.BEFORE_POPUP_HIDE); 234 | }); 235 | 236 | okfnAnnotator.subscribe('annotationEditorHidden', function(editor) { 237 | if (!annotorious.events.ui.hasTouch) { 238 | goog.style.showElement(editCanvas, false); 239 | } 240 | 241 | selector.stopSelection(); 242 | 243 | // TODO workaround before we have decent 'edit' behavior in Annotorious standalone! 244 | eventBroker.fireEvent(annotorious.events.EventType.BEFORE_POPUP_HIDE); 245 | }); 246 | } 247 | 248 | /** 249 | * OKFN plugin interface. 250 | */ 251 | window['Annotator']['Plugin']['AnnotoriousImagePlugin'] = (function() { 252 | 253 | function AnnotoriousImagePlugin(element, options) { 254 | this._el = element; 255 | } 256 | 257 | AnnotoriousImagePlugin.prototype['pluginInit'] = function() { 258 | var images = this._el.getElementsByTagName('img'); 259 | var self = this; 260 | goog.array.forEach(images, function(img, idx, array) { 261 | new annotorious.okfn.ImagePlugin(img, self['annotator']); 262 | }); 263 | } 264 | 265 | return AnnotoriousImagePlugin; 266 | })(); 267 | 268 | -------------------------------------------------------------------------------- /src/okfn/okfn_popup.js: -------------------------------------------------------------------------------- 1 | // var humanEvents = annotorious.events.ui.EventType; 2 | 3 | goog.provide('annotorious.okfn.Popup'); 4 | 5 | goog.require('goog.array'); 6 | goog.require('goog.style'); 7 | goog.require('goog.dom.classes'); 8 | 9 | /** 10 | * A wrapper around the OKFN viewer popup, mimicking the Annotorious Popup. 11 | * @param {element} image the image 12 | * @param {annotorious.events.EventBroker} eventBroker reference to the Yuma EventBroker 13 | * @param {Annotator} okfnAnnotator reference to the OKFN Annotator 14 | * @param {Object} the base offset of the annotatable DOM element 15 | * @constructor 16 | */ 17 | annotorious.okfn.Popup = function(image, okfnAnnotator, eventBroker, baseOffset) { 18 | /** @private **/ 19 | this._image = image; 20 | 21 | /** @private **/ 22 | this._okfnAnnotator = okfnAnnotator; 23 | 24 | /** @private **/ 25 | this._eventBroker = eventBroker; 26 | 27 | /** @private **/ 28 | this._baseOffset = baseOffset; 29 | 30 | /** @private **/ 31 | this._popupHideTimer; 32 | 33 | /** @private **/ 34 | this._cancelHide = false; 35 | 36 | /** @private **/ 37 | this._mouseoverHandlers = []; 38 | 39 | /** @private **/ 40 | this._mouseoutHandlers = []; 41 | 42 | var self = this; 43 | goog.events.listen(this._okfnAnnotator.viewer.element[0], humanEvents.OVER, function(event) { 44 | if (self.isViewerCurrentlyOwned()) { 45 | self.clearHideTimer() 46 | goog.array.forEach(self._mouseoverHandlers, function(handler) { 47 | handler(event); 48 | }); 49 | } 50 | }); 51 | 52 | goog.events.listen(this._okfnAnnotator.viewer.element[0], humanEvents.OUT, function(event) { 53 | if (self.isViewerCurrentlyOwned()) { 54 | okfnAnnotator.clearViewerHideTimer(); // Switch off Annotator's own fade out 55 | self.startHideTimer(); 56 | goog.array.forEach(self._mouseoutHandlers, function(handler) { 57 | handler(event); 58 | }); 59 | } 60 | }); 61 | } 62 | 63 | /** 64 | * Utility method that tests if the viewer is currently 'owned' by the image that this 65 | * popup wrapper is responsible for. I.e. whether the (first) current annotation in the 66 | * viewer is an image annotation, and the annotation 'url' property matches this wrapper's 67 | * _image.src. 68 | * @returns {boolean} true if the viewer is currently owned by this wrapper 69 | */ 70 | annotorious.okfn.Popup.prototype.isViewerCurrentlyOwned = function() { 71 | var annotations = this._okfnAnnotator.viewer.annotations; 72 | 73 | if (!annotations) 74 | return false; 75 | 76 | if (annotations.length < 1) 77 | return false; 78 | 79 | return annotations[0].src == this._image.src; 80 | } 81 | 82 | /** 83 | * Adds a mouseover event handler to this popup wrapper. Note that this handler 84 | * will _not_ be invoked on all mouseover events that happen on the underlying 85 | * Annotator popup, but _only_ on events that happen while the Annotator popup 86 | * is "owned" by this wrapper. (I.e. when the popup contains an annotation that 87 | * belongs to the same image as this popup wrapper.) 88 | */ 89 | annotorious.okfn.Popup.prototype.addMouseOverHandler = function(handler) { 90 | this._mouseoverHandlers.push(handler); 91 | } 92 | 93 | /** 94 | * Adds a mouseout event handler to this popup wrapper. Note that this handler 95 | * will _not_ be invoked on all mouseout events that happen on the underlying 96 | * Annotator popup, but _only_ on events that happen while the Annotator popup 97 | * is "owned" by this wrapper. (I.e. when the popup contains an annotation that 98 | * belongs to the same image as this popup wrapper.) 99 | */ 100 | annotorious.okfn.Popup.prototype.addMouseOutHandler = function(handler) { 101 | this._mouseoutHandlers.push(handler); 102 | } 103 | 104 | /** 105 | * Start the popup hide timer. 106 | */ 107 | annotorious.okfn.Popup.prototype.startHideTimer = function() { 108 | if (!goog.dom.classes.has(this._okfnAnnotator.viewer.element[0], 'annotator-hide')) { 109 | this._cancelHide = false; 110 | if (!this._popupHideTimer) { 111 | var self = this; 112 | this._popupHideTimer = window.setTimeout(function() { 113 | self._eventBroker.fireEvent(annotorious.events.EventType.BEFORE_POPUP_HIDE); 114 | if (!self._cancelHide && self.isViewerCurrentlyOwned()) { 115 | goog.dom.classes.add(self._okfnAnnotator.viewer.element[0], 'annotator-hide'); 116 | self._okfnAnnotator.viewer.annotations = []; 117 | delete self._popupHideTimer; 118 | } 119 | }, 300); 120 | } 121 | } 122 | } 123 | 124 | /** 125 | * Clear the popup hide timer. 126 | */ 127 | annotorious.okfn.Popup.prototype.clearHideTimer = function() { 128 | this._cancelHide = true; 129 | if (this._popupHideTimer) { 130 | window.clearTimeout(this._popupHideTimer); 131 | delete this._popupHideTimer; 132 | } 133 | } 134 | 135 | /** 136 | * Show the popup, loaded with the specified annotation, at the specified coordinates. 137 | * @param {Object} annotation the annotation 138 | * @param {annotorious.geom.Point} xy the viewport coordinate (relative to the image) 139 | */ 140 | annotorious.okfn.Popup.prototype.show = function(annotation, xy) { 141 | goog.dom.classes.remove(this._okfnAnnotator.viewer.element[0], 'annotator-hide'); 142 | 143 | var imgOffset = annotorious.dom.getOffset(this._image); 144 | 145 | goog.style.setPosition(this._okfnAnnotator.viewer.element[0], 0, window.pageYOffset - this._baseOffset.top); 146 | this._okfnAnnotator.viewer.load([annotation]); 147 | goog.style.setPosition(this._okfnAnnotator.viewer.element[0], 148 | imgOffset.left - this._baseOffset.left + xy.x + 16, 149 | imgOffset.top + window.pageYOffset - this._baseOffset.top + xy.y); 150 | this.clearHideTimer(); 151 | } 152 | 153 | /** 154 | * Set the position of the popup. 155 | * @param {number} x the viewport X coordinate (relative to the image) 156 | * @param {number} y the viewport Y coordinate (relative to the image) 157 | */ 158 | annotorious.okfn.Popup.prototype.setPosition = function(x, y) { 159 | goog.style.setPosition(this._okfnAnnotator.viewer.element[0], x, y); 160 | } 161 | -------------------------------------------------------------------------------- /src/popup.js: -------------------------------------------------------------------------------- 1 | goog.provide('annotorious.Popup'); 2 | 3 | goog.require('goog.dom'); 4 | goog.require('goog.dom.classes'); 5 | goog.require('goog.dom.query'); 6 | goog.require('goog.style'); 7 | 8 | /** 9 | * A popup bubble widget to show annotation details. 10 | * @param {Object} annotator reference to the annotator 11 | * @constructor 12 | */ 13 | annotorious.Popup = function(annotator) { 14 | this.element = goog.soy.renderAsElement(annotorious.templates.popup); 15 | 16 | /** @private **/ 17 | this._annotator = annotator; 18 | 19 | /** @private **/ 20 | this._currentAnnotation; 21 | 22 | /** @private **/ 23 | this._text = goog.dom.query('.annotorious-popup-text', this.element)[0]; 24 | 25 | /** @private **/ 26 | this._buttons = goog.dom.query('.annotorious-popup-buttons', this.element)[0]; 27 | 28 | /** @private **/ 29 | this._popupHideTimer; 30 | 31 | /** @private **/ 32 | this._buttonHideTimer; 33 | 34 | /** @private **/ 35 | this._cancelHide = false; 36 | 37 | /** @private **/ 38 | this._extraFields = []; 39 | 40 | var btnEdit = goog.dom.query('.annotorious-popup-button-edit', this._buttons)[0]; 41 | var btnDelete = goog.dom.query('.annotorious-popup-button-delete', this._buttons)[0]; 42 | 43 | var self = this; 44 | goog.events.listen(btnEdit, goog.events.EventType.MOUSEOVER, function(event) { 45 | goog.dom.classes.add(btnEdit, 'annotorious-popup-button-active'); 46 | }); 47 | 48 | goog.events.listen(btnEdit, goog.events.EventType.MOUSEOUT, function() { 49 | goog.dom.classes.remove(btnEdit, 'annotorious-popup-button-active'); 50 | }); 51 | 52 | goog.events.listen(btnEdit, goog.events.EventType.CLICK, function(event) { 53 | goog.style.setOpacity(self.element, 0); 54 | goog.style.setStyle(self.element, 'pointer-events', 'none'); 55 | annotator.editAnnotation(self._currentAnnotation); 56 | }); 57 | 58 | goog.events.listen(btnDelete, goog.events.EventType.MOUSEOVER, function(event) { 59 | goog.dom.classes.add(btnDelete, 'annotorious-popup-button-active'); 60 | }); 61 | 62 | goog.events.listen(btnDelete, goog.events.EventType.MOUSEOUT, function() { 63 | goog.dom.classes.remove(btnDelete, 'annotorious-popup-button-active'); 64 | }); 65 | 66 | goog.events.listen(btnDelete, goog.events.EventType.CLICK, function(event) { 67 | var cancelEvent = annotator.fireEvent(annotorious.events.EventType.BEFORE_ANNOTATION_REMOVED, self._currentAnnotation); 68 | if (!cancelEvent) { 69 | goog.style.setOpacity(self.element, 0); 70 | goog.style.setStyle(self.element, 'pointer-events', 'none'); 71 | annotator.removeAnnotation(self._currentAnnotation); 72 | annotator.fireEvent(annotorious.events.EventType.ANNOTATION_REMOVED, self._currentAnnotation); 73 | } 74 | }); 75 | 76 | if (annotorious.events.ui.hasMouse) { 77 | goog.events.listen(this.element, goog.events.EventType.MOUSEOVER, function(event) { 78 | window.clearTimeout(self._buttonHideTimer); 79 | if (goog.style.getStyle(self._buttons, 'opacity') < 0.9) 80 | goog.style.setOpacity(self._buttons, 0.9); 81 | self.clearHideTimer(); 82 | }); 83 | 84 | goog.events.listen(this.element, goog.events.EventType.MOUSEOUT, function(event) { 85 | goog.style.setOpacity(self._buttons, 0); 86 | self.startHideTimer(); 87 | }); 88 | 89 | annotator.addHandler(annotorious.events.EventType.MOUSE_OUT_OF_ANNOTATABLE_ITEM, function(event) { 90 | self.startHideTimer(); 91 | }); 92 | } 93 | 94 | goog.style.setOpacity(this._buttons, 0); 95 | goog.style.setOpacity(this.element, 0); 96 | goog.style.setStyle(this.element, 'pointer-events', 'none'); 97 | goog.dom.appendChild(annotator.element, this.element); 98 | } 99 | 100 | /** 101 | * Adds a field to the popup GUI widget. A field can be either an (HTML) string, or 102 | * a function that takes an Annotation as argument and returns an (HTML) string or 103 | * a DOM element. 104 | * @param {string | Function} field the field 105 | */ 106 | annotorious.Popup.prototype.addField = function(field) { 107 | var fieldEl = goog.dom.createDom('div', 'annotorious-popup-field'); 108 | 109 | if (goog.isString(field)) { 110 | fieldEl.innerHTML = field; 111 | } else if (goog.isFunction(field)) { 112 | this._extraFields.push({el: fieldEl, fn: field}); 113 | } else if (goog.dom.isElement(field)) { 114 | goog.dom.appendChild(fieldEl, field); 115 | } 116 | 117 | goog.dom.appendChild(this.element, fieldEl); 118 | } 119 | 120 | /** 121 | * Start the popup hide timer. 122 | */ 123 | annotorious.Popup.prototype.startHideTimer = function() { 124 | this._cancelHide = false; 125 | if (!this._popupHideTimer) { 126 | var self = this; 127 | this._popupHideTimer = window.setTimeout(function() { 128 | self._annotator.fireEvent(annotorious.events.EventType.BEFORE_POPUP_HIDE, self); 129 | if (!self._cancelHide) { 130 | goog.style.setOpacity(self.element, 0.0); 131 | goog.style.setStyle(self.element, 'pointer-events', 'none'); 132 | goog.style.setOpacity(self._buttons, 0.9); 133 | delete self._popupHideTimer; 134 | } 135 | }, 150); 136 | } 137 | } 138 | 139 | /** 140 | * Clear the popup hide timer. 141 | */ 142 | annotorious.Popup.prototype.clearHideTimer = function() { 143 | this._cancelHide = true; 144 | if (this._popupHideTimer) { 145 | window.clearTimeout(this._popupHideTimer); 146 | delete this._popupHideTimer; 147 | } 148 | } 149 | 150 | /** 151 | * Show the popup, loaded with the specified annotation, at the specified coordinates. 152 | * @param {annotorious.Annotation} annotation the annotation 153 | * @param {annotorious.shape.geom.Point} xy the viewport coordinate 154 | */ 155 | annotorious.Popup.prototype.show = function(annotation, xy) { 156 | this.clearHideTimer(); 157 | 158 | if (xy) 159 | this.setPosition(xy); 160 | 161 | if (annotation) 162 | this.setAnnotation(annotation); 163 | 164 | if (this._buttonHideTimer) 165 | window.clearTimeout(this._buttonHideTimer); 166 | 167 | goog.style.setOpacity(this._buttons, 0.9); 168 | 169 | if (annotorious.events.ui.hasMouse) { 170 | var self = this; 171 | this._buttonHideTimer = window.setTimeout(function() { 172 | goog.style.setOpacity(self._buttons, 0); 173 | }, 1000); 174 | } 175 | 176 | goog.style.setOpacity(this.element, 0.9); 177 | goog.style.setStyle(this.element, 'pointer-events', 'auto'); 178 | this._annotator.fireEvent(annotorious.events.EventType.POPUP_SHOWN, this._currentAnnotation); 179 | } 180 | 181 | /** 182 | * Set the position of the popup. 183 | * @param {annotorious.shape.geom.Point} xy the viewport coordinate 184 | */ 185 | annotorious.Popup.prototype.setPosition = function(xy) { 186 | goog.style.setPosition(this.element, new goog.math.Coordinate(xy.x, xy.y)); 187 | } 188 | 189 | /** 190 | * Set the annotation for the popup. 191 | * @param {annotorious.Annotation} annotation the annotation 192 | */ 193 | annotorious.Popup.prototype.setAnnotation = function(annotation) { 194 | this._currentAnnotation = annotation; 195 | if (annotation.text) 196 | this._text.innerHTML = annotation.text.replace(/\n/g, '
'); 197 | else 198 | this._text.innerHTML = 'No comment'; 199 | 200 | if (('editable' in annotation) && annotation.editable == false) 201 | goog.style.showElement(this._buttons, false); 202 | else 203 | goog.style.showElement(this._buttons, true); 204 | 205 | // Update extra fields (if any) 206 | goog.array.forEach(this._extraFields, function(field) { 207 | var f = field.fn(annotation); 208 | if (goog.isString(f)) { 209 | field.el.innerHTML = f; 210 | } else if (goog.dom.isElement(f)) { 211 | goog.dom.removeChildren(field.el); 212 | goog.dom.appendChild(field.el, f); 213 | } 214 | }); 215 | } 216 | 217 | /** API exports **/ 218 | annotorious.Popup.prototype['addField'] = annotorious.Popup.prototype.addField; 219 | -------------------------------------------------------------------------------- /src/shape/point.js: -------------------------------------------------------------------------------- 1 | goog.provide('annotorious.shape.geom.Point'); 2 | 3 | /** 4 | * A point. 5 | * @param {number} x the x coordinate 6 | * @param {number} y the y coordinate 7 | * @constructor 8 | */ 9 | annotorious.shape.geom.Point = function(x, y) { 10 | this.x = x; 11 | this.y = y; 12 | } 13 | 14 | -------------------------------------------------------------------------------- /src/shape/polygon.js: -------------------------------------------------------------------------------- 1 | goog.provide('annotorious.shape.geom.Polygon'); 2 | 3 | goog.require('annotorious.shape.geom.Point'); 4 | 5 | /** 6 | * A polygon. 7 | * @param {Array.} points the points 8 | * @constructor 9 | */ 10 | annotorious.shape.geom.Polygon = function(points) { 11 | this.points = points; 12 | } 13 | 14 | 15 | /** Polygon-specific helper functions & geometry computation utilities **/ 16 | 17 | 18 | /** 19 | * Computes the area of a polygon. Note that the area can be <0, depending on the 20 | * clockwise/counterclockwise orientation of the polygon vertices! 21 | * @param {Array.} points the points 22 | * @return {number} the area 23 | */ 24 | annotorious.shape.geom.Polygon.computeArea = function(points) { 25 | var area = 0.0; 26 | 27 | var j = points.length - 1; 28 | for (var i=0; i} points the points 39 | * @return {boolean} true if the geometry is in clockwise orientation 40 | */ 41 | annotorious.shape.geom.Polygon.isClockwise = function(points) { 42 | return annotorious.shape.geom.Polygon.computeArea(points) < 0; 43 | } 44 | 45 | /** 46 | * Computes the centroid coordinate of a polygon. 47 | * @param {Array.} points the points 48 | * @returns {annotorious.shape.geom.Point} the centroid X/Y coordinate 49 | */ 50 | annotorious.shape.geom.Polygon.computeCentroid = function(points) { 51 | var x = 0; 52 | var y = 0; 53 | var f; 54 | var j = points.length - 1; 55 | 56 | for (var i=0; ivertex. Used internally as a subroutine for polygon expansion. 70 | * @param {Array.} points the points 71 | * @return {Array.} the expanded triangle 72 | * @private 73 | */ 74 | annotorious.shape.geom.Polygon._expandTriangle = function(points, delta) { 75 | function signum(number) { 76 | return number > 0 ? 1 : number < 0 ? -1 : 0; 77 | } 78 | 79 | function shiftAlongAxis(px, centroid, delta) { 80 | var axis = { x: (px.x - centroid.x) , y: (px.y - centroid.y) }; 81 | var sign_delta = signum(delta); 82 | var sign_x = signum(axis.x) * sign_delta; 83 | var sign_y = signum(axis.y) * sign_delta; 84 | 85 | var dy = Math.sqrt(Math.pow(delta, 2) / (1 + Math.pow((axis.x / axis.y), 2))); 86 | var dx = (axis.x / axis.y) * dy; 87 | return { x: px.x + Math.abs(dx) * sign_x, y: px.y + Math.abs(dy) * sign_y }; 88 | } 89 | 90 | var centroid = annotorious.shape.geom.Polygon.computeCentroid(points); 91 | var expanded = []; 92 | 93 | for (var i=0; i} points the points 105 | * @param {number} delta the distance by which to expand 106 | * @return {Array.} the expanded polygon 107 | */ 108 | annotorious.shape.geom.Polygon.expandPolygon = function(points, delta) { 109 | var sign = (annotorious.shape.geom.Polygon.isClockwise(points)) ? -1 : 1; 110 | 111 | if (points.length < 4) 112 | return annotorious.shape.geom.Polygon._expandTriangle(points, sign * delta); 113 | 114 | var prev = points.length - 1; 115 | var next = 1; 116 | 117 | var expanded = []; 118 | for (var current = 0; current points.length - 1) 124 | next = 0; 125 | } 126 | 127 | return expanded; 128 | } 129 | -------------------------------------------------------------------------------- /src/shape/rectangle.js: -------------------------------------------------------------------------------- 1 | goog.provide('annotorious.shape.geom.Rectangle'); 2 | 3 | /** 4 | * A rectangle. 5 | * @param {number} x the anchor point x coordinate 6 | * @param {number} y the anchor point y coordinate 7 | * @param {number} width the rectangle width 8 | * @param {number} height the rectangle height 9 | * @constructor 10 | */ 11 | annotorious.shape.geom.Rectangle = function(x, y, width, height) { 12 | if (width > 0) { 13 | this.x = x; 14 | this.width = width; 15 | } else { 16 | this.x = x + width; 17 | this.width = -width; 18 | } 19 | 20 | if (height > 0) { 21 | this.y = y; 22 | this.height = height; 23 | } else { 24 | this.y = y + height; 25 | this.height = -height; 26 | } 27 | } 28 | 29 | -------------------------------------------------------------------------------- /src/shape/shape.js: -------------------------------------------------------------------------------- 1 | goog.provide('annotorious.shape'); 2 | 3 | goog.require('annotorious.shape.geom.Polygon'); 4 | goog.require('annotorious.shape.geom.Rectangle'); 5 | 6 | /** 7 | * A shape. Consists of descriptive shape metadata, plus the actual shape geometry. 8 | * @param {annotorious.shape.ShapeType} type the shape type 9 | * @param {annotorious.shape.geom.Point | annotorious.shape.geom.Rectangle | annotorious.shape.geom.Polygon} geometry the geometry 10 | * @param {annotorious.shape.Units=} units geometry measurement units 11 | * @param {Object} drawing style of the shape (optional) 12 | * @constructor 13 | */ 14 | annotorious.shape.Shape = function(type, geometry, units, style) { 15 | this.type = type 16 | this.geometry = geometry; 17 | if (units) 18 | this.units = units; 19 | if (style) 20 | this.style = style; 21 | else 22 | this.style = {}; 23 | } 24 | 25 | /** 26 | * Possible shape types 27 | * @enum {string} 28 | */ 29 | annotorious.shape.ShapeType = { 30 | POINT: 'point', 31 | RECTANGLE: 'rect', 32 | POLYGON: 'polygon' 33 | } 34 | 35 | /** 36 | * Possible unit types 37 | * @enum {string} 38 | */ 39 | annotorious.shape.Units = { 40 | PIXEL: 'pixel', 41 | FRACTION: 'fraction' 42 | } 43 | 44 | 45 | /** Helper functions & geometry computation utilities **/ 46 | 47 | 48 | /** 49 | * Checks whether a given shape intersects a point. 50 | * @param {annotorious.shape.Shape} shape the shape 51 | * @param {number} px the X coordinate 52 | * @param {number} py the Y coordinate 53 | * @return {boolean} true if the point intersects the shape 54 | */ 55 | annotorious.shape.intersects = function(shape, px, py) { 56 | if (shape.type == annotorious.shape.ShapeType.RECTANGLE) { 57 | if (px < shape.geometry.x) 58 | return false; 59 | 60 | if (py < shape.geometry.y) 61 | return false; 62 | 63 | if (px > shape.geometry.x + shape.geometry.width) 64 | return false; 65 | 66 | if (py > shape.geometry.y + shape.geometry.height) 67 | return false; 68 | 69 | return true; 70 | } else if (shape.type == annotorious.shape.ShapeType.POLYGON) { 71 | var points = shape.geometry.points; 72 | var inside = false; 73 | 74 | var j = points.length - 1; 75 | for (var i=0; i py) != (points[j].y > py) && 77 | (px < (points[j].x - points[i].x) * (py - points[i].y) / (points[j].y-points[i].y) + points[i].x)) { 78 | inside = !inside; 79 | } 80 | j = i; 81 | } 82 | 83 | return inside; 84 | } 85 | 86 | return false; 87 | } 88 | 89 | /** 90 | * Returns the size of a shape. 91 | * @param {annotorious.shape.Shape} shape the shape 92 | * @return {number} the size 93 | */ 94 | annotorious.shape.getSize = function(shape) { 95 | if (shape.type == annotorious.shape.ShapeType.RECTANGLE) { 96 | return shape.geometry.width * shape.geometry.height; 97 | } else if (shape.type == annotorious.shape.ShapeType.POLYGON) { 98 | return Math.abs(annotorious.shape.geom.Polygon.computeArea(shape.geometry.points)); 99 | } 100 | return 0; 101 | } 102 | 103 | /** 104 | * Returns the bounding rectangle of a shape. 105 | * @param {annotorious.shape.Shape} shape the shape 106 | * @return {annotorious.shape.Shape | undefined} the bounding rectangle 107 | */ 108 | annotorious.shape.getBoundingRect = function(shape) { 109 | if (shape.type == annotorious.shape.ShapeType.RECTANGLE) { 110 | return shape; 111 | } else if (shape.type == annotorious.shape.ShapeType.POLYGON) { 112 | var points = shape.geometry.points; 113 | 114 | var left = points[0].x; 115 | var right = points[0].x; 116 | var top = points[0].y; 117 | var bottom = points[0].y; 118 | 119 | for (var i=1; i right) 121 | right = points[i].x; 122 | 123 | if (points[i].x < left) 124 | left = points[i].x; 125 | 126 | if (points[i].y > bottom) 127 | bottom = points[i].y; 128 | 129 | if (points[i].y < top) 130 | top = points[i].y; 131 | } 132 | 133 | return new annotorious.shape.Shape(annotorious.shape.ShapeType.RECTANGLE, 134 | new annotorious.shape.geom.Rectangle(left, top, right - left, bottom - top), 135 | false, shape.style 136 | ); 137 | } 138 | 139 | return undefined; 140 | } 141 | 142 | /** 143 | * Computes the centroid coordinate for the specified shape. 144 | * @param {annotorious.shape.Shape} shape the shape 145 | * @returns {annotorious.shape.geom.Point | undefined} the centroid X/Y coordinate 146 | */ 147 | annotorious.shape.getCentroid = function(shape) { 148 | if (shape.type == annotorious.shape.ShapeType.RECTANGLE) { 149 | var rect = shape.geometry; 150 | return new annotorious.shape.geom.Point(rect.x + rect.width / 2, rect.y + rect.height / 2); 151 | } else if (shape.type == annotorious.shape.ShapeType.POLYGON) { 152 | return annotorious.shape.geom.Polygon.computeCentroid( shape.geometry.points); 153 | } 154 | 155 | return undefined; 156 | } 157 | 158 | /** 159 | * Expands a shape by a specified delta. 160 | * @param {annotorious.shape.Shape} shape the shape 161 | * @param {number} delta the delta 162 | */ 163 | annotorious.shape.expand = function(shape, delta) { 164 | // TODO for the sake of completeness: implement for RECTANGLE 165 | return new annotorious.shape.Shape(annotorious.shape.ShapeType.POLYGON, 166 | new annotorious.shape.geom.Polygon(annotorious.shape.geom.Polygon.expandPolygon(shape.geometry.points, delta)), 167 | false, shape.style); 168 | } 169 | 170 | /** 171 | * Transforms a shape from a source coordinate system to a destination coordinate 172 | * system. The transformation is calculated using the transformationFn parameter, 173 | * which must be a function(xy) that transforms a single XY coordinate. 174 | * @param {annotorious.shape.Shape} shape the shape to transform 175 | * @param {Function} transformationFn the transformation function 176 | * @return {annotorious.shape.Shape | undefined} the transformed shape 177 | */ 178 | annotorious.shape.transform = function(shape, transformationFn) { 179 | if (shape.type == annotorious.shape.ShapeType.RECTANGLE) { 180 | var geom = shape.geometry; 181 | var transformed = transformationFn(geom); 182 | return new annotorious.shape.Shape(annotorious.shape.ShapeType.RECTANGLE, transformed, false, shape.style); 183 | } else if (shape.type == annotorious.shape.ShapeType.POLYGON) { 184 | var transformedPoints = []; 185 | goog.array.forEach(shape.geometry.points, function(pt) { 186 | transformedPoints.push(transformationFn(pt)); 187 | }); 188 | return new annotorious.shape.Shape(annotorious.shape.ShapeType.POLYGON, 189 | new annotorious.shape.geom.Polygon(transformedPoints), 190 | false, shape.style 191 | ); 192 | } 193 | 194 | return undefined; 195 | } 196 | 197 | /** 198 | * Computes a 'hashCode' for the specified shape. Not the nicest (and most performat?) 199 | * way to do it. But we need a useful .toString kind-of fuctionality to use for hashtable 200 | * keys in the viewer! 201 | * @param {annotorious.shape.Shape} shape the shape 202 | * @return {string} a 'hashcode' for the shape 203 | */ 204 | annotorious.shape.hashCode = function(shape) { 205 | return JSON.stringify(shape.geometry); 206 | } 207 | -------------------------------------------------------------------------------- /standalone.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "annotorious", 3 | "define": { 4 | "goog.DEBUG": false, 5 | "goog.dom.ASSUME_STANDARDS_MODE": true 6 | }, 7 | "inputs": [ 8 | "src/annotorious.js", 9 | "src/api.js" // exports JS API and sets up the plugin namespace 10 | ], 11 | "paths": [ 12 | "src", 13 | "templates" 14 | ], 15 | "externs": [ 16 | "externs/api.externs.js", 17 | "externs/openlayers.externs.js", 18 | "externs/openseadragon.externs.js", 19 | "externs/jquery.externs.js", 20 | "//json.js", 21 | "//webkit_console.js" 22 | ], 23 | "mode": "ADVANCED", // "RAW" or "ADVANCED" 24 | "level": "DEFAULT", // "DEFAULT" or "VERBOSE" 25 | "pretty-print": false, 26 | "debug": false 27 | } 28 | -------------------------------------------------------------------------------- /templates/core_elements.soy: -------------------------------------------------------------------------------- 1 | {namespace annotorious.templates} 2 | 3 | /** 4 | * A dummy element to be used for annotation popups later on. 5 | */ 6 | {template .popup} 7 |
8 |
9 | EDIT 10 | DELETE 11 |
12 | 13 |
14 | {/template} 15 | 16 | 17 | /** 18 | * A basic annotation edit text form. 19 | */ 20 | {template .editform} 21 |
22 |
23 | 24 |
25 | Cancel 26 | Save 27 |
28 |
29 |
30 | {/template} 31 | -------------------------------------------------------------------------------- /templates/image_elements.soy: -------------------------------------------------------------------------------- 1 | {namespace annotorious.templates.image} 2 | 3 | /** 4 | * The image annotation canvas template 5 | * @param width 6 | * @param height 7 | */ 8 | {template .canvas} 9 | 10 | {/template} 11 | 12 | /** 13 | * The 'hint' element. 14 | * @param msg 15 | */ 16 | {template .hint} 17 |
18 |
{$msg}
19 |
20 |
21 | {/template} 22 | 23 | -------------------------------------------------------------------------------- /templates/openlayers_elements.soy: -------------------------------------------------------------------------------- 1 | {namespace annotorious.templates.openlayers} 2 | 3 | /** 4 | * The secondary 'Click and Drag' hint 5 | * @param msg 6 | */ 7 | {template .secondaryHint} 8 |
9 |
{$msg}
10 |
11 | {/template} 12 | -------------------------------------------------------------------------------- /test/image/371px-Claudius-Ptolemaeus.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/annotorious/annotorious-v1/3fb5a931428a3bc9e3f48bc905ab042a393d0907/test/image/371px-Claudius-Ptolemaeus.jpg -------------------------------------------------------------------------------- /test/image/630px-Ptolemaic-Map.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/annotorious/annotorious-v1/3fb5a931428a3bc9e3f48bc905ab042a393d0907/test/image/630px-Ptolemaic-Map.jpg -------------------------------------------------------------------------------- /test/image/640px-Hallstatt.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/annotorious/annotorious-v1/3fb5a931428a3bc9e3f48bc905ab042a393d0907/test/image/640px-Hallstatt.jpg -------------------------------------------------------------------------------- /test/image/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 29 | 56 | 57 | 58 | 59 |
60 |

Annotation Test Page

61 |

62 | This page serves as a development environment for the Annotorious JavaScript 63 | image annotation library. Hover over one of the annotatable images to get started. 64 | NOTE: make sure the plovr build tool is running! Use 65 |

66 |

67 | java -jar plovr/plovr.jar serve standalone.json 68 |

69 | to start plovr. 70 |

71 | 72 | 73 | 74 |

Test Images

75 |
76 | 77 |

78 | Hallstatt, Austria. By Nick Csakany, 2007. Public Domain. Source: 79 | Wikimedia 80 | Commons 81 |

82 |
83 |

84 | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Ut varius 85 | diam posuere quam molestie vestibulum. Vestibulum non volutpat elit. Integer 86 | vitae felis eget magna rutrum sagittis. Nulla facilisi. Praesent a consectetur 87 | velit. Cras eget nibh est, eu imperdiet mauris. Nulla quis justo urna. Sed eu 88 | rutrum mauris. Integer aliquet nulla sit amet ante mollis pellentesque. 89 | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Ut varius 90 | diam posuere quam molestie vestibulum. Vestibulum non volutpat elit. Integer 91 | vitae felis eget magna rutrum sagittis. Nulla facilisi. Praesent a consectetur 92 | velit. Cras eget nibh est, eu imperdiet mauris. Nulla quis justo urna. Sed eu 93 | rutrum mauris. Integer aliquet nulla sit amet ante mollis pellentesque. 94 | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Ut varius 95 | diam posuere quam molestie vestibulum. Vestibulum non volutpat elit. Integer 96 | vitae felis eget magna rutrum sagittis. Nulla facilisi. Praesent a consectetur 97 | velit. Cras eget nibh est, eu imperdiet mauris. Nulla quis justo urna. Sed eu 98 | rutrum mauris. Integer aliquet nulla sit amet ante mollis pellentesque. 99 |

100 |
101 | 102 |

103 | Medival Ideal Portrait of Ptolemy. Public Domain. Source: 104 | Wikimedia 105 | Commons 106 |

107 |
108 |

109 | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Ut varius 110 | diam posuere quam molestie vestibulum. Vestibulum non volutpat elit. Integer 111 | vitae felis eget magna rutrum sagittis. Nulla facilisi. Praesent a consectetur 112 | velit. Cras eget nibh est, eu imperdiet mauris. Nulla quis justo urna. Sed eu 113 | rutrum mauris. Integer aliquet nulla sit amet ante mollis pellentesque. 114 | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Ut varius 115 | diam posuere quam molestie vestibulum. Vestibulum non volutpat elit. Integer 116 | vitae felis eget magna rutrum sagittis. Nulla facilisi. Praesent a consectetur 117 | velit. Cras eget nibh est, eu imperdiet mauris. Nulla quis justo urna. Sed eu 118 | rutrum mauris. Integer aliquet nulla sit amet ante mollis pellentesque. 119 | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Ut varius 120 | diam posuere quam molestie vestibulum. Vestibulum non volutpat elit. Integer 121 | vitae felis eget magna rutrum sagittis. Nulla facilisi. Praesent a consectetur 122 | velit. Cras eget nibh est, eu imperdiet mauris. Nulla quis justo urna. Sed eu 123 | rutrum mauris. Integer aliquet nulla sit amet ante mollis pellentesque. 124 | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Ut varius 125 | diam posuere quam molestie vestibulum. Vestibulum non volutpat elit. Integer 126 | vitae felis eget magna rutrum sagittis. Nulla facilisi. Praesent a consectetur 127 | velit. Cras eget nibh est, eu imperdiet mauris. Nulla quis justo urna. Sed eu 128 | rutrum mauris. Integer aliquet nulla sit amet ante mollis pellentesque. 129 |

130 |

131 |
132 | 133 |

134 | Ptolemaic Map, 16th century. Public Domain. Source: 135 | Wikimedia 136 | Commons 137 |

138 |
139 |

140 | This image is not supposed to be annotatable! 141 |

142 |

143 |
144 | 145 | 146 | -------------------------------------------------------------------------------- /test/image/qunit/image-test-suite.css: -------------------------------------------------------------------------------- 1 | html, body { 2 | background-color:#cecedf; 3 | min-height:100%; 4 | padding:0; 5 | margin:0; 6 | } 7 | 8 | .column { 9 | width:80%; 10 | margin:0 auto; 11 | padding:10px 20px; 12 | background-color:#fff; 13 | height:100%; 14 | } 15 | 16 | button { 17 | padding:10px 25px; 18 | font-size:16px; 19 | } 20 | -------------------------------------------------------------------------------- /test/image/qunit/image-test-suite.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 |

15 | 16 |

17 |
18 |
19 |
20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /test/image/qunit/image-test-suite.js: -------------------------------------------------------------------------------- 1 | function drawAnnotation(from, to, text) { 2 | jQuery('.annotorious-annotationlayer').simulate('mouseover'); 3 | jQuery('.annotorious-item').simulate('mousedown', { clientX: from.x, clientY: from.y }); 4 | jQuery('.annotorious-item').simulate('mousemove', { clientX: to.x, clientY: to.y }); 5 | jQuery('.annotorious-item').simulate('mouseup'); 6 | jQuery('.annotorious-editor-text').val(text); 7 | jQuery('.annotorious-editor-button-save').simulate('click'); 8 | } 9 | 10 | function editAnnotation(pos, text) { 11 | jQuery('.annotorious-item').simulate('mousemove', { clientX: pos.x, clientY: pos.y }); 12 | jQuery('.annotorious-popup-button-edit').simulate('click'); 13 | jQuery('.annotorious-editor-text').val(text); 14 | jQuery('.annotorious-editor-button-save').simulate('click'); 15 | } 16 | 17 | function deleteAnnotation(pos) { 18 | jQuery('.annotorious-item').simulate('mousemove', { clientX: pos.x, clientY: pos.y }); 19 | jQuery('.annotorious-popup-button-delete').simulate('click'); 20 | } 21 | 22 | jQuery(function() { 23 | jQuery('#run').click(function() { 24 | 25 | // Create annotation #1 26 | drawAnnotation({x:445, y:195}, {x:500, y:355}, 'Church.'); 27 | test("Annotation #1 Created", function() { 28 | var annotations = anno.getAnnotations(); 29 | ok(annotations.length == 1); 30 | ok(annotations[0].text == 'Church.'); 31 | }); 32 | 33 | // Create annotation #2 34 | drawAnnotation({x:425, y:220}, {x:650, y:370}, 'Hallstatt.'); 35 | test("Annotation #2 Created", function() { 36 | var annotations = anno.getAnnotations(); 37 | ok(annotations.length == 2); 38 | ok(annotations[0].text == 'Church.'); 39 | ok(annotations[1].text == 'Hallstatt.'); 40 | }); 41 | 42 | // Edit annotation #1 43 | editAnnotation({x:475, y:196}, 'A church in Hallstatt.'); 44 | test("Annotation #1 Updated", function() { 45 | var annotations = anno.getAnnotations(); 46 | ok(annotations[0].text == 'Hallstatt.'); 47 | ok(annotations[1].text == 'A church in Hallstatt.'); 48 | }); 49 | 50 | // Delete annotation #2 51 | deleteAnnotation({x:502,y:280}); 52 | test("Annotation #2 Deleted", function() { 53 | var annotations = anno.getAnnotations(); 54 | ok(annotations.length == 1); 55 | ok(annotations[0].text == 'A church in Hallstatt.'); 56 | }); 57 | 58 | }); 59 | }); 60 | -------------------------------------------------------------------------------- /test/jquery.simulate.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * jQuery Simulate v0.0.1 - simulate browser mouse and keyboard events 3 | * https://github.com/jquery/jquery-simulate 4 | * 5 | * Copyright 2012 jQuery Foundation and other contributors 6 | * Released under the MIT license. 7 | * http://jquery.org/license 8 | * 9 | * Date: Sun Dec 9 12:15:33 2012 -0500 10 | */ 11 | 12 | ;(function( $, undefined ) { 13 | 14 | var rkeyEvent = /^key/, 15 | rmouseEvent = /^(?:mouse|contextmenu)|click/; 16 | 17 | $.fn.simulate = function( type, options ) { 18 | return this.each(function() { 19 | new $.simulate( this, type, options ); 20 | }); 21 | }; 22 | 23 | $.simulate = function( elem, type, options ) { 24 | var method = $.camelCase( "simulate-" + type ); 25 | 26 | this.target = elem; 27 | this.options = options; 28 | 29 | if ( this[ method ] ) { 30 | this[ method ](); 31 | } else { 32 | this.simulateEvent( elem, type, options ); 33 | } 34 | }; 35 | 36 | $.extend( $.simulate, { 37 | 38 | keyCode: { 39 | BACKSPACE: 8, 40 | COMMA: 188, 41 | DELETE: 46, 42 | DOWN: 40, 43 | END: 35, 44 | ENTER: 13, 45 | ESCAPE: 27, 46 | HOME: 36, 47 | LEFT: 37, 48 | NUMPAD_ADD: 107, 49 | NUMPAD_DECIMAL: 110, 50 | NUMPAD_DIVIDE: 111, 51 | NUMPAD_ENTER: 108, 52 | NUMPAD_MULTIPLY: 106, 53 | NUMPAD_SUBTRACT: 109, 54 | PAGE_DOWN: 34, 55 | PAGE_UP: 33, 56 | PERIOD: 190, 57 | RIGHT: 39, 58 | SPACE: 32, 59 | TAB: 9, 60 | UP: 38 61 | }, 62 | 63 | buttonCode: { 64 | LEFT: 0, 65 | MIDDLE: 1, 66 | RIGHT: 2 67 | } 68 | }); 69 | 70 | $.extend( $.simulate.prototype, { 71 | 72 | simulateEvent: function( elem, type, options ) { 73 | var event = this.createEvent( type, options ); 74 | this.dispatchEvent( elem, type, event, options ); 75 | }, 76 | 77 | createEvent: function( type, options ) { 78 | if ( rkeyEvent.test( type ) ) { 79 | return this.keyEvent( type, options ); 80 | } 81 | 82 | if ( rmouseEvent.test( type ) ) { 83 | return this.mouseEvent( type, options ); 84 | } 85 | }, 86 | 87 | mouseEvent: function( type, options ) { 88 | var event, eventDoc, doc, body; 89 | options = $.extend({ 90 | bubbles: true, 91 | cancelable: (type !== "mousemove"), 92 | view: window, 93 | detail: 0, 94 | screenX: 0, 95 | screenY: 0, 96 | clientX: 1, 97 | clientY: 1, 98 | ctrlKey: false, 99 | altKey: false, 100 | shiftKey: false, 101 | metaKey: false, 102 | button: 0, 103 | relatedTarget: undefined 104 | }, options ); 105 | 106 | if ( document.createEvent ) { 107 | event = document.createEvent( "MouseEvents" ); 108 | event.initMouseEvent( type, options.bubbles, options.cancelable, 109 | options.view, options.detail, 110 | options.screenX, options.screenY, options.clientX, options.clientY, 111 | options.ctrlKey, options.altKey, options.shiftKey, options.metaKey, 112 | options.button, options.relatedTarget || document.body.parentNode ); 113 | 114 | // IE 9+ creates events with pageX and pageY set to 0. 115 | // Trying to modify the properties throws an error, 116 | // so we define getters to return the correct values. 117 | if ( event.pageX === 0 && event.pageY === 0 && Object.defineProperty ) { 118 | eventDoc = event.relatedTarget.ownerDocument || document; 119 | doc = eventDoc.documentElement; 120 | body = eventDoc.body; 121 | 122 | Object.defineProperty( event, "pageX", { 123 | get: function() { 124 | return options.clientX + 125 | ( doc && doc.scrollLeft || body && body.scrollLeft || 0 ) - 126 | ( doc && doc.clientLeft || body && body.clientLeft || 0 ); 127 | } 128 | }); 129 | Object.defineProperty( event, "pageY", { 130 | get: function() { 131 | return options.clientY + 132 | ( doc && doc.scrollTop || body && body.scrollTop || 0 ) - 133 | ( doc && doc.clientTop || body && body.clientTop || 0 ); 134 | } 135 | }); 136 | } 137 | } else if ( document.createEventObject ) { 138 | event = document.createEventObject(); 139 | $.extend( event, options ); 140 | // standards event.button uses constants defined here: http://msdn.microsoft.com/en-us/library/ie/ff974877(v=vs.85).aspx 141 | // old IE event.button uses constants defined here: http://msdn.microsoft.com/en-us/library/ie/ms533544(v=vs.85).aspx 142 | // so we actually need to map the standard back to oldIE 143 | event.button = { 144 | 0: 1, 145 | 1: 4, 146 | 2: 2 147 | }[ event.button ] || event.button; 148 | } 149 | 150 | return event; 151 | }, 152 | 153 | keyEvent: function( type, options ) { 154 | var event; 155 | options = $.extend({ 156 | bubbles: true, 157 | cancelable: true, 158 | view: window, 159 | ctrlKey: false, 160 | altKey: false, 161 | shiftKey: false, 162 | metaKey: false, 163 | keyCode: 0, 164 | charCode: undefined 165 | }, options ); 166 | 167 | if ( document.createEvent ) { 168 | try { 169 | event = document.createEvent( "KeyEvents" ); 170 | event.initKeyEvent( type, options.bubbles, options.cancelable, options.view, 171 | options.ctrlKey, options.altKey, options.shiftKey, options.metaKey, 172 | options.keyCode, options.charCode ); 173 | // initKeyEvent throws an exception in WebKit 174 | // see: http://stackoverflow.com/questions/6406784/initkeyevent-keypress-only-works-in-firefox-need-a-cross-browser-solution 175 | // and also https://bugs.webkit.org/show_bug.cgi?id=13368 176 | // fall back to a generic event until we decide to implement initKeyboardEvent 177 | } catch( err ) { 178 | event = document.createEvent( "Events" ); 179 | event.initEvent( type, options.bubbles, options.cancelable ); 180 | $.extend( event, { 181 | view: options.view, 182 | ctrlKey: options.ctrlKey, 183 | altKey: options.altKey, 184 | shiftKey: options.shiftKey, 185 | metaKey: options.metaKey, 186 | keyCode: options.keyCode, 187 | charCode: options.charCode 188 | }); 189 | } 190 | } else if ( document.createEventObject ) { 191 | event = document.createEventObject(); 192 | $.extend( event, options ); 193 | } 194 | 195 | if ( !!/msie [\w.]+/.exec( navigator.userAgent.toLowerCase() ) || (({}).toString.call( window.opera ) === "[object Opera]") ) { 196 | event.keyCode = (options.charCode > 0) ? options.charCode : options.keyCode; 197 | event.charCode = undefined; 198 | } 199 | 200 | return event; 201 | }, 202 | 203 | dispatchEvent: function( elem, type, event ) { 204 | if ( elem.dispatchEvent ) { 205 | elem.dispatchEvent( event ); 206 | } else if ( elem.fireEvent ) { 207 | elem.fireEvent( "on" + type, event ); 208 | } 209 | }, 210 | 211 | simulateFocus: function() { 212 | var focusinEvent, 213 | triggered = false, 214 | element = $( this.target ); 215 | 216 | function trigger() { 217 | triggered = true; 218 | } 219 | 220 | element.bind( "focus", trigger ); 221 | element[ 0 ].focus(); 222 | 223 | if ( !triggered ) { 224 | focusinEvent = $.Event( "focusin" ); 225 | focusinEvent.preventDefault(); 226 | element.trigger( focusinEvent ); 227 | element.triggerHandler( "focus" ); 228 | } 229 | element.unbind( "focus", trigger ); 230 | }, 231 | 232 | simulateBlur: function() { 233 | var focusoutEvent, 234 | triggered = false, 235 | element = $( this.target ); 236 | 237 | function trigger() { 238 | triggered = true; 239 | } 240 | 241 | element.bind( "blur", trigger ); 242 | element[ 0 ].blur(); 243 | 244 | // blur events are async in IE 245 | setTimeout(function() { 246 | // IE won't let the blur occur if the window is inactive 247 | if ( element[ 0 ].ownerDocument.activeElement === element[ 0 ] ) { 248 | element[ 0 ].ownerDocument.body.focus(); 249 | } 250 | 251 | // Firefox won't trigger events if the window is inactive 252 | // IE doesn't trigger events if we had to manually focus the body 253 | if ( !triggered ) { 254 | focusoutEvent = $.Event( "focusout" ); 255 | focusoutEvent.preventDefault(); 256 | element.trigger( focusoutEvent ); 257 | element.triggerHandler( "blur" ); 258 | } 259 | element.unbind( "blur", trigger ); 260 | }, 1 ); 261 | } 262 | }); 263 | 264 | 265 | 266 | /** complex events **/ 267 | 268 | function findCenter( elem ) { 269 | var offset, 270 | document = $( elem.ownerDocument ); 271 | elem = $( elem ); 272 | offset = elem.offset(); 273 | 274 | return { 275 | x: offset.left + elem.outerWidth() / 2 - document.scrollLeft(), 276 | y: offset.top + elem.outerHeight() / 2 - document.scrollTop() 277 | }; 278 | } 279 | 280 | function findCorner( elem ) { 281 | var offset, 282 | document = $( elem.ownerDocument ); 283 | elem = $( elem ); 284 | offset = elem.offset(); 285 | 286 | return { 287 | x: offset.left - document.scrollLeft(), 288 | y: offset.top - document.scrollTop() 289 | }; 290 | } 291 | 292 | $.extend( $.simulate.prototype, { 293 | simulateDrag: function() { 294 | var i = 0, 295 | target = this.target, 296 | options = this.options, 297 | center = options.handle === "corner" ? findCorner( target ) : findCenter( target ), 298 | x = Math.floor( center.x ), 299 | y = Math.floor( center.y ), 300 | coord = { clientX: x, clientY: y }, 301 | dx = options.dx || ( options.x !== undefined ? options.x - x : 0 ), 302 | dy = options.dy || ( options.y !== undefined ? options.y - y : 0 ), 303 | moves = options.moves || 3; 304 | 305 | this.simulateEvent( target, "mousedown", coord ); 306 | 307 | for ( ; i < moves ; i++ ) { 308 | x += dx / moves; 309 | y += dy / moves; 310 | 311 | coord = { 312 | clientX: Math.round( x ), 313 | clientY: Math.round( y ) 314 | }; 315 | 316 | this.simulateEvent( target.ownerDocument, "mousemove", coord ); 317 | } 318 | 319 | if ( $.contains( document, target ) ) { 320 | this.simulateEvent( target, "mouseup", coord ); 321 | this.simulateEvent( target, "click", coord ); 322 | } else { 323 | this.simulateEvent( document, "mouseup", coord ); 324 | } 325 | } 326 | }); 327 | 328 | })( jQuery ); 329 | -------------------------------------------------------------------------------- /test/okfn/image_okfn.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 37 | 38 | 39 | 40 |
41 |

Annotation Test Page

42 |

43 | This page serves as a development environment for the Yuma2 (working title) OKFN 44 | Annotator plugin. Hover over one of the images to get started. 45 | NOTE: make sure the plovr build tool is running! Use 46 |

47 |

48 | java -jar plovr/plovr.jar serve okfn_plugin.json 49 |

50 | to start plovr. 51 |

52 | 53 |
54 |

Test Images

55 |
56 | 57 |

58 | Hallstatt, Austria. By Nick Csakany, 2007. Public Domain. Source: 59 | Wikimedia 60 | Commons 61 |

62 |
63 |

64 | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Ut varius 65 | diam posuere quam molestie vestibulum. Vestibulum non volutpat elit. Integer 66 | vitae felis eget magna rutrum sagittis. Nulla facilisi. Praesent a consectetur 67 | velit. Cras eget nibh est, eu imperdiet mauris. Nulla quis justo urna. Sed eu 68 | rutrum mauris. Integer aliquet nulla sit amet ante mollis pellentesque. 69 | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Ut varius 70 | diam posuere quam molestie vestibulum. Vestibulum non volutpat elit. Integer 71 | vitae felis eget magna rutrum sagittis. Nulla facilisi. Praesent a consectetur 72 | velit. Cras eget nibh est, eu imperdiet mauris. Nulla quis justo urna. Sed eu 73 | rutrum mauris. Integer aliquet nulla sit amet ante mollis pellentesque. 74 | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Ut varius 75 | diam posuere quam molestie vestibulum. Vestibulum non volutpat elit. Integer 76 | vitae felis eget magna rutrum sagittis. Nulla facilisi. Praesent a consectetur 77 | velit. Cras eget nibh est, eu imperdiet mauris. Nulla quis justo urna. Sed eu 78 | rutrum mauris. Integer aliquet nulla sit amet ante mollis pellentesque. 79 |

80 |
81 | 82 |

83 | Medival Ideal Portrait of Ptolemy. Public Domain. Source: 84 | Wikimedia 85 | Commons 86 |

87 |
88 |

89 | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Ut varius 90 | diam posuere quam molestie vestibulum. Vestibulum non volutpat elit. Integer 91 | vitae felis eget magna rutrum sagittis. Nulla facilisi. Praesent a consectetur 92 | velit. Cras eget nibh est, eu imperdiet mauris. Nulla quis justo urna. Sed eu 93 | rutrum mauris. Integer aliquet nulla sit amet ante mollis pellentesque. 94 | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Ut varius 95 | diam posuere quam molestie vestibulum. Vestibulum non volutpat elit. Integer 96 | vitae felis eget magna rutrum sagittis. Nulla facilisi. Praesent a consectetur 97 | velit. Cras eget nibh est, eu imperdiet mauris. Nulla quis justo urna. Sed eu 98 | rutrum mauris. Integer aliquet nulla sit amet ante mollis pellentesque. 99 | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Ut varius 100 | diam posuere quam molestie vestibulum. Vestibulum non volutpat elit. Integer 101 | vitae felis eget magna rutrum sagittis. Nulla facilisi. Praesent a consectetur 102 | velit. Cras eget nibh est, eu imperdiet mauris. Nulla quis justo urna. Sed eu 103 | rutrum mauris. Integer aliquet nulla sit amet ante mollis pellentesque. 104 | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Ut varius 105 | diam posuere quam molestie vestibulum. Vestibulum non volutpat elit. Integer 106 | vitae felis eget magna rutrum sagittis. Nulla facilisi. Praesent a consectetur 107 | velit. Cras eget nibh est, eu imperdiet mauris. Nulla quis justo urna. Sed eu 108 | rutrum mauris. Integer aliquet nulla sit amet ante mollis pellentesque. 109 |

110 |

111 |
112 |
113 | 114 | 118 | 119 | 120 | -------------------------------------------------------------------------------- /test/openlayers/OpenLayers_LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright 2005-2012 OpenLayers Contributors. All rights reserved. See 2 | authors.txt for full list. 3 | 4 | Redistribution and use in source and binary forms, with or without modification, 5 | are permitted provided that the following conditions are met: 6 | 7 | 1. Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | 2. Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation and/or 12 | other materials provided with the distribution. 13 | 14 | THIS SOFTWARE IS PROVIDED BY OPENLAYERS CONTRIBUTORS ``AS IS'' AND ANY EXPRESS 15 | OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 16 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT 17 | SHALL COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 18 | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 19 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 20 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 21 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 22 | OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 23 | ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | 25 | The views and conclusions contained in the software and documentation are those 26 | of the authors and should not be interpreted as representing official policies, 27 | either expressed or implied, of OpenLayers Contributors. 28 | -------------------------------------------------------------------------------- /test/openlayers/map_muenster/0/0/0.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/annotorious/annotorious-v1/3fb5a931428a3bc9e3f48bc905ab042a393d0907/test/openlayers/map_muenster/0/0/0.jpg -------------------------------------------------------------------------------- /test/openlayers/map_muenster/1/0/0.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/annotorious/annotorious-v1/3fb5a931428a3bc9e3f48bc905ab042a393d0907/test/openlayers/map_muenster/1/0/0.jpg -------------------------------------------------------------------------------- /test/openlayers/map_muenster/1/0/1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/annotorious/annotorious-v1/3fb5a931428a3bc9e3f48bc905ab042a393d0907/test/openlayers/map_muenster/1/0/1.jpg -------------------------------------------------------------------------------- /test/openlayers/map_muenster/1/1/0.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/annotorious/annotorious-v1/3fb5a931428a3bc9e3f48bc905ab042a393d0907/test/openlayers/map_muenster/1/1/0.jpg -------------------------------------------------------------------------------- /test/openlayers/map_muenster/1/1/1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/annotorious/annotorious-v1/3fb5a931428a3bc9e3f48bc905ab042a393d0907/test/openlayers/map_muenster/1/1/1.jpg -------------------------------------------------------------------------------- /test/openlayers/map_muenster/2/0/0.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/annotorious/annotorious-v1/3fb5a931428a3bc9e3f48bc905ab042a393d0907/test/openlayers/map_muenster/2/0/0.jpg -------------------------------------------------------------------------------- /test/openlayers/map_muenster/2/0/1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/annotorious/annotorious-v1/3fb5a931428a3bc9e3f48bc905ab042a393d0907/test/openlayers/map_muenster/2/0/1.jpg -------------------------------------------------------------------------------- /test/openlayers/map_muenster/2/0/2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/annotorious/annotorious-v1/3fb5a931428a3bc9e3f48bc905ab042a393d0907/test/openlayers/map_muenster/2/0/2.jpg -------------------------------------------------------------------------------- /test/openlayers/map_muenster/2/1/0.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/annotorious/annotorious-v1/3fb5a931428a3bc9e3f48bc905ab042a393d0907/test/openlayers/map_muenster/2/1/0.jpg -------------------------------------------------------------------------------- /test/openlayers/map_muenster/2/1/1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/annotorious/annotorious-v1/3fb5a931428a3bc9e3f48bc905ab042a393d0907/test/openlayers/map_muenster/2/1/1.jpg -------------------------------------------------------------------------------- /test/openlayers/map_muenster/2/1/2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/annotorious/annotorious-v1/3fb5a931428a3bc9e3f48bc905ab042a393d0907/test/openlayers/map_muenster/2/1/2.jpg -------------------------------------------------------------------------------- /test/openlayers/map_muenster/2/2/0.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/annotorious/annotorious-v1/3fb5a931428a3bc9e3f48bc905ab042a393d0907/test/openlayers/map_muenster/2/2/0.jpg -------------------------------------------------------------------------------- /test/openlayers/map_muenster/2/2/1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/annotorious/annotorious-v1/3fb5a931428a3bc9e3f48bc905ab042a393d0907/test/openlayers/map_muenster/2/2/1.jpg -------------------------------------------------------------------------------- /test/openlayers/map_muenster/2/2/2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/annotorious/annotorious-v1/3fb5a931428a3bc9e3f48bc905ab042a393d0907/test/openlayers/map_muenster/2/2/2.jpg -------------------------------------------------------------------------------- /test/openlayers/map_muenster/2/3/0.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/annotorious/annotorious-v1/3fb5a931428a3bc9e3f48bc905ab042a393d0907/test/openlayers/map_muenster/2/3/0.jpg -------------------------------------------------------------------------------- /test/openlayers/map_muenster/2/3/1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/annotorious/annotorious-v1/3fb5a931428a3bc9e3f48bc905ab042a393d0907/test/openlayers/map_muenster/2/3/1.jpg -------------------------------------------------------------------------------- /test/openlayers/map_muenster/2/3/2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/annotorious/annotorious-v1/3fb5a931428a3bc9e3f48bc905ab042a393d0907/test/openlayers/map_muenster/2/3/2.jpg -------------------------------------------------------------------------------- /test/openlayers/map_muenster/tilemapresource.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | map.jpg 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /test/openlayers/openlayers_fullscreen.html: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/annotorious/annotorious-v1/3fb5a931428a3bc9e3f48bc905ab042a393d0907/test/openlayers/openlayers_fullscreen.html -------------------------------------------------------------------------------- /test/openlayers/openlayers_standalone.html: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/annotorious/annotorious-v1/3fb5a931428a3bc9e3f48bc905ab042a393d0907/test/openlayers/openlayers_standalone.html -------------------------------------------------------------------------------- /test/openlayers/theme/default/google.css: -------------------------------------------------------------------------------- 1 | .olLayerGoogleCopyright { 2 | right: 3px; 3 | bottom: 2px; 4 | left: auto; 5 | } 6 | .olLayerGoogleV3.olLayerGoogleCopyright { 7 | bottom: 0px; 8 | right: 0px !important; 9 | } 10 | .olLayerGooglePoweredBy { 11 | left: 2px; 12 | bottom: 2px; 13 | } 14 | .olLayerGoogleV3.olLayerGooglePoweredBy { 15 | bottom: 0px !important; 16 | } 17 | 18 | -------------------------------------------------------------------------------- /test/openlayers/theme/default/google.tidy.css: -------------------------------------------------------------------------------- 1 | .olLayerGoogleCopyright{right:3px;bottom:2px;left:auto;}.olLayerGoogleV3.olLayerGoogleCopyright{bottom:0;right:0!important;}.olLayerGooglePoweredBy{left:2px;bottom:2px;}.olLayerGoogleV3.olLayerGooglePoweredBy{bottom:0!important;} -------------------------------------------------------------------------------- /test/openlayers/theme/default/ie6-style.css: -------------------------------------------------------------------------------- 1 | .olControlZoomPanel div { 2 | background-image: url(img/zoom-panel-NOALPHA.png); 3 | } 4 | .olControlPanPanel div { 5 | background-image: url(img/pan-panel-NOALPHA.png); 6 | } 7 | .olControlEditingToolbar { 8 | width: 200px; 9 | } 10 | 11 | -------------------------------------------------------------------------------- /test/openlayers/theme/default/ie6-style.tidy.css: -------------------------------------------------------------------------------- 1 | .olControlZoomPanel div{background-image:url(img/zoom-panel-NOALPHA.png);}.olControlPanPanel div{background-image:url(img/pan-panel-NOALPHA.png);}.olControlEditingToolbar{width:200px;} -------------------------------------------------------------------------------- /test/openlayers/theme/default/img/add_point_off.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/annotorious/annotorious-v1/3fb5a931428a3bc9e3f48bc905ab042a393d0907/test/openlayers/theme/default/img/add_point_off.png -------------------------------------------------------------------------------- /test/openlayers/theme/default/img/add_point_on.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/annotorious/annotorious-v1/3fb5a931428a3bc9e3f48bc905ab042a393d0907/test/openlayers/theme/default/img/add_point_on.png -------------------------------------------------------------------------------- /test/openlayers/theme/default/img/blank.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/annotorious/annotorious-v1/3fb5a931428a3bc9e3f48bc905ab042a393d0907/test/openlayers/theme/default/img/blank.gif -------------------------------------------------------------------------------- /test/openlayers/theme/default/img/close.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/annotorious/annotorious-v1/3fb5a931428a3bc9e3f48bc905ab042a393d0907/test/openlayers/theme/default/img/close.gif -------------------------------------------------------------------------------- /test/openlayers/theme/default/img/drag-rectangle-off.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/annotorious/annotorious-v1/3fb5a931428a3bc9e3f48bc905ab042a393d0907/test/openlayers/theme/default/img/drag-rectangle-off.png -------------------------------------------------------------------------------- /test/openlayers/theme/default/img/drag-rectangle-on.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/annotorious/annotorious-v1/3fb5a931428a3bc9e3f48bc905ab042a393d0907/test/openlayers/theme/default/img/drag-rectangle-on.png -------------------------------------------------------------------------------- /test/openlayers/theme/default/img/draw_line_off.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/annotorious/annotorious-v1/3fb5a931428a3bc9e3f48bc905ab042a393d0907/test/openlayers/theme/default/img/draw_line_off.png -------------------------------------------------------------------------------- /test/openlayers/theme/default/img/draw_line_on.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/annotorious/annotorious-v1/3fb5a931428a3bc9e3f48bc905ab042a393d0907/test/openlayers/theme/default/img/draw_line_on.png -------------------------------------------------------------------------------- /test/openlayers/theme/default/img/draw_point_off.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/annotorious/annotorious-v1/3fb5a931428a3bc9e3f48bc905ab042a393d0907/test/openlayers/theme/default/img/draw_point_off.png -------------------------------------------------------------------------------- /test/openlayers/theme/default/img/draw_point_on.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/annotorious/annotorious-v1/3fb5a931428a3bc9e3f48bc905ab042a393d0907/test/openlayers/theme/default/img/draw_point_on.png -------------------------------------------------------------------------------- /test/openlayers/theme/default/img/draw_polygon_off.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/annotorious/annotorious-v1/3fb5a931428a3bc9e3f48bc905ab042a393d0907/test/openlayers/theme/default/img/draw_polygon_off.png -------------------------------------------------------------------------------- /test/openlayers/theme/default/img/draw_polygon_on.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/annotorious/annotorious-v1/3fb5a931428a3bc9e3f48bc905ab042a393d0907/test/openlayers/theme/default/img/draw_polygon_on.png -------------------------------------------------------------------------------- /test/openlayers/theme/default/img/editing_tool_bar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/annotorious/annotorious-v1/3fb5a931428a3bc9e3f48bc905ab042a393d0907/test/openlayers/theme/default/img/editing_tool_bar.png -------------------------------------------------------------------------------- /test/openlayers/theme/default/img/move_feature_off.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/annotorious/annotorious-v1/3fb5a931428a3bc9e3f48bc905ab042a393d0907/test/openlayers/theme/default/img/move_feature_off.png -------------------------------------------------------------------------------- /test/openlayers/theme/default/img/move_feature_on.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/annotorious/annotorious-v1/3fb5a931428a3bc9e3f48bc905ab042a393d0907/test/openlayers/theme/default/img/move_feature_on.png -------------------------------------------------------------------------------- /test/openlayers/theme/default/img/navigation_history.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/annotorious/annotorious-v1/3fb5a931428a3bc9e3f48bc905ab042a393d0907/test/openlayers/theme/default/img/navigation_history.png -------------------------------------------------------------------------------- /test/openlayers/theme/default/img/overview_replacement.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/annotorious/annotorious-v1/3fb5a931428a3bc9e3f48bc905ab042a393d0907/test/openlayers/theme/default/img/overview_replacement.gif -------------------------------------------------------------------------------- /test/openlayers/theme/default/img/pan-panel-NOALPHA.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/annotorious/annotorious-v1/3fb5a931428a3bc9e3f48bc905ab042a393d0907/test/openlayers/theme/default/img/pan-panel-NOALPHA.png -------------------------------------------------------------------------------- /test/openlayers/theme/default/img/pan-panel.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/annotorious/annotorious-v1/3fb5a931428a3bc9e3f48bc905ab042a393d0907/test/openlayers/theme/default/img/pan-panel.png -------------------------------------------------------------------------------- /test/openlayers/theme/default/img/pan_off.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/annotorious/annotorious-v1/3fb5a931428a3bc9e3f48bc905ab042a393d0907/test/openlayers/theme/default/img/pan_off.png -------------------------------------------------------------------------------- /test/openlayers/theme/default/img/pan_on.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/annotorious/annotorious-v1/3fb5a931428a3bc9e3f48bc905ab042a393d0907/test/openlayers/theme/default/img/pan_on.png -------------------------------------------------------------------------------- /test/openlayers/theme/default/img/panning-hand-off.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/annotorious/annotorious-v1/3fb5a931428a3bc9e3f48bc905ab042a393d0907/test/openlayers/theme/default/img/panning-hand-off.png -------------------------------------------------------------------------------- /test/openlayers/theme/default/img/panning-hand-on.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/annotorious/annotorious-v1/3fb5a931428a3bc9e3f48bc905ab042a393d0907/test/openlayers/theme/default/img/panning-hand-on.png -------------------------------------------------------------------------------- /test/openlayers/theme/default/img/remove_point_off.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/annotorious/annotorious-v1/3fb5a931428a3bc9e3f48bc905ab042a393d0907/test/openlayers/theme/default/img/remove_point_off.png -------------------------------------------------------------------------------- /test/openlayers/theme/default/img/remove_point_on.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/annotorious/annotorious-v1/3fb5a931428a3bc9e3f48bc905ab042a393d0907/test/openlayers/theme/default/img/remove_point_on.png -------------------------------------------------------------------------------- /test/openlayers/theme/default/img/ruler.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/annotorious/annotorious-v1/3fb5a931428a3bc9e3f48bc905ab042a393d0907/test/openlayers/theme/default/img/ruler.png -------------------------------------------------------------------------------- /test/openlayers/theme/default/img/save_features_off.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/annotorious/annotorious-v1/3fb5a931428a3bc9e3f48bc905ab042a393d0907/test/openlayers/theme/default/img/save_features_off.png -------------------------------------------------------------------------------- /test/openlayers/theme/default/img/save_features_on.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/annotorious/annotorious-v1/3fb5a931428a3bc9e3f48bc905ab042a393d0907/test/openlayers/theme/default/img/save_features_on.png -------------------------------------------------------------------------------- /test/openlayers/theme/default/img/view_next_off.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/annotorious/annotorious-v1/3fb5a931428a3bc9e3f48bc905ab042a393d0907/test/openlayers/theme/default/img/view_next_off.png -------------------------------------------------------------------------------- /test/openlayers/theme/default/img/view_next_on.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/annotorious/annotorious-v1/3fb5a931428a3bc9e3f48bc905ab042a393d0907/test/openlayers/theme/default/img/view_next_on.png -------------------------------------------------------------------------------- /test/openlayers/theme/default/img/view_previous_off.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/annotorious/annotorious-v1/3fb5a931428a3bc9e3f48bc905ab042a393d0907/test/openlayers/theme/default/img/view_previous_off.png -------------------------------------------------------------------------------- /test/openlayers/theme/default/img/view_previous_on.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/annotorious/annotorious-v1/3fb5a931428a3bc9e3f48bc905ab042a393d0907/test/openlayers/theme/default/img/view_previous_on.png -------------------------------------------------------------------------------- /test/openlayers/theme/default/img/zoom-panel-NOALPHA.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/annotorious/annotorious-v1/3fb5a931428a3bc9e3f48bc905ab042a393d0907/test/openlayers/theme/default/img/zoom-panel-NOALPHA.png -------------------------------------------------------------------------------- /test/openlayers/theme/default/img/zoom-panel.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/annotorious/annotorious-v1/3fb5a931428a3bc9e3f48bc905ab042a393d0907/test/openlayers/theme/default/img/zoom-panel.png -------------------------------------------------------------------------------- /test/openlayers/theme/default/style.mobile.css: -------------------------------------------------------------------------------- 1 | div.olControlZoom { 2 | position: absolute; 3 | top: 8px; 4 | left: 8px; 5 | background: rgba(255,255,255,0.4); 6 | border-radius: 4px; 7 | padding: 2px; 8 | } 9 | * { 10 | -webkit-tap-highlight-color: rgba(0, 0, 0, 0); 11 | } 12 | div.olControlZoom a { 13 | display: block; 14 | margin: 1px; 15 | padding: 0; 16 | color: white; 17 | font-size: 28px; 18 | font-family: sans-serif; 19 | font-weight: bold; 20 | text-decoration: none; 21 | text-align: center; 22 | height: 32px; 23 | width: 32px; 24 | line-height: 28px; 25 | text-shadow: 0 0 3px rgba(0,0,0,0.8); 26 | background: #130085; /* fallback for IE - IE6 requires background shorthand*/ 27 | background: rgba(0, 60, 136, 0.5); 28 | filter: alpha(opacity=80); 29 | } 30 | a.olControlZoomIn { 31 | border-radius: 4px 4px 0 0; 32 | } 33 | a.olControlZoomOut { 34 | border-radius: 0 0 4px 4px; 35 | } 36 | div.olControlZoom a:hover { 37 | background: #130085; /* fallback for IE */ 38 | background: rgba(0, 60, 136, 0.7); 39 | filter: alpha(opacity=100); 40 | } 41 | @media only screen and (max-width: 600px) { 42 | div.olControlZoom a:hover { 43 | background: rgba(0, 60, 136, 0.5); 44 | } 45 | } 46 | .olLayerGrid .olTileImage { 47 | -webkit-transition: opacity 0.2s linear; 48 | -moz-transition: opacity 0.2s linear; 49 | -o-transition: opacity 0.2s linear; 50 | transition: opacity 0.2s linear; 51 | } 52 | /* Enable 3d acceleration when operating on tiles, this is 53 | known to yield better performance on IOS Safari. 54 | http://osgeo-org.1803224.n2.nabble.com/Harware-accelerated-CSS3-animations-for-iOS-td6255560.html 55 | 56 | It also prevents tile blinking effects in iOS 5. 57 | See https://github.com/openlayers/openlayers/issues/511 58 | */ 59 | @media (-webkit-transform-3d) { 60 | img.olTileImage { 61 | -webkit-transform: translate3d(0, 0, 0); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /test/openlayers/theme/default/style.mobile.tidy.css: -------------------------------------------------------------------------------- 1 | div.olControlZoom{position:absolute;top:8px;left:8px;background:rgba(255,255,255,0.4);border-radius:4px;padding:2px;}*{-webkit-tap-highlight-color:rgba(0,0,0,0);}div.olControlZoom a{display:block;color:#FFF;font-size:28px;font-family:sans-serif;font-weight:700;text-decoration:none;text-align:center;height:32px;width:32px;line-height:28px;text-shadow:0 0 3px rgba(0,0,0,0.8);background:rgba(0,60,136,0.5);filter:alpha(opacity=80);margin:1px;padding:0;}a.olControlZoomIn{border-radius:4px 4px 0 0;}a.olControlZoomOut{border-radius:0 0 4px 4px;}div.olControlZoom a:hover{background:rgba(0,60,136,0.7);filter:alpha(opacity=100);}.olLayerGrid .olTileImage{-webkit-transition:opacity .2s linear;-moz-transition:opacity .2s linear;-o-transition:opacity .2s linear;transition:opacity .2s linear;}@media only screen and max-width 600px{div.olControlZoom a:hover{background:rgba(0,60,136,0.5);}}@media -webkit-transform-3d{img.olTileImage{-webkit-transform:translate3d(0,0,0);}} -------------------------------------------------------------------------------- /test/openlayers/theme/default/style.tidy.css: -------------------------------------------------------------------------------- 1 | div.olMap{z-index:0;cursor:default;margin:0!important;padding:0!important;}div.olMapViewport{text-align:left;}.olLayerGoogleCopyright{left:2px;bottom:2px;}.olLayerGoogleV3.olLayerGoogleCopyright{right:auto!important;}.olLayerGooglePoweredBy{left:2px;bottom:15px;}.olLayerGoogleV3.olLayerGooglePoweredBy{bottom:15px!important;}.olControlAttribution{font-size:smaller;right:3px;bottom:4.5em;position:absolute;display:block;}.olControlScale{right:3px;bottom:3em;display:block;position:absolute;font-size:smaller;}.olControlScaleLine{display:block;position:absolute;left:10px;bottom:15px;font-size:xx-small;}.olControlScaleLineBottom{border:solid 2px #000;border-bottom:none;margin-top:-2px;text-align:center;}.olControlScaleLineTop{border:solid 2px #000;border-top:none;text-align:center;}.olControlPermalink{right:3px;bottom:1.5em;display:block;position:absolute;font-size:smaller;}div.olControlMousePosition{bottom:0;right:3px;display:block;position:absolute;font-family:Arial;font-size:smaller;}.olControlOverviewMapContainer{position:absolute;bottom:0;right:0;}.olControlOverviewMapElement{background-color:#00008B;-moz-border-radius:1em 0 0;padding:10px 18px 10px 10px;}.olControlOverviewMapMinimizeButton,.olControlOverviewMapMaximizeButton{height:18px;width:18px;right:0;bottom:80px;cursor:pointer;}.olControlOverviewMapExtentRectangle{overflow:hidden;background-image:url(img/blank.gif);cursor:move;border:2px dotted red;}.olControlOverviewMapRectReplacement{overflow:hidden;cursor:move;background-image:url(img/overview_replacement.gif);background-repeat:no-repeat;background-position:center;}.olLayerGeoRSSDescription{float:left;width:100%;overflow:auto;font-size:1em;}.olLayerGeoRSSClose{float:right;color:gray;font-size:1.2em;margin-right:6px;font-family:sans-serif;}.olLayerGeoRSSTitle{float:left;font-size:1.2em;}.olControlNavigationHistory{background-image:url(img/navigation_history.png);background-repeat:no-repeat;width:24px;height:24px;}.olControlNavigationHistoryPreviousItemActive{background-position:0 0;}.olControlNavigationHistoryPreviousItemInactive{background-position:0 -24px;}.olControlNavigationHistoryNextItemActive{background-position:-24px 0;}.olControlNavigationHistoryNextItemInactive{background-position:-24px -24px;}div.olControlSaveFeaturesItemActive{background-image:url(img/save_features_on.png);background-repeat:no-repeat;background-position:0 1px;}div.olControlSaveFeaturesItemInactive{background-image:url(img/save_features_off.png);background-repeat:no-repeat;background-position:0 1px;}.olHandlerBoxZoomBox{border:2px solid red;position:absolute;background-color:#FFF;opacity:.5;font-size:1px;filter:alpha(opacity=50);}.olHandlerBoxSelectFeature{border:2px solid blue;position:absolute;background-color:#FFF;opacity:.5;font-size:1px;filter:alpha(opacity=50);}.olControlPanPanel{top:10px;left:5px;}.olControlPanPanel div{background-image:url(img/pan-panel.png);height:18px;width:18px;cursor:pointer;position:absolute;}.olControlPanPanel .olControlPanNorthItemInactive{top:0;left:9px;background-position:0 0;}.olControlPanPanel .olControlPanSouthItemInactive{top:36px;left:9px;background-position:18px 0;}.olControlPanPanel .olControlPanWestItemInactive{position:absolute;top:18px;left:0;background-position:0 18px;}.olControlPanPanel .olControlPanEastItemInactive{top:18px;left:18px;background-position:18px 18px;}.olControlZoomPanel{top:71px;left:14px;}.olControlZoomPanel div{background-image:url(img/zoom-panel.png);position:absolute;height:18px;width:18px;cursor:pointer;}.olControlZoomPanel .olControlZoomInItemInactive{top:0;left:0;background-position:0 0;}.olControlZoomPanel .olControlZoomToMaxExtentItemInactive{top:18px;left:0;background-position:0 -18px;}.olControlZoomPanel .olControlZoomOutItemInactive{top:36px;left:0;background-position:0 18px;}.olControlPanZoomBar div{font-size:1px;}.olPopupCloseBox{background:url(img/close.gif) no-repeat;cursor:pointer;}.olImageLoadError{background-color:#FFC0CB;opacity:.5;filter:alpha(opacity=50);}.olCursorWait{cursor:wait;}.olDrawBox{cursor:crosshair;}.olControlDragFeatureActive.olControlDragFeatureOver.olDragDown{cursor:0;}.olControlLayerSwitcher{position:absolute;top:25px;right:0;width:20em;font-family:sans-serif;font-weight:700;margin-top:3px;margin-left:3px;margin-bottom:3px;font-size:smaller;color:#FFF;background-color:transparent;}.olControlLayerSwitcher .layersDiv{background-color:#00008B;padding:5px 10px;}.olControlLayerSwitcher .layersDiv .baseLbl,.olControlLayerSwitcher .layersDiv .dataLbl{margin-top:3px;margin-left:3px;margin-bottom:3px;}.olControlLayerSwitcher .layersDiv .baseLayersDiv,.olControlLayerSwitcher .layersDiv .dataLayersDiv{padding-left:10px;}.olControlLayerSwitcher .maximizeDiv,.olControlLayerSwitcher .minimizeDiv{width:18px;height:18px;top:5px;right:0;cursor:pointer;}.olBingAttribution{color:#DDD;}span.olGoogleAttribution a{color:#77C;}.olControlNavToolbar,.olControlEditingToolbar{margin:5px 5px 0 0;}.olControlNavToolbar div,.olControlEditingToolbar div{background-image:url(img/editing_tool_bar.png);background-repeat:no-repeat;width:24px;height:22px;cursor:pointer;margin:0 0 5px 5px;}.olControlEditingToolbar{right:0;top:0;}.olControlNavToolbar{top:295px;left:9px;}.olControlEditingToolbar div{float:right;}.olControlNavToolbar .olControlNavigationItemInactive,.olControlEditingToolbar .olControlNavigationItemInactive{background-position:-103px -1px;}.olControlNavToolbar .olControlNavigationItemActive,.olControlEditingToolbar .olControlNavigationItemActive{background-position:-103px -24px;}.olControlNavToolbar .olControlZoomBoxItemInactive{background-position:-128px -1px;}.olControlNavToolbar .olControlZoomBoxItemActive{background-position:-128px -24px;}.olControlEditingToolbar .olControlDrawFeaturePointItemInactive{background-position:-77px -1px;}.olControlEditingToolbar .olControlDrawFeaturePointItemActive{background-position:-77px -24px;}.olControlEditingToolbar .olControlDrawFeaturePathItemInactive{background-position:-51px -1px;}.olControlEditingToolbar .olControlDrawFeaturePathItemActive{background-position:-51px -24px;}.olControlEditingToolbar .olControlDrawFeaturePolygonItemInactive{background-position:-26px -1px;}.olControlEditingToolbar .olControlDrawFeaturePolygonItemActive{background-position:-26px -24px;}div.olControlZoom{position:absolute;top:8px;left:8px;background:rgba(255,255,255,0.4);border-radius:4px;padding:2px;}div.olControlZoom a{display:block;color:#FFF;font-size:18px;font-family:'Lucida Grande', Verdana, Geneva, Lucida, Arial, Helvetica, sans-serif;font-weight:700;text-decoration:none;text-align:center;height:22px;width:22px;line-height:19px;background:rgba(0,60,136,0.5);filter:alpha(opacity=80);margin:1px;padding:0;}div.olControlZoom a:hover{background:rgba(0,60,136,0.7);filter:alpha(opacity=100);}a.olControlZoomIn{border-radius:4px 4px 0 0;}a.olControlZoomOut{border-radius:0 0 4px 4px;}.olLayerGrid .olTileImage{-webkit-transition:opacity .2s linear;-moz-transition:opacity .2s linear;-o-transition:opacity .2s linear;transition:opacity .2s linear;}div.olLayerDiv,.olControlNoSelect{-khtml-user-select:none;-moz-user-select:none;}.olPopupContent,.olFramedCloudPopupContent{overflow:auto;padding:5px;}.olDragDown,.olControlDragFeatureOver{cursor:move;}.olBingAttribution.road,.olGoogleAttribution{color:#333;}.olGoogleAttribution.hybrid,.olGoogleAttribution.satellite,span.olGoogleAttribution.hybrid a,span.olGoogleAttribution.satellite a{color:#EEE;}@media only screen and max-width 600px{div.olControlZoom a:hover{background:rgba(0,60,136,0.5);}} -------------------------------------------------------------------------------- /test/openseadragon/2003rosen1799/0001q.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/annotorious/annotorious-v1/3fb5a931428a3bc9e3f48bc905ab042a393d0907/test/openseadragon/2003rosen1799/0001q.jpg -------------------------------------------------------------------------------- /test/openseadragon/2003rosen1799/0001r.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/annotorious/annotorious-v1/3fb5a931428a3bc9e3f48bc905ab042a393d0907/test/openseadragon/2003rosen1799/0001r.jpg -------------------------------------------------------------------------------- /test/openseadragon/2003rosen1799/0001v.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/annotorious/annotorious-v1/3fb5a931428a3bc9e3f48bc905ab042a393d0907/test/openseadragon/2003rosen1799/0001v.jpg -------------------------------------------------------------------------------- /test/openseadragon/openseadragon/images/fullpage_grouphover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/annotorious/annotorious-v1/3fb5a931428a3bc9e3f48bc905ab042a393d0907/test/openseadragon/openseadragon/images/fullpage_grouphover.png -------------------------------------------------------------------------------- /test/openseadragon/openseadragon/images/fullpage_hover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/annotorious/annotorious-v1/3fb5a931428a3bc9e3f48bc905ab042a393d0907/test/openseadragon/openseadragon/images/fullpage_hover.png -------------------------------------------------------------------------------- /test/openseadragon/openseadragon/images/fullpage_pressed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/annotorious/annotorious-v1/3fb5a931428a3bc9e3f48bc905ab042a393d0907/test/openseadragon/openseadragon/images/fullpage_pressed.png -------------------------------------------------------------------------------- /test/openseadragon/openseadragon/images/fullpage_rest.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/annotorious/annotorious-v1/3fb5a931428a3bc9e3f48bc905ab042a393d0907/test/openseadragon/openseadragon/images/fullpage_rest.png -------------------------------------------------------------------------------- /test/openseadragon/openseadragon/images/home_grouphover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/annotorious/annotorious-v1/3fb5a931428a3bc9e3f48bc905ab042a393d0907/test/openseadragon/openseadragon/images/home_grouphover.png -------------------------------------------------------------------------------- /test/openseadragon/openseadragon/images/home_hover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/annotorious/annotorious-v1/3fb5a931428a3bc9e3f48bc905ab042a393d0907/test/openseadragon/openseadragon/images/home_hover.png -------------------------------------------------------------------------------- /test/openseadragon/openseadragon/images/home_pressed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/annotorious/annotorious-v1/3fb5a931428a3bc9e3f48bc905ab042a393d0907/test/openseadragon/openseadragon/images/home_pressed.png -------------------------------------------------------------------------------- /test/openseadragon/openseadragon/images/home_rest.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/annotorious/annotorious-v1/3fb5a931428a3bc9e3f48bc905ab042a393d0907/test/openseadragon/openseadragon/images/home_rest.png -------------------------------------------------------------------------------- /test/openseadragon/openseadragon/images/next_grouphover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/annotorious/annotorious-v1/3fb5a931428a3bc9e3f48bc905ab042a393d0907/test/openseadragon/openseadragon/images/next_grouphover.png -------------------------------------------------------------------------------- /test/openseadragon/openseadragon/images/next_hover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/annotorious/annotorious-v1/3fb5a931428a3bc9e3f48bc905ab042a393d0907/test/openseadragon/openseadragon/images/next_hover.png -------------------------------------------------------------------------------- /test/openseadragon/openseadragon/images/next_pressed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/annotorious/annotorious-v1/3fb5a931428a3bc9e3f48bc905ab042a393d0907/test/openseadragon/openseadragon/images/next_pressed.png -------------------------------------------------------------------------------- /test/openseadragon/openseadragon/images/next_rest.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/annotorious/annotorious-v1/3fb5a931428a3bc9e3f48bc905ab042a393d0907/test/openseadragon/openseadragon/images/next_rest.png -------------------------------------------------------------------------------- /test/openseadragon/openseadragon/images/previous_grouphover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/annotorious/annotorious-v1/3fb5a931428a3bc9e3f48bc905ab042a393d0907/test/openseadragon/openseadragon/images/previous_grouphover.png -------------------------------------------------------------------------------- /test/openseadragon/openseadragon/images/previous_hover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/annotorious/annotorious-v1/3fb5a931428a3bc9e3f48bc905ab042a393d0907/test/openseadragon/openseadragon/images/previous_hover.png -------------------------------------------------------------------------------- /test/openseadragon/openseadragon/images/previous_pressed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/annotorious/annotorious-v1/3fb5a931428a3bc9e3f48bc905ab042a393d0907/test/openseadragon/openseadragon/images/previous_pressed.png -------------------------------------------------------------------------------- /test/openseadragon/openseadragon/images/previous_rest.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/annotorious/annotorious-v1/3fb5a931428a3bc9e3f48bc905ab042a393d0907/test/openseadragon/openseadragon/images/previous_rest.png -------------------------------------------------------------------------------- /test/openseadragon/openseadragon/images/zoomin_grouphover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/annotorious/annotorious-v1/3fb5a931428a3bc9e3f48bc905ab042a393d0907/test/openseadragon/openseadragon/images/zoomin_grouphover.png -------------------------------------------------------------------------------- /test/openseadragon/openseadragon/images/zoomin_hover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/annotorious/annotorious-v1/3fb5a931428a3bc9e3f48bc905ab042a393d0907/test/openseadragon/openseadragon/images/zoomin_hover.png -------------------------------------------------------------------------------- /test/openseadragon/openseadragon/images/zoomin_pressed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/annotorious/annotorious-v1/3fb5a931428a3bc9e3f48bc905ab042a393d0907/test/openseadragon/openseadragon/images/zoomin_pressed.png -------------------------------------------------------------------------------- /test/openseadragon/openseadragon/images/zoomin_rest.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/annotorious/annotorious-v1/3fb5a931428a3bc9e3f48bc905ab042a393d0907/test/openseadragon/openseadragon/images/zoomin_rest.png -------------------------------------------------------------------------------- /test/openseadragon/openseadragon/images/zoomout_grouphover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/annotorious/annotorious-v1/3fb5a931428a3bc9e3f48bc905ab042a393d0907/test/openseadragon/openseadragon/images/zoomout_grouphover.png -------------------------------------------------------------------------------- /test/openseadragon/openseadragon/images/zoomout_hover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/annotorious/annotorious-v1/3fb5a931428a3bc9e3f48bc905ab042a393d0907/test/openseadragon/openseadragon/images/zoomout_hover.png -------------------------------------------------------------------------------- /test/openseadragon/openseadragon/images/zoomout_pressed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/annotorious/annotorious-v1/3fb5a931428a3bc9e3f48bc905ab042a393d0907/test/openseadragon/openseadragon/images/zoomout_pressed.png -------------------------------------------------------------------------------- /test/openseadragon/openseadragon/images/zoomout_rest.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/annotorious/annotorious-v1/3fb5a931428a3bc9e3f48bc905ab042a393d0907/test/openseadragon/openseadragon/images/zoomout_rest.png -------------------------------------------------------------------------------- /test/openseadragon/test.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Annotorious OpenSeadragon Test Page 4 | 5 | 6 | 7 | 34 | 47 | 48 | 49 | 50 |
51 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /test/plugins/plugin-hello-world.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Annotorious Hello World Plugin 4 | 5 | 6 | 16 | 17 | 18 | 19 | 20 |
21 | 22 | 23 | 24 |
25 |

26 | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Ut varius 27 | diam posuere quam molestie vestibulum. Vestibulum non volutpat elit. Integer 28 | vitae felis eget magna rutrum sagittis. Nulla facilisi. Praesent a consectetur 29 | velit. Cras eget nibh est, eu imperdiet mauris. Nulla quis justo urna. Sed eu 30 | rutrum mauris. Integer aliquet nulla sit amet ante mollis pellentesque. 31 | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Ut varius 32 | diam posuere quam molestie vestibulum. Vestibulum non volutpat elit. Integer 33 | vitae felis eget magna rutrum sagittis. Nulla facilisi. Praesent a consectetur 34 | velit. Cras eget nibh est, eu imperdiet mauris. Nulla quis justo urna. Sed eu 35 | rutrum mauris. Integer aliquet nulla sit amet ante mollis pellentesque. 36 | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Ut varius 37 | diam posuere quam molestie vestibulum. Vestibulum non volutpat elit. Integer 38 | vitae felis eget magna rutrum sagittis. Nulla facilisi. Praesent a consectetur 39 | velit. Cras eget nibh est, eu imperdiet mauris. Nulla quis justo urna. Sed eu 40 | rutrum mauris. Integer aliquet nulla sit amet ante mollis pellentesque. 41 |

42 | 43 | 77 | 78 | 79 | -------------------------------------------------------------------------------- /test/qunit-1.12.0.css: -------------------------------------------------------------------------------- 1 | /** 2 | * QUnit v1.12.0 - A JavaScript Unit Testing Framework 3 | * 4 | * http://qunitjs.com 5 | * 6 | * Copyright 2012 jQuery Foundation and other contributors 7 | * Released under the MIT license. 8 | * http://jquery.org/license 9 | */ 10 | 11 | /** Font Family and Sizes */ 12 | 13 | #qunit-tests, #qunit-header, #qunit-banner, #qunit-testrunner-toolbar, #qunit-userAgent, #qunit-testresult { 14 | font-family: "Helvetica Neue Light", "HelveticaNeue-Light", "Helvetica Neue", Calibri, Helvetica, Arial, sans-serif; 15 | } 16 | 17 | #qunit-testrunner-toolbar, #qunit-userAgent, #qunit-testresult, #qunit-tests li { font-size: small; } 18 | #qunit-tests { font-size: smaller; } 19 | 20 | 21 | /** Resets */ 22 | 23 | #qunit-tests, #qunit-header, #qunit-banner, #qunit-userAgent, #qunit-testresult, #qunit-modulefilter { 24 | margin: 0; 25 | padding: 0; 26 | } 27 | 28 | 29 | /** Header */ 30 | 31 | #qunit-header { 32 | padding: 0.5em 0 0.5em 1em; 33 | 34 | color: #8699a4; 35 | background-color: #0d3349; 36 | 37 | font-size: 1.5em; 38 | line-height: 1em; 39 | font-weight: normal; 40 | 41 | border-radius: 5px 5px 0 0; 42 | -moz-border-radius: 5px 5px 0 0; 43 | -webkit-border-top-right-radius: 5px; 44 | -webkit-border-top-left-radius: 5px; 45 | } 46 | 47 | #qunit-header a { 48 | text-decoration: none; 49 | color: #c2ccd1; 50 | } 51 | 52 | #qunit-header a:hover, 53 | #qunit-header a:focus { 54 | color: #fff; 55 | } 56 | 57 | #qunit-testrunner-toolbar label { 58 | display: inline-block; 59 | padding: 0 .5em 0 .1em; 60 | } 61 | 62 | #qunit-banner { 63 | height: 5px; 64 | } 65 | 66 | #qunit-testrunner-toolbar { 67 | padding: 0.5em 0 0.5em 2em; 68 | color: #5E740B; 69 | background-color: #eee; 70 | overflow: hidden; 71 | } 72 | 73 | #qunit-userAgent { 74 | padding: 0.5em 0 0.5em 2.5em; 75 | background-color: #2b81af; 76 | color: #fff; 77 | text-shadow: rgba(0, 0, 0, 0.5) 2px 2px 1px; 78 | } 79 | 80 | #qunit-modulefilter-container { 81 | float: right; 82 | } 83 | 84 | /** Tests: Pass/Fail */ 85 | 86 | #qunit-tests { 87 | list-style-position: inside; 88 | } 89 | 90 | #qunit-tests li { 91 | padding: 0.4em 0.5em 0.4em 2.5em; 92 | border-bottom: 1px solid #fff; 93 | list-style-position: inside; 94 | } 95 | 96 | #qunit-tests.hidepass li.pass, #qunit-tests.hidepass li.running { 97 | display: none; 98 | } 99 | 100 | #qunit-tests li strong { 101 | cursor: pointer; 102 | } 103 | 104 | #qunit-tests li a { 105 | padding: 0.5em; 106 | color: #c2ccd1; 107 | text-decoration: none; 108 | } 109 | #qunit-tests li a:hover, 110 | #qunit-tests li a:focus { 111 | color: #000; 112 | } 113 | 114 | #qunit-tests li .runtime { 115 | float: right; 116 | font-size: smaller; 117 | } 118 | 119 | .qunit-assert-list { 120 | margin-top: 0.5em; 121 | padding: 0.5em; 122 | 123 | background-color: #fff; 124 | 125 | border-radius: 5px; 126 | -moz-border-radius: 5px; 127 | -webkit-border-radius: 5px; 128 | } 129 | 130 | .qunit-collapsed { 131 | display: none; 132 | } 133 | 134 | #qunit-tests table { 135 | border-collapse: collapse; 136 | margin-top: .2em; 137 | } 138 | 139 | #qunit-tests th { 140 | text-align: right; 141 | vertical-align: top; 142 | padding: 0 .5em 0 0; 143 | } 144 | 145 | #qunit-tests td { 146 | vertical-align: top; 147 | } 148 | 149 | #qunit-tests pre { 150 | margin: 0; 151 | white-space: pre-wrap; 152 | word-wrap: break-word; 153 | } 154 | 155 | #qunit-tests del { 156 | background-color: #e0f2be; 157 | color: #374e0c; 158 | text-decoration: none; 159 | } 160 | 161 | #qunit-tests ins { 162 | background-color: #ffcaca; 163 | color: #500; 164 | text-decoration: none; 165 | } 166 | 167 | /*** Test Counts */ 168 | 169 | #qunit-tests b.counts { color: black; } 170 | #qunit-tests b.passed { color: #5E740B; } 171 | #qunit-tests b.failed { color: #710909; } 172 | 173 | #qunit-tests li li { 174 | padding: 5px; 175 | background-color: #fff; 176 | border-bottom: none; 177 | list-style-position: inside; 178 | } 179 | 180 | /*** Passing Styles */ 181 | 182 | #qunit-tests li li.pass { 183 | color: #3c510c; 184 | background-color: #fff; 185 | border-left: 10px solid #C6E746; 186 | } 187 | 188 | #qunit-tests .pass { color: #528CE0; background-color: #D2E0E6; } 189 | #qunit-tests .pass .test-name { color: #366097; } 190 | 191 | #qunit-tests .pass .test-actual, 192 | #qunit-tests .pass .test-expected { color: #999999; } 193 | 194 | #qunit-banner.qunit-pass { background-color: #C6E746; } 195 | 196 | /*** Failing Styles */ 197 | 198 | #qunit-tests li li.fail { 199 | color: #710909; 200 | background-color: #fff; 201 | border-left: 10px solid #EE5757; 202 | white-space: pre; 203 | } 204 | 205 | #qunit-tests > li:last-child { 206 | border-radius: 0 0 5px 5px; 207 | -moz-border-radius: 0 0 5px 5px; 208 | -webkit-border-bottom-right-radius: 5px; 209 | -webkit-border-bottom-left-radius: 5px; 210 | } 211 | 212 | #qunit-tests .fail { color: #000000; background-color: #EE5757; } 213 | #qunit-tests .fail .test-name, 214 | #qunit-tests .fail .module-name { color: #000000; } 215 | 216 | #qunit-tests .fail .test-actual { color: #EE5757; } 217 | #qunit-tests .fail .test-expected { color: green; } 218 | 219 | #qunit-banner.qunit-fail { background-color: #EE5757; } 220 | 221 | 222 | /** Result */ 223 | 224 | #qunit-testresult { 225 | padding: 0.5em 0.5em 0.5em 2.5em; 226 | 227 | color: #2b81af; 228 | background-color: #D2E0E6; 229 | 230 | border-bottom: 1px solid white; 231 | } 232 | #qunit-testresult .module-name { 233 | font-weight: bold; 234 | } 235 | 236 | /** Fixture */ 237 | 238 | #qunit-fixture { 239 | position: absolute; 240 | top: -10000px; 241 | left: -10000px; 242 | width: 1000px; 243 | height: 1000px; 244 | } 245 | --------------------------------------------------------------------------------