├── LICENSE ├── README.md ├── css ├── CAPCreator.css ├── core.css ├── images │ ├── ajax-loader.gif │ └── icons-18-white.png └── jquery.mobile-1.3.0.min.css ├── img ├── cap.png ├── layer-switcher-maximize.png ├── layer-switcher-minimize.png └── mobile-loc.png ├── index.html ├── js ├── CAPCreator.js ├── CAPCreator_test.js ├── OpenLayers.js ├── cap_map.js ├── caplib.js ├── caplib_test.js ├── config.js ├── jquery-1.10.2.min.js ├── jquery-1.10.2.min.map ├── jquery.mobile-1.3.0.min.js ├── moment.min.js ├── run_tests.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 ├── widgets.js └── widgets_test.js └── templates ├── area ├── cmu_b19.xml ├── index.json ├── test_area1.xml └── test_area2.xml └── message ├── exercise.xml ├── index.json ├── test_msg1.xml ├── test_msg2.xml ├── test_msg3.xml ├── test_msg4.xml └── test_msg5.xml /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013, Carnegie Mellon University 2 | All rights reserved. 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 | Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | Redistributions in binary form must reproduce the above copyright notice, this 11 | list of conditions and the following disclaimer in the documentation and/or 12 | other materials provided with the distribution. 13 | 14 | Neither the name of the Carnegie Mellon University nor the names of its 15 | contributors may be used to endorse or promote products derived from 16 | this software without specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 19 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 20 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 22 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 23 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 24 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 25 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 27 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | CAPCreator™ 2 | ========== 3 | 4 | **CAPCreator™** is a simple tool for authoring alerts and other messages using the 5 | [Common Alerting Protocol (v1.2)](http://docs.oasis-open.org/emergency/cap/v1.2/CAP-v1.2-os.html). 6 | 7 | Combined with [CapCollector™](https://github.com/CAPTools/CapCollector), 8 | the tool offers an easy-to-use web form for creating and updating alerts, 9 | authentication mechanisms to control alert publishing, a database for recording 10 | published alerts, and endpoints for external users to view and download active 11 | alerts. 12 | 13 | It is designed to run on any standard web server, both locally hosted and from 14 | cloud-hosted providers. 15 | 16 | **Features** 17 | 18 | * Create and reuse standard templates to simplify new alert generation. 19 | * Required technical fields in the Common Alerting Protocol specification are 20 | populated automatically to ensure alerts are properly and consistently identified 21 | and updated. 22 | * Authentication gives you control over who can officially publish alerts. 23 | * Built-in support for a serving a feed of active alerts, both as XML and as an 24 | embeddable HTML widget. 25 | * Designed to work well on tablets and mobile devices as well as desktops. 26 | 27 | 28 | **A few notable limits on the current version:** 29 | 30 | * Only one language can be used in a single message (however multiple messages 31 | can be authored to address multilingual alerting requirements); 32 | * Only one target area can be specified for an individual alert message (but 33 | it may include multiple polygons and/or circles); and, 34 | * All alerts are assumed to be effective immediately; the "effective" and 35 | "onset" elements are not supported. 36 | 37 | The CAP Creator combines two pieces of the [open-source CAPTools™](https://github.com/CAPTools) 38 | project originally created at Carnegie Mellon University by a team lead by Art 39 | Botterell, one of the original designers of the Common Alerting Protocol. 40 | 41 | **CAPTools™**, **CAPCreator™**, **CAPCollector™** and 42 | **CAPConsumer™** are trademarks of Carnegie Mellon University. 43 | -------------------------------------------------------------------------------- /css/CAPCreator.css: -------------------------------------------------------------------------------- 1 | /* 2 | CAPCreator.css -- styles for CAPCreator 3 | version 0.9 - 20 August 2013 4 | 5 | Copyright (c) 2013, Carnegie Mellon University 6 | All rights reserved. 7 | 8 | Redistribution and use in source and binary forms, with or without modification, are permitted 9 | provided that the following conditions are met: 10 | 11 | * Redistributions of source code must retain the above copyright notice, this list of conditions 12 | and the following disclaimer. 13 | 14 | * Redistributions in binary form must reproduce the above copyright notice, this list of conditions 15 | and the following disclaimer in the documentation and/or other materials provided with the distribution. 16 | 17 | * Neither the name of Carnegie Mellon University nor the names of its contributors may be used to endorse or 18 | promote products derived from this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR 21 | IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 22 | FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS 23 | BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 24 | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR 25 | BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 26 | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 27 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | 29 | Contributors: Art Botterell , , 30 | 31 | */ 32 | 33 | .jqm-header { 34 | padding: 8px; 35 | } 36 | 37 | .page_label { 38 | font-size: 16px; 39 | } 40 | 41 | .tm { 42 | font-size: 12px; 43 | font-weight: normal; 44 | color: gray; 45 | } 46 | 47 | .jqm-content { 48 | margin: 10px 0px 5px 0px; 49 | } 50 | 51 | .jqm-footer { 52 | padding: 3px; 53 | margin: 0px; 54 | } 55 | 56 | /* avoid pink tiles in maps */ 57 | .olImageLoadError { 58 | background-color: transparent !important; 59 | } 60 | 61 | #coords { 62 | margin-left: 5px; 63 | font-size: 0.8em; 64 | float: left; 65 | color: White; 66 | } 67 | 68 | #radius { 69 | width: 180px; 70 | height: 20px; 71 | float:right; 72 | font-size: 0.8em; 73 | text-align: right; 74 | margin-right: 5px; 75 | color: White; 76 | } 77 | 78 | #display_div { 79 | width: 95%; 80 | } 81 | 82 | #map_panel { 83 | background-color: SteelBlue; 84 | } 85 | 86 | .largemap { 87 | width: 100%; 88 | height: 380px; 89 | } 90 | 91 | #map { 92 | cursor: crosshair; 93 | } 94 | 95 | #map_panel, #mode_panel, #review_div { 96 | margin: 0 15px; 97 | } 98 | 99 | #mode_panel { 100 | border: 1px; 101 | } 102 | 103 | #geocode_div { 104 | margin: 0 10px 10px 10px; 105 | } 106 | 107 | input[type=radio] { 108 | cursor: pointer; 109 | } 110 | 111 | input[type=button] { 112 | cursor: pointer; 113 | } 114 | 115 | .tuple-text { 116 | width: 90%!important; 117 | display: inline-block!important; 118 | margin: 0 .2em 0 0!important; 119 | } 120 | 121 | .ui-mini .ui-btn-inner .ui-btn-text { 122 | font-size: 12px; 123 | padding: 2px; 124 | margin: 2px; 125 | } 126 | 127 | .info_block { 128 | font-size: 10pt; 129 | 130 | } 131 | 132 | .field_label { 133 | font-size: 0.7em; 134 | font-weight:bold; 135 | } 136 | 137 | .review-label { 138 | margin-top: 15px; 139 | margin-left: 8px; 140 | } 141 | 142 | #headline_length { 143 | font-style: italic; 144 | } 145 | 146 | #review_div { 147 | font-size: 0.8em; 148 | height: 300px; 149 | background-color: #eee; 150 | overflow: auto; 151 | } 152 | 153 | #about_div { 154 | margin:5px 0px 10px 5px; 155 | font-size: 0.8em; 156 | height: 40px; 157 | background-color: White; 158 | overflow: auto; 159 | } 160 | 161 | #current_alerts_div { 162 | margin: 0px; 163 | font-size: 0.6em; 164 | height: 400px; 165 | background-color: White; 166 | overflow: auto; 167 | } 168 | 169 | #response_status, #response_uuid { 170 | font-size: 0.7em; 171 | margin: 0 0 0 15px; 172 | background-color: white; 173 | } 174 | 175 | .subhead { 176 | margin: 0px 0px 6px 0px; 177 | font-size: 1em; 178 | font-weight: bold; 179 | } 180 | 181 | .html_table { 182 | font-size: 0.9em; 183 | } 184 | 185 | .html_label_cell { 186 | font-weight: bold; 187 | font-size: 0.7em; 188 | background-color: #e0e0e0; 189 | padding: 0px 3px 0px 3px; 190 | text-align:right; 191 | } 192 | 193 | .form_row_div { 194 | margin: 0 15px 15px 15px; 195 | } 196 | 197 | .ui-content { 198 | padding: 0; 199 | } 200 | 201 | .ui-controlgroup-horizontal.ui-mini .ui-btn-inner { 202 | height: auto; 203 | } 204 | 205 | .create-new-template { 206 | padding: 4px 0; 207 | } 208 | 209 | .ui-btn-up-c, .ui-btn-hover-c { 210 | font-weight: normal; 211 | text-shadow: none; 212 | } 213 | 214 | .divider { 215 | width: 100%; 216 | border-top: 1px solid #ddd; 217 | margin: 0 0 15px 0; 218 | } 219 | 220 | a.main, a.next { 221 | whitespace: normal; 222 | width: 200px; 223 | } 224 | 225 | a.next, a.main, a.about { 226 | margin-left: 15px; 227 | margin-right: 15px; 228 | } 229 | 230 | a.about { 231 | width: 200px; 232 | font-style:italic; 233 | font-weight:normal; 234 | } 235 | 236 | a.release { 237 | width: 180px; 238 | margin-top: 8px; 239 | margin-left: 15px; 240 | } 241 | 242 | a.next, a.release { 243 | background: #7c7c7c; 244 | padding: 10px 5px 10px 0; 245 | color: #fff!important; 246 | font-weight: bold!important; 247 | border-radius: 30px; 248 | } 249 | 250 | a.next .ui-btn-inner, a.release .ui-btn-inner { 251 | border-top: none; 252 | } 253 | 254 | option.prepopulated:checked, span.prepopulated, input.prepopulated, 255 | textarea.prepopulated { 256 | color: #42bd41; 257 | } 258 | 259 | .templates-control .templates { 260 | max-width: 15em; 261 | display: inline-block; 262 | } 263 | 264 | #current_alerts_div, #alert_view_div { 265 | padding: 0 15px 15px 15px; 266 | } 267 | 268 | .templates-control .re-apply, .templates-control .clear { 269 | max-width: 15em; 270 | display: inline-block; 271 | font-size: small; 272 | margin-left: 1em; 273 | } 274 | 275 | .re-apply a, .clear a { 276 | text-decoration: none; 277 | display: none; 278 | font-weight: normal; 279 | } 280 | 281 | div.ui-input-text { 282 | max-width: 490px; 283 | padding: 0 5px; 284 | } 285 | 286 | #textarea-description, #textarea-instruction, #textarea-areaDesc { 287 | max-width: 500px; 288 | min-height: 3em; 289 | } 290 | 291 | .required, .required-placeholder, .indicate-required-select, 292 | .invalid-placeholder, .invalid-placeholder-message, 293 | .required-combined-placeholder { 294 | color: red; 295 | } 296 | 297 | .required-placeholder, .required-combined-placeholder, .invalid-placeholder { 298 | font-size: 0.7em; 299 | font-weight: bold; 300 | } 301 | 302 | .required-placeholder, .invalid-placeholder, .required-combined-placeholder { 303 | padding: 0 0 0 15px; 304 | } 305 | 306 | .indicate-required-input, .indicate-invalid-placeholder { 307 | border: 1px solid red; 308 | } 309 | 310 | a.parameter { 311 | display: inline-block; 312 | } 313 | 314 | .hidden { 315 | display: none; 316 | } 317 | 318 | .cap-parameter { 319 | display: inline-block; 320 | max-width: 210px!important; 321 | width: 210px; 322 | } 323 | 324 | .cap-parameter .invalid-placeholder-message { 325 | margin: 3px 0 3px 3px; 326 | overflow-wrap: break-word; 327 | font-size: 0.7em; 328 | font-weight: bold; 329 | } 330 | 331 | /* Make sure datetime picker is in front of other menu and input fields */ 332 | #picker-expires-block { 333 | position: relative; 334 | z-index: 9999; 335 | } 336 | 337 | #ui-datepicker-div .ui-datepicker-next .ui-icon, 338 | #ui-datepicker-div .ui-datepicker-prev .ui-icon { 339 | background-color: transparent; 340 | } 341 | 342 | /* Hide now button from datetime picker */ 343 | .ui-datepicker .ui-datepicker-buttonpane button.ui-datepicker-current { 344 | display: none; 345 | } 346 | 347 | .select2-search__field, .select2-results__option, .select2-selection__choice { 348 | font-family: Helvetica,Arial,sans-serif; 349 | text-shadow: none; 350 | font-size: 14px !important; 351 | } 352 | 353 | -------------------------------------------------------------------------------- /css/core.css: -------------------------------------------------------------------------------- 1 | body { 2 | font-family: arial, sans-serif; 3 | padding: 16px; 4 | margin: 0px; 5 | font-size: 100%; 6 | line-height: 120%; 7 | } 8 | 9 | a { 10 | text-decoration: none; 11 | } 12 | 13 | a:hover { 14 | border-bottom: 1px solid; 15 | padding-bottom: 0.01em; 16 | } 17 | 18 | h2 { 19 | padding:16px 16px 0 16px; 20 | margin:0px; 21 | font-weight: 400; 22 | font-size: x-large; 23 | } 24 | 25 | .content { 26 | padding-right: 16px; 27 | padding-bottom: 16px; 28 | } 29 | 30 | .metadata { 31 | color: #777; 32 | font-size:small; 33 | margin-top:6px; 34 | } 35 | 36 | .subtitle { 37 | padding-left: 16px; 38 | padding-top: 0px; 39 | } 40 | 41 | .normal { 42 | font-size:small; 43 | margin-top:6px; 44 | padding-left:16px; 45 | } 46 | 47 | .alert-list { 48 | list-style-type: none; 49 | padding: 0px; 50 | margin: 0px; 51 | } 52 | 53 | .alert-list li { 54 | padding: 16px; 55 | border-top: 1px solid #e0e0e0; 56 | } 57 | 58 | .alert-list li:first-child { 59 | margin-top: 16px; 60 | } 61 | 62 | .alert-list li:last-child { 63 | border-bottom: 1px solid #e0e0e0; 64 | } 65 | 66 | .alert-list .normal { 67 | padding-left: 0; 68 | } 69 | 70 | .alert-title { 71 | font-size:large; 72 | margin-bottom:0px; 73 | } 74 | 75 | .locations { 76 | border-top: 1px solid #e0e0e0; 77 | padding-top: 16px; 78 | margin-top: 16px; 79 | } 80 | 81 | .instructions { 82 | margin: 16px 0 8px; 83 | padding-top: 16px; 84 | border-top:1px solid #e0e0e0; 85 | } 86 | 87 | .subsection { 88 | font-weight: bold; 89 | } 90 | 91 | .description { 92 | padding-top: 16px; 93 | border-top:1px solid #e0e0e0; 94 | margin-top:16px; 95 | } 96 | 97 | .back { 98 | border-top: 1px solid #e0e0e0; 99 | padding-top: 6px; 100 | } 101 | -------------------------------------------------------------------------------- /css/images/ajax-loader.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CAPTools/CAPCreator/a4191b4bedaf7c3f7525593a4cce167ef60e29fe/css/images/ajax-loader.gif -------------------------------------------------------------------------------- /css/images/icons-18-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CAPTools/CAPCreator/a4191b4bedaf7c3f7525593a4cce167ef60e29fe/css/images/icons-18-white.png -------------------------------------------------------------------------------- /img/cap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CAPTools/CAPCreator/a4191b4bedaf7c3f7525593a4cce167ef60e29fe/img/cap.png -------------------------------------------------------------------------------- /img/layer-switcher-maximize.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CAPTools/CAPCreator/a4191b4bedaf7c3f7525593a4cce167ef60e29fe/img/layer-switcher-maximize.png -------------------------------------------------------------------------------- /img/layer-switcher-minimize.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CAPTools/CAPCreator/a4191b4bedaf7c3f7525593a4cce167ef60e29fe/img/layer-switcher-minimize.png -------------------------------------------------------------------------------- /img/mobile-loc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CAPTools/CAPCreator/a4191b4bedaf7c3f7525593a4cce167ef60e29fe/img/mobile-loc.png -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | CAPCreator 33 | 34 | 35 | 36 | 37 | 38 |
39 | 40 |
41 | Current Alerts    42 |
43 | 44 |
45 | 46 |
47 | 48 | Issue a New Alert 50 | 51 |
52 | Close 53 | 54 |
55 | 57 | 59 |
60 |
61 | 62 | 63 |
64 | About CAPCreator™ 65 | 87 | 88 |
89 | 90 |
91 |
92 | 99 |
100 |
101 | 102 |
103 | 104 | 105 | 106 |
107 | 108 |
109 | New Alert    110 |
111 | 115 |
116 | 117 |
118 | 119 |
120 | 121 | 122 | 125 | 126 |
127 | 128 |
129 | 130 | 136 | 137 | 142 | 143 | 148 |
149 |
150 | 151 |
152 | 153 |
154 | 155 | 170 | 171 | 183 |
184 |
185 | 186 |
187 | 188 |
189 | 190 | 198 | 199 | 207 | 208 | 216 |
217 |
218 | 219 |
220 | 221 |
222 | 223 | 236 |
237 |
238 | 239 | 240 |
241 | 242 | NEXT: Add Alert Details 244 | 245 | 246 |
247 | 248 |
249 |
250 | 257 |
258 |
259 | 260 |
261 | 262 | 263 | 264 |
265 | 266 |
267 | Alert Details    268 |
269 | 270 |
271 | 272 | 273 |
274 | 275 | 276 |
277 | 278 |
279 | 280 | 281 |
282 | 283 |
284 | 285 | 286 |
287 | 288 |
289 | 290 | 291 |
292 | 293 |
294 | 295 | 296 |
297 | 298 |
299 | 300 | 313 | 314 |
315 | 316 | NEXT: Target Area 318 | 319 |
320 | 321 |
322 |
323 | 330 |
331 |
332 | 333 |
334 | 335 | 336 | 337 |
338 | 339 |
340 | Alert Area    341 |
342 | 343 |
344 | 345 | 348 | 349 | 350 | 351 | 352 |
353 |
354 |
355 |
356 | 357 |
358 |
359 | 360 | 361 | 362 | 363 | 364 | 365 | Clear Last 366 | Clear All 367 |
368 |
369 | 370 |
371 | 372 | NEXT: Review & Release 374 | 375 |
376 | 377 |
378 |
379 | 386 |
387 |
388 | 389 |
390 | 391 | 392 | 393 |
394 | 395 |
396 | Review & Release    397 |
398 | 399 |
400 | 401 | 402 |
403 | 404 |
405 | 406 | 407 | 408 | Release Alert 409 |
410 | 411 | 412 | 413 |
414 | 415 |
416 |
417 | 424 |
425 |
426 | 427 |
428 | 429 | 430 | 431 | 432 | -------------------------------------------------------------------------------- /js/CAPCreator.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @license 3 | * Copyright (c) 2013, Carnegie Mellon University 4 | * All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without modification, 7 | * are permitted provided that the following conditions are met: 8 | * 9 | * Redistributions of source code must retain the above copyright notice, this 10 | * list of conditions and the following disclaimer. 11 | * 12 | * Redistributions in binary form must reproduce the above copyright notice, this 13 | * list of conditions and the following disclaimer in the documentation and/or 14 | * other materials provided with the distribution. 15 | * 16 | * Neither the name of the Carnegie Mellon University nor the names of its 17 | * contributors may be used to endorse or promote products derived from 18 | * this software without specific prior written permission. 19 | * 20 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 21 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 22 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 24 | * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 25 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 26 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 27 | * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 29 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | * 31 | * CAPCreator.js -- methods and handlers for CAPCreator 32 | * version 0.9.3 - 12 June 2014 33 | * 34 | * Copyright (c) 2013, Carnegie Mellon University 35 | * All rights reserved. 36 | * 37 | * See LICENSE.txt for license terms (Modified BSD) 38 | * 39 | * DEPENDENCIES AND REQUIREMENTS: 40 | * OpenLayers, jQuery and jQuery Mobile as well as local libraries 41 | * config.js, caplib.js cap_map.js and widgets.js must be loaded in the 42 | * HTML first. 43 | * 44 | * 45 | */ 46 | 47 | var versionID = config.versionID; 48 | var submitUrl = config.CAPCollectorSubmitURL; 49 | var atomUrl = config.atomUrl; 50 | var maxHeadlineLength = config.maxHeadlineLength; 51 | var timeZone = config.timeZone; 52 | 53 | var alert = new Alert(); 54 | var info = alert.addInfo(); 55 | var area = info.addArea(); 56 | 57 | var parameter_set; 58 | var geocodeSet; 59 | var templateAreaDescriptions = []; 60 | var selectedAreaTemplates = {}; 61 | 62 | var area_templates; 63 | var message_templates; 64 | 65 | var messageTemplatePrepopulatedFieldIds = []; 66 | 67 | 68 | // When initializing pages, apply data-set widgets 69 | $(document).on('pageinit', '#info', function() { 70 | parameter_set = new CapTupleSetWidget(gettext('Parameter'), area, 71 | $('#parameter_div')); 72 | $('.tm').html('CAPCreator™ ' + versionID); 73 | $('#textarea-note').val('Using CAPCreator' + versionID); 74 | }); 75 | 76 | 77 | $(document).on('pageshow', '#info', function() { 78 | // On show pick up default language if no language specified. 79 | if (!$('#select-language').val()) { 80 | $('#select-language').val($('#ui-language').val()).selectmenu('refresh'); 81 | } 82 | info.language = $('#ui-language').val(); 83 | }); 84 | 85 | 86 | $(document).on('pageinit', '#area', function() { 87 | $('#select-area-template').select2({ 88 | minimumResultsForSearch: Infinity, 89 | tags: true, 90 | tokenSeparators: [','] 91 | }); 92 | geocodeSet = new CapTupleSetWidget(gettext('Geocode'), area, 93 | $('#geocode_div'), changeGeocode, deleteGeocode); 94 | }); 95 | 96 | function changeGeocode(newTuple, previousTuple) { 97 | if (previousTuple.valueName && previousTuple.value) { 98 | clearGeocodePreview(previousTuple.valueName + '|' + previousTuple.value); 99 | } 100 | if (newTuple.valueName && newTuple.value) { 101 | maybeClearAreaTemplates(null); 102 | loadPreviewPolygons([newTuple]); 103 | } 104 | } 105 | 106 | function deleteGeocode(capTupleWidget) { 107 | var tuple = capTupleWidget.getValue(); 108 | clearGeocodePreview(tuple.valueName + '|' + tuple.value); 109 | maybeClearAreaTemplates(tuple); 110 | } 111 | 112 | function maybeClearAreaTemplates(deletedTuple) { 113 | var areaDescriptionEditedManually = 114 | $('#textarea-areaDesc').val() != templateAreaDescriptions.join(', '); 115 | 116 | if (deletedTuple) { 117 | var matchingTemplateCount = 0; 118 | var matchingSingleGeocodeTemplateKey = null; 119 | // If the tuple being deleted matches exactly one template, remove that one 120 | // from the areaDesc and template list. Else, clear the areaDesc and 121 | // template list (since the user has changed what the templates 122 | // represented, they should fill in a new areaDesc). 123 | $.each(selectedAreaTemplates, function(key) { 124 | var template = this; 125 | $(template.geocodes).each(function() { 126 | if (this.valueName == deletedTuple.valueName 127 | && this.value == deletedTuple.value) { 128 | matchingTemplateCount++; 129 | if (template.geocodes.length == 1) { 130 | matchingSingleGeocodeTemplateKey = key; 131 | } 132 | } 133 | }); 134 | }); 135 | if (matchingTemplateCount == 1 && matchingSingleGeocodeTemplateKey) { 136 | if (!areaDescriptionEditedManually) { 137 | var removedAreaDesc = 138 | selectedAreaTemplates[matchingSingleGeocodeTemplateKey].areaDesc; 139 | templateAreaDescriptions.splice( 140 | templateAreaDescriptions.indexOf(removedAreaDesc), 1); 141 | $('#textarea-areaDesc').val(templateAreaDescriptions.join(', ')); 142 | } 143 | delete selectedAreaTemplates[matchingSingleGeocodeTemplateKey]; 144 | var value = $('#select-area-template').val(); 145 | value.splice(value.indexOf(matchingSingleGeocodeTemplateKey), 1); 146 | $('#select-area-template').val(value).trigger("change"); 147 | return; 148 | } 149 | } 150 | 151 | if (!areaDescriptionEditedManually) { 152 | templateAreaDescriptions = []; 153 | $('#textarea-areaDesc').val(''); 154 | } 155 | selectedAreaTemplates = {}; 156 | $('#select-area-template').val(null).trigger("change"); 157 | } 158 | 159 | function initAlertPage() { 160 | // Hide custom expiration time input on page init. 161 | $('#custom-expiration-time-block').hide(); 162 | // Set default expiration time. 163 | setDefaultAlertExpiration(config.defaultExpiresDurationMinutes, false); 164 | if (config.useDatetimePicker) { 165 | // init datetime picker 166 | // Since second selection is disabled, set minDateTime to be next whole 167 | // minute from now. 168 | var now = moment().tz(timeZone); 169 | var minDate = new Date(now.year(), now.month(), now.date(), 170 | now.hours(), now.minutes() + 1, 0, 0); 171 | 172 | $('#picker-expires').datetimepicker({ 173 | minDateTime: minDate, 174 | timezone: now.format('Z'), 175 | showTimezone: false, 176 | controlType: 'select', 177 | dateFormat: 'yy-mm-dd\'T\'', 178 | timeFormat: 'HH:mm:ss Z', 179 | showSecond: false, 180 | second: 0, 181 | onSelect: function(dateText, inst) { 182 | // Force refresh to ensure drop down updates are picked up. 183 | $('#picker-expires').datetimepicker('refresh'); 184 | } 185 | }); 186 | } 187 | } 188 | 189 | $(document).on('pageinit', '#alert', initAlertPage); 190 | 191 | 192 | // Any time we enter the Current Alerts page, get ATOM feed and update display. 193 | $(document).on('pageshow', '#current', function() { 194 | $.ajax({ 195 | url: atomUrl, 196 | dataType: 'xml', 197 | cache: false, 198 | success: function(data, status, jqXHR) { 199 | var $span = $('#current_alerts_span'); 200 | $span.html(''); 201 | var xml = $.parseXML(jqXHR.responseText); 202 | $(xml).find('entry').each(function() { 203 | $this = $(this); 204 | var sender = $this.find('name').text(); 205 | var title = $this.find('title').text(); 206 | var updated = $this.find('updated').text(); 207 | var link = escape_text($this.find('link').attr('href')); 208 | var urgency = $this.find('urgency').text(); 209 | var severity = $this.find('severity').text(); 210 | var certainty = $this.find('certainty').text(); 211 | var responseType = $this.find('responseType').text(); 212 | var areaDesc = $this.find('areaDesc').text(); 213 | $span.append('' + title + 215 | '
'); 216 | $span.append('FOR: ' + areaDesc + '
'); 217 | $span.append('ACTION: ' + responseType + ' (' + urgency + ' / ' + 218 | severity + ' / ' + certainty + ')
'); 219 | $span.append(updated + ' FROM ' + sender + '
'); 220 | $span.append('
'); 221 | }); 222 | } 223 | }); 224 | }); 225 | 226 | // Any time we go to the Review page, show the CAP XML, clear the authentication 227 | $(document).on('pageshow', '#release', function() { 228 | $('#response_span').html(''); // clear the response message 229 | view2model(); // force an update from screens 230 | $('#review_span').text(alert.getCAP()); 231 | 232 | // TBD qualify alert against profiles / rules 233 | 234 | // Set focus to username field. 235 | $('#text-uid').focus(); 236 | 237 | // Set focus to password field after user pressed enter on username field. 238 | $('#text-uid').keydown(function(event) { 239 | if (event.which == 13) { 240 | $('#text-pwd').focus(); 241 | } 242 | }); 243 | 244 | // Release alert after user pressed enter on password field. 245 | $('#text-pwd').keydown(function(event) { 246 | if (event.which == 13) { 247 | $('#sending-alert-button').trigger('click'); 248 | } 249 | }); 250 | }); 251 | 252 | // character counter/limiter for headline 253 | $(document).on('keyup', '#text-headline', function() { 254 | var current_text = $(this).val(); 255 | if (current_text.length > maxHeadlineLength) { 256 | current_text = current_text.substring(0, maxHeadlineLength); 257 | $(this).val(current_text); 258 | } 259 | $('#headline_counter').text(String(maxHeadlineLength - current_text.length)); 260 | }); 261 | 262 | 263 | // display a current CAP alert in the popup div 264 | function viewAlert(link) { 265 | $.ajax({ 266 | url: link, 267 | dataType: 'xml', 268 | cache: false, 269 | success: function(data, status, jqXHR) { 270 | var xml = jqXHR.responseText; 271 | var $div = $('#alert_view_div'); 272 | var $span = $('#alert_view_span'); 273 | $span.html(''); 274 | $div.popup('open'); 275 | $span.append(cap2html(xml)); 276 | var alert = parseCAP2Alert(xml); 277 | alert.references = alert.sender + ',' + alert.identifier + ',' + 278 | alert.sent; 279 | $('#cancel_button').click(function(e) { 280 | $('#select-message-template').val('None').selectmenu('refresh'); 281 | clearAreaTemplates(); 282 | alert.msgType = 'Cancel'; 283 | clearWebFieldIfAutopopulated(alert); 284 | alert2view(alert); 285 | $.mobile.navigate('#alert'); 286 | }); 287 | $('#update_button').click(function(e) { 288 | $('#select-message-template').val('None').selectmenu('refresh'); 289 | clearAreaTemplates(); 290 | alert.msgType = 'Update'; 291 | clearWebFieldIfAutopopulated(alert); 292 | alert2view(alert); 293 | $.mobile.navigate('#alert'); 294 | loadPreviewPolygons(geocodeSet.getAll()); 295 | }); 296 | $('#view_button').click(function(e) { 297 | var newTab = window.open('/feed/' + alert.identifier + '.xml', 298 | '_blank'); 299 | newTab.focus(); 300 | }); 301 | } 302 | }); 303 | } 304 | 305 | /** 306 | * Clear the web field set automatically by the app when prepopulating alert 307 | * data for update or cancel. 308 | */ 309 | function clearWebFieldIfAutopopulated(alert) { 310 | var info = alert.infos[0]; 311 | if (info.web.indexOf('/feed/' + alert.identifier) != -1) { 312 | info.web = ''; 313 | } 314 | } 315 | 316 | function removeStyles(element) { 317 | if (element) { 318 | var el = $(element); 319 | // Remove pre-populated from template related styles. 320 | el.removeClass('prepopulated'); 321 | // Remove validation related styles. 322 | el.removeClass('indicate-required-select'); 323 | el.closest('.indicate-required-input').removeClass( 324 | 'indicate-required-input'); 325 | el.closest('.indicate-required-select').removeClass( 326 | 'indicate-required-select'); 327 | el.closest('.indicate-invalid-placeholder').removeClass( 328 | 'indicate-invalid-placeholder'); 329 | } 330 | } 331 | 332 | function handleAreaTemplateChange(urlPrefix) { 333 | var selectedArr = $('#select-area-template').val() || []; 334 | handleAddedAreaTemplates(urlPrefix, selectedArr); 335 | handleRemovedAreaTemplates(selectedArr); 336 | } 337 | 338 | function handleAddedAreaTemplates(urlPrefix, selectedArr) { 339 | var addedTemplates = []; 340 | $.each(selectedArr, function() { 341 | if (!selectedAreaTemplates[this]) { 342 | addedTemplates.push(this); 343 | } 344 | }); 345 | if (addedTemplates.length > 0) { 346 | // NOTE: Currently only one template can be added at a time 347 | var addedTemplateId = addedTemplates[0]; 348 | loadTemplate(urlPrefix, addedTemplateId, function(templateAlert) { 349 | var templateInfo = templateAlert.infos[0]; 350 | var templateArea = templateInfo.areas[0]; 351 | var geocodes = []; 352 | 353 | if (!selectedAreaTemplates[addedTemplateId]) { 354 | selectedAreaTemplates[addedTemplateId] = templateArea; 355 | templateAreaDescriptions.push(templateArea.areaDesc); 356 | $(templateArea.geocodes).each(function() { 357 | if (!geocodeSet.contains(this.valueName, this.value)) { 358 | geocodes.push({valueName: this.valueName, value: this.value}); 359 | geocodeSet.addAndPopulate(this.valueName, this.value); 360 | } 361 | }); 362 | $(templateArea.polygons).each(function() { 363 | addCapPolygonToMap(String(this), templateArea.areaDesc); 364 | }); 365 | $(templateArea.circles).each(function() { 366 | addCapCircleToMap(String(this), templateArea.areaDesc); 367 | }); 368 | } 369 | $('#textarea-areaDesc').val(templateAreaDescriptions.join(', ')); 370 | loadPreviewPolygons(geocodes); 371 | }); 372 | } 373 | } 374 | 375 | function handleRemovedAreaTemplates(selectedArr) { 376 | var removedTemplates = []; 377 | $.each(selectedAreaTemplates, function(key) { 378 | if (selectedArr.indexOf(key) == -1) { 379 | removedTemplates.push(key); 380 | } 381 | }); 382 | $.each(removedTemplates, function() { 383 | var templateArea = selectedAreaTemplates[this]; 384 | delete selectedAreaTemplates[this]; 385 | $(templateArea.geocodes).each(function() { 386 | geocodeSet.deleteByValue(this.valueName, this.value, false); 387 | clearGeocodePreview(this.valueName + '|' + this.value); 388 | }); 389 | clearDrawnAreaBySource(templateArea.areaDesc); 390 | templateAreaDescriptions.splice( 391 | templateAreaDescriptions.indexOf(templateArea.areaDesc), 1); 392 | $('#textarea-areaDesc').val(templateAreaDescriptions.join(', ')); 393 | }); 394 | } 395 | 396 | function clearAreaTemplates() { 397 | $('#select-area-template').val(null); 398 | selectedAreaTemplates = {}; 399 | templateAreaDescriptions = []; 400 | $('#textarea-areaDesc').val(''); 401 | geocodeSet.removeAll(); 402 | clearAllDrawnAreas(); 403 | } 404 | 405 | function loadPreviewPolygons(geocodes) { 406 | if (geocodes.length == 0) { 407 | return; 408 | } 409 | 410 | $.ajax({ 411 | url: polygonPreviewUrl, 412 | type: 'POST', 413 | data: { 414 | 'csrfmiddlewaretoken': csrfToken, 415 | 'geocodes': JSON.stringify(geocodes) 416 | }, 417 | dataType: 'json', 418 | success: function(data, textStatus, jqXHR) { 419 | var responseJson = data; 420 | $(responseJson).each(function() { 421 | var id = this.id; 422 | var xml = $.parseXML('' + this.content + ''); 423 | $(xml).find('polygon').each(function() { 424 | addCapPolygonToMap($(this).text(), 'geocodepreview', id); 425 | }); 426 | }); 427 | } 428 | }); 429 | } 430 | 431 | function prepopulateValue(elementId, elementValue) { 432 | if (elementValue) { 433 | messageTemplatePrepopulatedFieldIds.push(elementId); 434 | var element = $(elementId); 435 | element.addClass('prepopulated'); 436 | element.val(elementValue); 437 | } 438 | } 439 | 440 | function prepopulateMenu(elementId, elementValue, opt_highlightPrepopulated) { 441 | if (opt_highlightPrepopulated === undefined) { 442 | opt_highlightPrepopulated = true; 443 | } 444 | var element = $(elementId); 445 | if (elementValue) { 446 | messageTemplatePrepopulatedFieldIds.push(elementId); 447 | element.val(elementValue); 448 | } 449 | if (opt_highlightPrepopulated) { 450 | element.find(':selected').addClass('prepopulated'); 451 | } 452 | element.selectmenu('refresh'); 453 | } 454 | 455 | function setDefaultAlertExpiration(expiresMinutes, highlightPrepopulated) { 456 | prepopulateMenu('#select-expires-min', expiresMinutes, highlightPrepopulated); 457 | if (expiresMinutes == 'Other') { 458 | $('#custom-expiration-time-block').show(); 459 | } else if ($('#select-expires-min').val() != expiresMinutes) { 460 | // If default expiration minutes not in dropdown, select 'Other' and 461 | // populate default. 462 | prepopulateMenu('#select-expires-min', 'Other', highlightPrepopulated); 463 | $('#custom-expiration-time-block').show(); 464 | if (config.useDatetimePicker) { 465 | var alertDate = moment(moment().unix() * 1000 + 466 | expiresMinutes * 60 * 1000).tz(timeZone); 467 | var alertDateStr = alertDate.format('YYYY-MM-DD') + 468 | 'T ' + $.datepicker.formatTime('HH:mm:ss Z', { 469 | hour: alertDate.hours(), 470 | minute: alertDate.minutes(), 471 | second: 0, 472 | timezone: alertDate.format('Z')}); 473 | prepopulateValue('#picker-expires', alertDateStr); 474 | } else { 475 | prepopulateValue('#text-expires', expiresMinutes); 476 | } 477 | } 478 | } 479 | 480 | function handleMessageTemplateChange(urlPrefix, adminUrl) { 481 | var templateId = $('#select-message-template').find(':selected').val(); 482 | if (templateId == 'CreateNewMessageTemplate') { 483 | $('#reapply-message-template').hide(); 484 | createPopupWindow(adminUrl, 800, 400); 485 | return; 486 | } 487 | if (!templateId || templateId == 'None') { 488 | $('.prepopulated').removeClass('prepopulated'); 489 | $('#reapply-message-template').hide(); 490 | return; 491 | } 492 | 493 | loadTemplate(urlPrefix, templateId, function(templateAlert) { 494 | messageTemplatePrepopulatedFieldIds = []; 495 | 496 | $('#custom-expiration-time-block').hide(); 497 | $('#reapply-message-template').hide(); 498 | $('.prepopulated').removeClass('prepopulated'); 499 | prepopulateMenu('#select-message-template'); 500 | 501 | var templateInfo = templateAlert.infos[0]; 502 | // Load message fields into the current view. 503 | 504 | prepopulateMenu('#select-status', templateAlert.status); 505 | // Don't change message type for 'Update' and 'Cancel'. 506 | if (templateAlert.msgType != 'Update' && 507 | templateAlert.msgType != 'Cancel') { 508 | prepopulateMenu('#select-msgType', templateAlert.msgType); 509 | } 510 | prepopulateMenu('#select-scope', templateAlert.scope); 511 | prepopulateValue('#textarea-note', templateAlert.note); 512 | // Only the first value is imported. 513 | if (templateInfo.categories && templateInfo.categories[0]) { 514 | prepopulateMenu('#select-categories', templateInfo.categories[0]); 515 | } 516 | // Only the first value is imported. 517 | if (templateInfo.responseTypes && templateInfo.responseTypes[0]) { 518 | prepopulateMenu('#select-responseTypes', templateInfo.responseTypes[0]); 519 | } 520 | prepopulateMenu('#select-urgency', templateInfo.urgency); 521 | prepopulateMenu('#select-severity', templateInfo.severity); 522 | prepopulateMenu('#select-certainty', templateInfo.certainty); 523 | if (templateAlert.expiresDurationMinutes) { 524 | setDefaultAlertExpiration(templateAlert.expiresDurationMinutes, true); 525 | } else { 526 | setDefaultAlertExpiration(config.defaultExpiresDurationMinutes, false); 527 | } 528 | prepopulateMenu('#select-language', templateInfo.language); 529 | 530 | if (!$('#select-language').val()) { 531 | $('#select-language').val( 532 | $('#ui-language').val()).selectmenu('refresh'); 533 | } 534 | prepopulateValue('#text-senderName', templateInfo.senderName); 535 | prepopulateValue('#text-headline', templateInfo.headline); 536 | prepopulateValue('#text-event', templateInfo.event); 537 | prepopulateValue('#textarea-description', templateInfo.description); 538 | prepopulateValue('#textarea-instruction', templateInfo.instruction); 539 | prepopulateValue('#text-web', templateInfo.web); 540 | prepopulateValue('#text-contact', templateInfo.contact); 541 | prepopulateValue('#text-source', templateInfo.source); 542 | 543 | if (templateInfo.parameters) { 544 | // Clear and reload parameter set in widget. 545 | parameter_set.removeAll(); 546 | $(templateInfo.parameters).each(function() { 547 | parameter_set.addAndPopulate(this.valueName, this.value); 548 | }); 549 | $('.tuple_text_input').addClass('prepopulated'); 550 | } 551 | 552 | // Handle params not visible in the UI 553 | alert.source = templateAlert.source; 554 | alert.restriction = templateAlert.restriction; 555 | alert.addresses = templateAlert.addresses; 556 | alert.code = templateAlert.code; 557 | alert.incidents = templateAlert.incidents; 558 | info.audience = templateInfo.audience; 559 | info.eventCodes = templateInfo.eventCodes; 560 | }); 561 | } 562 | 563 | function createPopupWindow(url, width, height) { 564 | var left = screen.width / 2 - width / 2; 565 | var top = screen.height / 2 - height / 2; 566 | var dimenstions = 'height=' + height + ', width=' + width; 567 | var position = 'top=' + top + ', left=' + left; 568 | var params = dimenstions + ' ' + position; 569 | var newTab = window.open(url, '_blank', params); 570 | newTab.focus(); 571 | } 572 | 573 | function loadTemplate(urlPrefix, templateId, onSuccess) { 574 | var link = urlPrefix + '?template_id=' + templateId; 575 | $.ajax({ 576 | url: link, 577 | dataType: 'xml', 578 | cache: false, 579 | success: function(data, status, jqXHR) { 580 | var xml = jqXHR.responseText; 581 | var alert = parseTemplateToAlert(xml); 582 | onSuccess(alert); 583 | } 584 | }); 585 | } 586 | 587 | function setLanguage(language) { 588 | $.ajax('/i18n/setlang/', { 589 | type: 'POST', 590 | data: { 591 | 'csrfmiddlewaretoken': csrfToken, 592 | 'language': language 593 | }, 594 | dataType: 'text', 595 | success: function(data, textStatus, jqXHR) { 596 | window.location.reload(); 597 | } 598 | }); 599 | } 600 | 601 | 602 | function parseTemplateToAlert(template_xml) { 603 | var xml = $.parseXML(template_xml); 604 | var alert = parseCAP2Alert(xml); 605 | // Non-CAP-compliant fields: 606 | alert.expiresDurationMinutes = $(xml).find('expiresDurationMinutes').text(); 607 | return alert; 608 | } 609 | 610 | 611 | // update model with values from screen 612 | function view2model(element) { 613 | alert.identifier = 'pending'; 614 | alert.sender = 'unverified'; 615 | alert.sent = moment().tz(timeZone).format(); 616 | alert.status = $('#select-status').val(); 617 | alert.msgType = $('#select-msgType').val(); 618 | if ((alert.msgType == 'Update') || (alert.msgType == 'Cancel')) { 619 | $('#hidden-references').prop('readonly', false); 620 | } else { 621 | $('#hidden-references').prop('readonly', true); 622 | } 623 | alert.scope = $('#select-scope').val(); 624 | alert.references = $('#hidden-references').val(); 625 | alert.source = escape_text($('#text-source').val()); 626 | alert.note = escape_text($('#textarea-note').val()); 627 | info.event = escape_text($('#text-event').val()); 628 | info.categories = []; 629 | info.addCategory($('#select-categories').val()); 630 | info.responseTypes = []; 631 | // Set default response type if not set. 632 | info.addResponseType($('#select-responseTypes').val() || 'None'); 633 | info.urgency = $('#select-urgency').val(); 634 | info.severity = $('#select-severity').val(); 635 | info.certainty = $('#select-certainty').val(); 636 | 637 | var expiresInMinutes = $('#select-expires-min').val(); 638 | if (expiresInMinutes == 'Other') { 639 | $('#custom-expiration-time-block').show(); 640 | expiresInMinutes = $('#text-expires').val(); 641 | } else { 642 | $('#custom-expiration-time-block').hide(); 643 | } 644 | var expiresString; 645 | if (!expiresInMinutes) { 646 | if ($('#picker-expires').val()) { 647 | pickedDate = new Date($('#picker-expires').datetimepicker('getDate')); 648 | expiresString = moment(pickedDate).tz(timeZone).format(); 649 | } else { 650 | expiresInMinutes = 60; 651 | } 652 | } 653 | if (!expiresString) { 654 | var expiresInMillis = moment().unix() * 1000 + (expiresInMinutes * 60000); 655 | expiresString = moment(expiresInMillis).tz(timeZone).format(); 656 | } 657 | info.expires = expiresString; 658 | 659 | info.language = $('#select-language').val(); 660 | info.senderName = escape_text($('#text-senderName').val()); 661 | info.headline = escape_text($('#text-headline').val()); 662 | info.description = escape_text($('#textarea-description').val()); 663 | info.instruction = escape_text($('#textarea-instruction').val()); 664 | info.contact = escape_text($('#text-contact').val()); 665 | info.web = escape_text($('#text-web').val() || 'pending'); 666 | if (parameter_set) { info.parameters = parameter_set.getAll(); } 667 | area.areaDesc = escape_text($('#textarea-areaDesc').val()); 668 | area.polygons = getPolygons(); // function getPolygons() from cap_map.js 669 | area.circles = getCircles(); // function getCircles() from cap_map.js 670 | if (geocodeSet) { 671 | area.geocodes = geocodeSet.getAll(); 672 | } 673 | if (element) { 674 | removeStyles(element); 675 | if (messageTemplatePrepopulatedFieldIds.indexOf( 676 | '#' + $(element).attr('id')) != -1 && 677 | $('#select-message-template').val() != 'None') { 678 | $('#reapply-message-template').show(); 679 | } 680 | } 681 | } 682 | 683 | 684 | // submit alert JSON to server 685 | function sendAlert(element) { 686 | var result_message = ''; 687 | var uid = $('#text-uid').val(); 688 | var password = $('#text-pwd').val(); 689 | 690 | $('#sending-alert-button').addClass('hidden'); 691 | $('#sending-alert-indicator').removeClass('hidden'); 692 | $('#response_status').html(''); 693 | $(element).unbind('click'); 694 | 695 | $.ajax(submitUrl, { 696 | type: 'POST', 697 | data: { 698 | 'csrfmiddlewaretoken': csrfToken, 699 | 'uid': uid, 700 | 'password': password, 701 | 'xml': alert.getCAP() 702 | }, 703 | dataType: 'json', 704 | success: function(data, textStatus, jqXHR) { 705 | var response_json = data; 706 | var isValid = response_json['valid']; 707 | if (isValid) { 708 | result_message += 709 | gettext('Success: Valid CAP 1.2 MESSAGE SENT') + '
\n'; 710 | } else { 711 | result_message += gettext('INVALID CAP 1.2') + '
\n'; 712 | result_message += gettext('SERVER MESSAGE') + ': ' + 713 | escape_text(response_json.error) + '\n'; 714 | } 715 | result_uuid = 'UUID: ' + escape_text(response_json['uuid']); 716 | $(element).hide(); 717 | // Display the result. 718 | $('#response_status').html(result_message); 719 | $('#response_uuid').html(result_uuid); 720 | $('#text-uid').val(''); // Clear the uid field. 721 | $('#text-pwd').val(''); // Clear the password field. 722 | parameter_set.removeAll(); // Clear parameter set. 723 | templateAreaDescriptions = []; // Clear area descriptions. 724 | // And after delay, loop back to the "Current Alerts" screen. 725 | setTimeout(function() { window.location.href = '/'; }, 3000); 726 | }, 727 | error: function(data, textStatus, errorThrown) { 728 | setTimeout(function() { 729 | $('#sending-alert-indicator').addClass('hidden'); 730 | $('#sending-alert-button').removeClass('hidden'); 731 | $(element).click(function() { 732 | sendAlert.call(element); 733 | }); 734 | if (data.status == 400) { 735 | result_message = gettext( 736 | 'Please enter valid login and password.'); 737 | $('#response_status').html(result_message); 738 | return; 739 | } else if (data.status == 403) { 740 | result_message = gettext( 741 | 'You are not authorized to release alerts. ' + 742 | 'Ask your app administrator to be added to the ' + 743 | '"can release alerts" group.'); 744 | $('#response_status').html(result_message); 745 | return; 746 | } else { 747 | result_message += gettext('POSSIBLE ERROR IN TRANSMISSION.'); 748 | result_message += 749 | ' ' + gettext('Check active alerts before resending.'); 750 | // Display the results. 751 | $('#response_status').html(result_message); 752 | console.log('Error: ' + data.status + ' ' + data.responseText); 753 | } 754 | }, 300); 755 | } 756 | }); 757 | } 758 | 759 | 760 | // update the screens with values from an Alert object 761 | function alert2view(alert) { 762 | var info = alert.infos[0]; 763 | var area = info.areas[0]; 764 | $('#select-status').val(alert.status).selectmenu('refresh'); 765 | $('#select-msgType').val(alert.msgType).selectmenu('refresh'); 766 | $('#select-scope').val(alert.scope).selectmenu('refresh'); 767 | $('#hidden-references').val(alert.references); 768 | // only the first value is imported 769 | $('#select-categories').val(info.categories[0]).selectmenu('refresh'); 770 | // only the first value is imported 771 | $('#select-responseTypes').val(info.responseTypes[0]).selectmenu('refresh'); 772 | $('#select-urgency').val(info.urgency).selectmenu('refresh'); 773 | $('#select-severity').val(info.severity).selectmenu('refresh'); 774 | $('#select-certainty').val(info.certainty).selectmenu('refresh'); 775 | // expiration is not imported 776 | $('#select-language').val(info.language).selectmenu('refresh'); 777 | $('#text-senderName').val(info.senderName); 778 | $('#text-event').val(info.event); 779 | $('#text-headline').val(info.headline); 780 | $('#textarea-description').val(info.description); 781 | $('#textarea-instruction').val(info.instruction); 782 | $('#text-contact').val(info.contact); 783 | $('#text-source').val(info.source); 784 | $('#text-web').val(info.web); 785 | $('#textarea-note').val(area.note); 786 | 787 | // clear and reload parameter set in widget 788 | if (parameter_set) { 789 | parameter_set.removeAll(); 790 | } 791 | $(info.parameters).each(function() { 792 | parameter_set.addAndPopulate(this.valueName, this.value); 793 | }); 794 | 795 | // resources currently not implemented 796 | 797 | $('#textarea-areaDesc').val(area.areaDesc); 798 | 799 | // clear and reload geocode set in widget 800 | if (geocodeSet) { 801 | geocodeSet.removeAll(); 802 | } 803 | $(area.geocodes).each(function() { 804 | geocodeSet.addAndPopulate(this.valueName, this.value); 805 | }); 806 | 807 | // clear and reload polygons in map 808 | if (drawingLayer) { 809 | drawingLayer.destroyFeatures(); 810 | } 811 | $(area.polygons).each(function() { 812 | addCapPolygonToMap(String(this), area.areaDesc); 813 | }); 814 | // clear and reload circles in map 815 | $(area.circles).each(function() { 816 | addCapCircleToMap(String(this), area.areaDesc); 817 | }); 818 | // altitude is not imported 819 | // ceiling is not imported 820 | 821 | if (area.areaDesc) { 822 | templateAreaDescriptions = area.areaDesc.split(', '); 823 | } 824 | } 825 | 826 | function cap2htmlcells(name, value, opt_colspan) { 827 | return "" + name + 828 | '' + 829 | escape_text(value) + '\n'; 830 | } 831 | 832 | function cap2htmlrow(name, value) { 833 | return '' + cap2htmlcells(name, value, 5) + '\n'; 834 | } 835 | 836 | function cap2htmlrow2(name1, value1, name2, value2) { 837 | return '' + cap2htmlcells(name1, value1) + 838 | cap2htmlcells(name2, value2, 3) + '\n'; 839 | } 840 | 841 | // style CAP XML string as HTML 842 | function cap2html(cap_xml) { 843 | var xml = $.parseXML(cap_xml); 844 | var alert = parseCAP2Alert(cap_xml); 845 | var info = alert.infos[0]; 846 | var area = info.areas[0]; 847 | // create HTML fragment (suitable to insert into a div) 848 | var html = "\n" + 849 | cap2htmlrow('headline', info.headline) + 850 | cap2htmlrow('senderName', info.senderName) + 851 | cap2htmlrow2('sender', alert.sender, 'sent', alert.sent) + 852 | cap2htmlrow2('status', alert.status, 'msgType', alert.msgType) + 853 | '' + cap2htmlcells('urgency', info.urgency) + 854 | cap2htmlcells('severity', info.severity) + 855 | cap2htmlcells('certainty', info.certainty) + '\n' + 856 | cap2htmlrow('response', info.response) + 857 | cap2htmlrow('areaDesc', area.areaDesc) + 858 | cap2htmlrow('description', info.description) + 859 | cap2htmlrow('instruction', info.instruction) + 860 | cap2htmlrow('expires', info.expires) + 861 | cap2htmlrow('identifier', alert.identifier) + 862 | cap2htmlrow2('category', info.categories[0], 'event', info.event) + 863 | '
\n'; 864 | return html; 865 | } 866 | 867 | 868 | function resetAllFields() { 869 | var blankAlert = new Alert(); 870 | var blankInfo = blankAlert.addInfo(); 871 | var blankArea = blankInfo.addArea(); 872 | 873 | // Clear template selections. 874 | $('#select-message-template').val('None').selectmenu('refresh'); 875 | clearAreaTemplates(); 876 | 877 | // Remove styles. 878 | $('.indicate-required-select').removeClass('indicate-required-select'); 879 | $('.prepopulated').removeClass('prepopulated'); 880 | $('.invalid-placeholder').addClass('hidden'); 881 | $('.required-combined-placeholder').addClass('hidden'); 882 | $('.required-placeholder').hide(); 883 | $('#reapply-message-template').hide(); 884 | 885 | alert2view(blankAlert); 886 | } 887 | 888 | 889 | $(document).on('click', '#current-next-button', function() { 890 | resetAllFields(); 891 | $.mobile.navigate('#alert'); 892 | }); 893 | 894 | 895 | function validate(elementId) { 896 | // Hide error messages. 897 | $('.invalid-placeholder-message').addClass('hidden'); 898 | $('.invalid-placeholder-message-error').text(''); 899 | 900 | // Required fields validation. 901 | var requiredFieldsValid = true; 902 | var requiredFields = $(elementId + ' .required-field:visible'); 903 | var requiredPlaceholder = $(elementId + ' .required-placeholder'); 904 | 905 | $.each(requiredFields, function() { 906 | var tagName = $(this).prop('tagName').toLowerCase(); 907 | if (tagName == 'select' && !$(this).find(':selected').val()) { 908 | $(this).closest('.ui-btn-up-c').addClass('indicate-required-select'); 909 | requiredFieldsValid = false; 910 | } else if ((tagName == 'input' || tagName == 'textarea') && 911 | !$(this).val()) { 912 | $(this).parents('div.form_row_div').find( 913 | '.ui-input-text').addClass('indicate-required-input'); 914 | requiredFieldsValid = false; 915 | } 916 | requiredPlaceholder.show(); 917 | }); 918 | 919 | if (requiredFieldsValid) { 920 | requiredPlaceholder.hide(); 921 | } 922 | 923 | // Template placeholders validation. 924 | var templateFieldsValid = true; 925 | var templateFields = $(elementId + ' .placeholder-field'); 926 | var templatePlaceholder = $(elementId + ' .invalid-placeholder'); 927 | 928 | $.each(templateFields, function() { 929 | var fieldValue = $(this).val(); 930 | var templateRegExp = new RegExp('{{.*?}}', 'g'); 931 | var matchArray = fieldValue.match(templateRegExp); 932 | 933 | var validateTemplatesPlaceholder = $(this).parents('.form_row_div').find( 934 | '.invalid-placeholder-message'); 935 | var validateTemplatesError = validateTemplatesPlaceholder.find( 936 | '.invalid-placeholder-message-error'); 937 | 938 | if (matchArray) { 939 | templateFieldsValid = false; 940 | var elementName = $(this).attr('name'); 941 | 942 | if (elementName == 'valueName' || elementName == 'value') { 943 | var parentDiv = $(this).parents('.cap-parameter'); 944 | parentDiv.find('div.tuple-text').addClass( 945 | 'indicate-invalid-placeholder'); 946 | validateTemplatesPlaceholder = parentDiv.find( 947 | '.invalid-placeholder-message'); 948 | validateTemplatesError = validateTemplatesPlaceholder.find( 949 | '.invalid-placeholder-message-error'); 950 | } else { 951 | validateTemplatesError = validateTemplatesPlaceholder.find( 952 | '.invalid-placeholder-message-error'); 953 | $(this).parents('div.form_row_div').find('.ui-input-text').addClass( 954 | 'indicate-invalid-placeholder'); 955 | } 956 | 957 | validateTemplatesPlaceholder.removeClass('hidden'); 958 | validateTemplatesError.text(matchArray[0]); 959 | templatePlaceholder.show(); 960 | } 961 | }); 962 | 963 | if (templateFieldsValid) { 964 | templatePlaceholder.hide(); 965 | } 966 | 967 | return requiredFieldsValid && templateFieldsValid; 968 | } 969 | 970 | 971 | function validateBeforeNavigate(buttonId, currentTab, nextTab) { 972 | $(document).on('click', buttonId, function() { 973 | var inputIsValid = validate(currentTab); // Input validation goes first. 974 | var tabSpecificIsValid = true; 975 | 976 | // Tab specific validations. 977 | if (currentTab == '#area') { // Area tab. 978 | var area = alert.infos[0].areas[0]; 979 | var requiredAreaPlaceholder = $('.required-combined-placeholder'); 980 | var validGeocode = false; 981 | if (area.geocodes.length) { 982 | validGeocode = area.geocodes[0].valueName && area.geocodes[0].value; 983 | } 984 | if (!area.circles.length && !area.polygons.length && !validGeocode) { 985 | requiredAreaPlaceholder.removeClass('hidden'); 986 | tabSpecificIsValid = false; 987 | } else { 988 | requiredAreaPlaceholder.addClass('hidden'); 989 | tabSpecificIsValid = true; 990 | } 991 | } 992 | 993 | if (inputIsValid && tabSpecificIsValid) { 994 | $.mobile.navigate(nextTab); 995 | } 996 | }); 997 | } 998 | 999 | 1000 | validateBeforeNavigate('#alert-next-button', '#alert', '#info'); 1001 | validateBeforeNavigate('#info-next-button', '#info', '#area'); 1002 | validateBeforeNavigate('#area-next-button', '#area', '#release'); 1003 | -------------------------------------------------------------------------------- /js/CAPCreator_test.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @fileoverview CAPCreator.js unit tests. 3 | * @author arcadiy@google.com (Arkadii Yakovets) 4 | */ 5 | 6 | 7 | /* Define unit test module for CAPCreator tests. */ 8 | QUnit.module('CAPCreator.js Unit Tests', { 9 | beforeEach: function() { 10 | this.savedAjax = $.ajax; 11 | }, 12 | afterEach: function() { 13 | $.ajax = this.savedAjax; 14 | } 15 | }); 16 | 17 | 18 | /* Tests that escapedText() function returns expected result. */ 19 | QUnit.test('Test escape_text function', function(assert) { 20 | // Test escaped characters. 21 | var initialText = 'text to be '; 22 | var escapedText = escape_text(initialText); 23 | assert.equal(escapedText, 'text to be <escaped>', 24 | 'Expected: text to be <escaped>'); 25 | 26 | // Test regular characters. 27 | initialText = 'text to be not escaped'; 28 | escapedText = escape_text(initialText); 29 | assert.equal(escapedText, initialText, 'Expected: text to be ' + initialText); 30 | }); 31 | 32 | /** Tests that cap2htmlcells escapes correctly. */ 33 | QUnit.test('Test cap2htmlcells escapes correctly', function(assert) { 34 | assert.equal( 35 | "xy\n", 36 | cap2htmlcells('x', 'y', 5)); 37 | var initial = 'test\'">'; 38 | var escaped = 'test\'"><img src=x onerror=prompt(1)>'; 39 | assert.equal( 40 | "x" + escaped + '\n', 41 | cap2htmlcells('x', initial)); 42 | }); 43 | 44 | 45 | /* Tests that validate() function highlights invalid fields and shows error 46 | * messages. 47 | */ 48 | QUnit.test('Test required field validation', function(assert) { 49 | // DOM fixture for the test. 50 | var html = '
' + 51 | '' + 53 | '
' + 59 | '
'; 61 | $('
').html(html).appendTo($('#qunit-fixture')); 62 | 63 | var elementId = '#info'; 64 | var textEvent = $('#text-event'); 65 | var requiredPlaceholder = $(elementId + ' .required-placeholder'); 66 | 67 | // Test validation failed. 68 | var initialClassList = textEvent.attr('class').split(/\s+/); 69 | assert.equal(initialClassList.indexOf('indicate-required-input'), -1, 70 | 'Expected: indicate-required-input not in initial class list'); 71 | var valid = validate(elementId); 72 | assert.equal(valid, false, 'Expected: validation failed'); 73 | 74 | var validatedClassList = textEvent.attr('class').split(/\s+/); 75 | assert.notEqual(validatedClassList.indexOf('indicate-required-input'), -1, 76 | 'Expected: indicate-required-input in validated class list'); 77 | assert.notEqual(requiredPlaceholder.css('display'), 'none', 78 | 'Expected: required placeholder is shown'); 79 | 80 | // Test validation succeeded. 81 | textEvent.val('Some text event'); 82 | valid = validate('#info'); 83 | assert.ok(valid, 'Expected: validation succeeded'); 84 | validatedClassList = textEvent.attr('class').split(/\s+/); 85 | assert.equal(requiredPlaceholder.css('display'), 'none', 86 | 'Expected: required placeholder is not shown'); 87 | }); 88 | 89 | 90 | /** 91 | * Tests that clearWebFieldIfAutopopulated() function clears text-web correctly. 92 | */ 93 | QUnit.test('Test clearWebFieldIfAutopopulated()', function(assert) { 94 | var initialAlert = new Alert(); 95 | initialAlert.identifier = 'uuid'; 96 | 97 | var initialAlertInfo = initialAlert.addInfo(); 98 | initialAlertInfo.web = 'https://autogenerated.by.captools.com/feed/uuid.html'; 99 | 100 | var initialAlertArea = initialAlertInfo.addArea(); 101 | 102 | clearWebFieldIfAutopopulated(initialAlert); 103 | assert.equal(initialAlertInfo.web, '', 104 | 'Expected: text-web field to be empty'); 105 | 106 | initialAlertInfo.web = 'https://some-alert-provider.com/alerts/' + 107 | 'manually-set.html'; 108 | clearWebFieldIfAutopopulated(initialAlert); 109 | assert.notEqual(initialAlertInfo.web, '', 110 | 'Expected: text-web field not to be empty'); 111 | }); 112 | 113 | 114 | /** 115 | * Tests alert datetime picker working correctly. 116 | */ 117 | QUnit.test('Test datetime picker', function(assert) { 118 | initAlertPage(); 119 | 120 | $('#picker-expires').trigger('click'); 121 | assert.ok($('#ui-datepicker-div').is(':visible'), 'Datetime picker visible'); 122 | 123 | var dateString = '2016-04-04T 15:30:25 -0700'; 124 | $('#picker-expires').val(dateString); 125 | var expectedDate = new Date('2016-04-04T15:30:25-0700'); 126 | var actualDate = new Date($('#picker-expires').datetimepicker('getDate')); 127 | assert.equal(expectedDate.toString(), actualDate.toString(), 128 | 'Expect prepopulated value same as selected.'); 129 | }); 130 | 131 | 132 | /** 133 | * Tests that the area template picker is functioning. 134 | */ 135 | QUnit.test('Test area template picker', function(assert) { 136 | initAlertPage(); 137 | polygonPreviewUrl = 'preview/polygons'; 138 | csrfToken = 'fake'; 139 | geocodeSet = new CapTupleSetWidget(Geocode, area, 140 | $('#geocode_div'), changeGeocode, deleteGeocode); 141 | drawingLayer = {features: []}; 142 | 143 | $.ajax = function(params) { 144 | var responseJson = null; 145 | var responseText = ''; 146 | if (params['url'] == 'template/area?template_id=1') { 147 | responseText = 'Palo Alto' 148 | + 'geocode' 149 | + 'PaloAlto'; 150 | } else if (params['url'] == 'template/area?template_id=2') { 151 | responseText = 'Mountain View' 152 | + 'geocode' 153 | + 'MountainView'; 154 | } else { 155 | responseJson = '[]'; 156 | } 157 | params['success']( 158 | null /* data */, null /* status */, {responseText: responseText}); 159 | }; 160 | 161 | // add an area template 162 | $('#select-area-template').val(['1']).trigger('change'); 163 | assert.deepEqual($('#select-area-template').val(), ['1']); 164 | assert.notEqual(selectedAreaTemplates['1'], undefined); 165 | assert.equal($('#textarea-areaDesc').val(), 'Palo Alto'); 166 | assert.equal(geocodeSet.getAll().length, 1); 167 | assert.equal(geocodeSet.getAll()[0].valueName, 'geocode'); 168 | assert.equal(geocodeSet.getAll()[0].value, 'PaloAlto'); 169 | 170 | // remove it 171 | $('#select-area-template').val(null).trigger('change'); 172 | assert.equal($('#select-area-template').val(), null); 173 | assert.deepEqual(selectedAreaTemplates, {}); 174 | assert.equal($('#textarea-areaDesc').val(), ''); 175 | assert.equal(geocodeSet.getAll().length, 0); 176 | 177 | // add two 178 | $('#select-area-template').val(['1']).trigger('change'); 179 | $('#select-area-template').val(['1', '2']).trigger('change'); 180 | assert.deepEqual($('#select-area-template').val(), ['1', '2']); 181 | assert.notEqual(selectedAreaTemplates['1'], undefined); 182 | assert.notEqual(selectedAreaTemplates['2'], undefined); 183 | assert.equal($('#textarea-areaDesc').val(), 'Palo Alto, Mountain View'); 184 | assert.equal(geocodeSet.getAll().length, 2); 185 | assert.equal(geocodeSet.getAll()[0].valueName, 'geocode'); 186 | assert.equal(geocodeSet.getAll()[0].value, 'PaloAlto'); 187 | assert.equal(geocodeSet.getAll()[1].valueName, 'geocode'); 188 | assert.equal(geocodeSet.getAll()[1].value, 'MountainView'); 189 | 190 | // remove one 191 | $('#select-area-template').val(['2']).trigger('change'); 192 | assert.deepEqual($('#select-area-template').val(), ['2']); 193 | assert.equal(selectedAreaTemplates['1'], undefined); 194 | assert.notEqual(selectedAreaTemplates['2'], undefined); 195 | assert.equal($('#textarea-areaDesc').val(), 'Mountain View'); 196 | assert.equal(geocodeSet.getAll().length, 1); 197 | assert.equal(geocodeSet.getAll()[0].valueName, 'geocode'); 198 | assert.equal(geocodeSet.getAll()[0].value, 'MountainView'); 199 | 200 | // add back the same one, no change 201 | $('#select-area-template').val(['2']).trigger('change'); 202 | assert.deepEqual($('#select-area-template').val(), ['2']); 203 | assert.equal(selectedAreaTemplates['1'], undefined); 204 | assert.notEqual(selectedAreaTemplates['2'], undefined); 205 | assert.equal($('#textarea-areaDesc').val(), 'Mountain View'); 206 | assert.equal(geocodeSet.getAll().length, 1); 207 | assert.equal(geocodeSet.getAll()[0].valueName, 'geocode'); 208 | assert.equal(geocodeSet.getAll()[0].value, 'MountainView'); 209 | 210 | // change a geocode 211 | var tuple = geocodeSet.tuples[0]; 212 | tuple.valueName.find('input').val('foo').change(); 213 | tuple.value.find('input').val('bar').change(); 214 | assert.equal(selectedAreaTemplates['2'], undefined); 215 | assert.equal($('#textarea-areaDesc').val(), ''); 216 | assert.equal($('#select-area-template').val(), null); 217 | assert.equal(geocodeSet.getAll().length, 1); 218 | assert.equal(geocodeSet.getAll()[0].valueName, 'foo'); 219 | assert.equal(geocodeSet.getAll()[0].value, 'bar'); 220 | 221 | // delete a geocode 222 | $('#select-area-template').val(['1']).trigger('change'); 223 | assert.deepEqual($('#select-area-template').val(), ['1']); 224 | assert.notEqual(selectedAreaTemplates['1'], undefined); 225 | assert.equal($('#textarea-areaDesc').val(), 'Palo Alto'); 226 | assert.equal(geocodeSet.getAll().length, 2); 227 | assert.equal(geocodeSet.getAll()[1].valueName, 'geocode'); 228 | assert.equal(geocodeSet.getAll()[1].value, 'PaloAlto'); 229 | geocodeSet.deleteItem({data: geocodeSet.tuples[0]}); 230 | assert.equal($('#textarea-areaDesc').val(), ''); 231 | assert.equal($('#select-area-template').val(), null); 232 | assert.equal(geocodeSet.getAll().length, 1); 233 | 234 | // add two, delete a geocode, remaining template stays selected 235 | $('#select-area-template').val(['1']).trigger('change'); 236 | $('#select-area-template').val(['1', '2']).trigger('change'); 237 | assert.equal(geocodeSet.getAll().length, 2); 238 | geocodeSet.deleteItem({data: geocodeSet.tuples[0]}); 239 | assert.deepEqual($('#select-area-template').val(), ['2']); 240 | assert.equal(selectedAreaTemplates['1'], undefined); 241 | assert.notEqual(selectedAreaTemplates['2'], undefined); 242 | assert.equal($('#textarea-areaDesc').val(), 'Mountain View'); 243 | assert.equal(geocodeSet.getAll().length, 1); 244 | assert.equal(geocodeSet.getAll()[0].valueName, 'geocode'); 245 | assert.equal(geocodeSet.getAll()[0].value, 'MountainView'); 246 | 247 | // manually change area desc, change a geocode 248 | $('#select-area-template').val(['1', '2']).trigger('change'); 249 | $('#textarea-areaDesc').val('manual edit'); 250 | var tuple = geocodeSet.tuples[0]; 251 | tuple.valueName.find('input').val('foo').change(); 252 | tuple.value.find('input').val('bar').change(); 253 | assert.equal($('#textarea-areaDesc').val(), 'manual edit'); 254 | assert.equal($('#select-area-template').val(), null); 255 | assert.equal(geocodeSet.getAll().length, 2); 256 | 257 | geocodeSet.deleteItem({data: geocodeSet.tuples[1]}); 258 | geocodeSet.deleteItem({data: geocodeSet.tuples[0]}); 259 | 260 | // manually change area desc, delete a geocode 261 | $('#select-area-template').val(['1']).trigger('change'); 262 | $('#select-area-template').val(['1', '2']).trigger('change'); 263 | $('#textarea-areaDesc').val('manual edit'); 264 | geocodeSet.deleteItem({data: geocodeSet.tuples[1]}); 265 | assert.equal($('#textarea-areaDesc').val(), 'manual edit'); 266 | assert.deepEqual($('#select-area-template').val(), ['1']); 267 | assert.equal(geocodeSet.getAll().length, 1); 268 | }); 269 | 270 | /** 271 | * Tests datetime fields are in specified timezone. 272 | */ 273 | QUnit.test('Test datetime field in specified time zone', function(assert) { 274 | var expectedTimeZoneOffset = moment().tz(timeZone).format('Z'); 275 | $('#select-expires-min').val(['1']).trigger('change'); 276 | 277 | view2model(null); 278 | // Check sent & expire field datetime string time zone offset same as 279 | // given time zone. 280 | assert.equal(alert.sent.slice(-6), expectedTimeZoneOffset); 281 | assert.equal(info.expires.slice(-6), expectedTimeZoneOffset); 282 | 283 | // Test template timezone 284 | setDefaultAlertExpiration(20, true); 285 | var alertDateStr = $('#picker-expires').val(); 286 | assert.equal(alertDateStr.slice(-6), expectedTimeZoneOffset); 287 | }); 288 | -------------------------------------------------------------------------------- /js/cap_map.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @license 3 | * Copyright (c) 2013, Carnegie Mellon University 4 | * All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without modification, 7 | * are permitted provided that the following conditions are met: 8 | * 9 | * Redistributions of source code must retain the above copyright notice, this 10 | * list of conditions and the following disclaimer. 11 | * 12 | * Redistributions in binary form must reproduce the above copyright notice, this 13 | * list of conditions and the following disclaimer in the documentation and/or 14 | * other materials provided with the distribution. 15 | * 16 | * Neither the name of the Carnegie Mellon University nor the names of its 17 | * contributors may be used to endorse or promote products derived from 18 | * this software without specific prior written permission. 19 | * 20 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 21 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 22 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 24 | * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 25 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 26 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 27 | * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 29 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | * 31 | * cap_map.js -- Map for authoring CAP geometries (circles and polygons) 32 | * version 0.9.3 - 12 June 2014 33 | * 34 | * Copyright (c) 2013, Carnegie Mellon University 35 | * All rights reserved. 36 | * 37 | * See LICENSE.txt for license terms (Modified BSD) 38 | * 39 | * DEPENDENCIES AND REQUIREMENTS: 40 | * OpenLayers, jQuery, jQuery Mobile, config.js and caplib.js must already be loaded into the document. 41 | * Document must have appropriately-sized and -located
s named "map", "coords" and "radius". 42 | * Document must have radio-button set named "noneToggle", "circleToggle" and "polygonToggle" with values "none", "circle" and "polygon" respectively 43 | * 44 | * API: 45 | * renderFeatures() - returns an array of geometry descriptions from the drawing layer 46 | * clearAll() - removes all features from the drawing layer 47 | * clearLast() - removes the last feature added to the drawing layer 48 | * setView(float, float, int) - requires the latitude and longitude to center on and the relative zoom level to view 49 | * 50 | */ 51 | 52 | OpenLayers.ImgPath = config.OpenLayersImgPath; 53 | var Geographic = new OpenLayers.Projection('EPSG:4326'); 54 | var Mercator = new OpenLayers.Projection('EPSG:3857'); // more modern (and official) version of 900913 55 | 56 | var map, drawControls, drawingLayer, cap_area; 57 | 58 | // this is done in advance to support update and creates when the map hasn't been viewed yet 59 | $(document).delegate('#area', 'pageinit', function() { // create drawingLayer immediately 60 | drawingLayer = new OpenLayers.Layer.Vector('Drawing Layer'); 61 | }); 62 | 63 | // wait until the page is visible, to get div size 64 | $(document).delegate('#area', 'pageshow', function() { 65 | 66 | if (!map) { // only initialize the map once! 67 | 68 | if (!cap_area) { 69 | cap_area = new Area('Undesignated Area'); // constructor in caplib.js 70 | } 71 | 72 | map = new OpenLayers.Map({ 73 | div: 'map', 74 | allOverlays: true, 75 | projection: Mercator, 76 | displayProjection: Geographic, 77 | autoUpdateSize: true, 78 | controls: [ 79 | new OpenLayers.Control.Navigation({zoomWheelEnabled: false}), 80 | new OpenLayers.Control.PanPanel(), 81 | new OpenLayers.Control.ZoomPanel(), 82 | new OpenLayers.Control.ArgParser(), 83 | new OpenLayers.Control.Attribution() 84 | ]}); 85 | // create reference layers 86 | var gphyLayer = new OpenLayers.Layer.Google( 87 | 'Google', { 88 | type: google.maps.MapTypeId.TERRAIN, 89 | numZoomLevels: 22 90 | }); 91 | 92 | var osmLayer = new OpenLayers.Layer.OSM('OpenStreetMap'); 93 | osmLayer.setVisibility(false); 94 | 95 | // add all layers to map 96 | map.addLayers([gphyLayer, osmLayer, drawingLayer]); 97 | 98 | // create draw controls 99 | drawControls = { 100 | circle: new OpenLayers.Control.DrawFeature( 101 | drawingLayer, 102 | OpenLayers.Handler.RegularPolygon, 103 | { 104 | handlerOptions: {sides: 40}, 105 | callbacks: {move: showRadius} 106 | } 107 | ), 108 | polygon: new OpenLayers.Control.DrawFeature( 109 | drawingLayer, 110 | OpenLayers.Handler.Polygon 111 | ) 112 | }; 113 | 114 | // prevent map from panning when in "circle" mode 115 | drawControls['circle'].handler.stopDown = stop; 116 | drawControls['circle'].handler.stopUp = stop; 117 | 118 | // reset mode to "Navigate" after each feature 119 | drawingLayer.events.register('featureadded', this, resetToNav); 120 | 121 | // add controls to the map 122 | // ...including the collection of (named) draw controls 123 | for (var key in drawControls) { 124 | map.addControl(drawControls[key]); 125 | } 126 | map.addControl(new OpenLayers.Control.LayerSwitcher()); 127 | 128 | // Default map viewport. 129 | setView(config.mapDefaultViewport.centerLat, 130 | config.mapDefaultViewport.centerLon, 131 | config.mapDefaultViewport.zoomLevel); 132 | } 133 | 134 | }); // end of $(document).bind("pageinit") initialization 135 | 136 | // set the map view 137 | function setView(centerLat, centerLon, zoomLevel) { 138 | var centerCoords = new OpenLayers.LonLat(centerLon, centerLat); 139 | map.setCenter(centerCoords.transform(Geographic, Mercator), zoomLevel); 140 | } 141 | 142 | // handler for radio buttons, activates the corresponding OpenLayers control 143 | function toggleControl(element) { 144 | for (key in drawControls) { 145 | var control = drawControls[key]; 146 | if (element.value == key && element.checked) { 147 | control.activate(); 148 | } else { 149 | control.deactivate(); 150 | } 151 | } 152 | } 153 | 154 | //reset map to Navigate mode (handler for drawingLayer's "featureadded" event) 155 | function resetToNav(feature) { 156 | $('#noneToggle').prop('checked', true).checkboxradio('refresh'); 157 | $('#circleToggle').prop('checked', false).checkboxradio('refresh'); 158 | // sets the radio button 159 | $('#polygonToggle').prop('checked', false).checkboxradio('refresh'); 160 | toggleControl($('#noneToggle')); // and sets the drawing mode 161 | $('#radius').text(''); // clear the radius display, if any 162 | } 163 | 164 | // return an array of polygon strings 165 | function getPolygons() { 166 | var polygons = []; 167 | if (drawingLayer) { 168 | for (var i = 0; i < drawingLayer.features.length; i++) { 169 | var feature = drawingLayer.features[i]; 170 | if (feature.attributes['polygon'] && !feature.attributes['id']) { 171 | polygons.push(polygonToCapXml(feature)); 172 | } 173 | } 174 | } 175 | return polygons; 176 | } 177 | 178 | // return an array of circle strings 179 | function getCircles() { 180 | var circles = []; 181 | if (drawingLayer) { 182 | for (var i = 0; i < drawingLayer.features.length; i++) { 183 | var feature = drawingLayer.features[i]; 184 | if (feature.attributes['circle']) { 185 | circles.push(circleToCapXml(feature)); 186 | } 187 | } 188 | } 189 | return circles; 190 | } 191 | 192 | // clear all features drawn to the draw layer 193 | function clearAllDrawnAreas() { 194 | while(clearLastDrawnArea()); 195 | } 196 | 197 | // remove the last feature added to the draw layer, skipping geocode previews 198 | function clearLastDrawnArea() { 199 | var i = drawingLayer.features.length - 1; 200 | while (i >= 0 && drawingLayer.features[i].attributes && 201 | drawingLayer.features[i].attributes['id']) { 202 | i--; 203 | } 204 | if (i >= 0) { 205 | drawingLayer.destroyFeatures(drawingLayer.features[i]); 206 | return true; 207 | } 208 | return false; 209 | } 210 | 211 | function clearDrawnAreaBySource(source) { 212 | var found = false; 213 | for (var i = 0; i < drawingLayer.features.length; i++) { 214 | var feature = drawingLayer.features[i]; 215 | if (feature.attributes && feature.attributes['source'] == source) { 216 | drawingLayer.destroyFeatures(feature); 217 | found = true; 218 | i--; 219 | } 220 | } 221 | return found; 222 | } 223 | 224 | // clear all geocode preview polygons with the given id from the map 225 | function clearGeocodePreview(id) { 226 | var found = false; 227 | for (var i = 0; i < drawingLayer.features.length; i++) { 228 | var feature = drawingLayer.features[i]; 229 | if (feature.attributes && feature.attributes['id'] == id) { 230 | drawingLayer.destroyFeatures(feature); 231 | found = true; 232 | i--; 233 | } 234 | } 235 | return found; 236 | } 237 | 238 | // add a polygon in CAP string format as a feature on the drawing layer 239 | function addCapPolygonToMap(polygonString, source, opt_id) { 240 | points = []; 241 | pointStrings = polygonString.split(' '); 242 | // note swap of coordinate order 'twixt CAP and OpenLayers 243 | for (var i = 0; i < pointStrings.length; i++) { 244 | var coords = pointStrings[i].split(','); 245 | points.push(new OpenLayers.Geometry.Point( 246 | parseFloat(coords[1]), 247 | parseFloat(coords[0])).transform(Geographic, Mercator)); 248 | } 249 | var ring = new OpenLayers.Geometry.LinearRing(points); 250 | var polygon = new OpenLayers.Geometry.Polygon(ring); 251 | var feature = new OpenLayers.Feature.Vector(polygon); 252 | feature.attributes = { polygon: true, source: source }; 253 | if (opt_id) { 254 | feature.attributes.id = opt_id; 255 | } 256 | drawingLayer.addFeatures([feature]); 257 | } 258 | 259 | //add a circle in CAP string format as a feature on the drawing layer 260 | function addCapCircleToMap(circleString, source) { 261 | var parts = circleString.split(' '); 262 | var radius = parseFloat(parts[1]) * 1000; 263 | var coords = parts[0].split(','); 264 | var centerPoint = new OpenLayers.Geometry.Point( 265 | parseFloat(coords[1]), 266 | parseFloat(coords[0])).transform(Geographic, Mercator); 267 | var circle = new OpenLayers.Geometry.Polygon.createRegularPolygon( 268 | centerPoint, 269 | radius, 270 | 40); 271 | var feature = new OpenLayers.Feature.Vector(circle); 272 | feature.attributes = { circle: true, source: source }; 273 | drawingLayer.addFeatures([feature]); 274 | } 275 | 276 | // handle changes in a textArea with id "areaDesc" 277 | function setAreaDesc() { 278 | cap_area.areaDesc = $('textarea#areaDesc').val(); 279 | } 280 | 281 | /** 282 | VARIOUS UTILITY FUNCTIONS 283 | **/ 284 | 285 | function polygonToCapXml(feature) { 286 | var vertices = feature.geometry.getVertices(); 287 | if (vertices.length < 3) return null; // need at least three vertices 288 | var polygon = []; 289 | var polygonString = ''; 290 | for (var i = 0; i < vertices.length; i++) { 291 | polygonString += pointToRoundedCAPString(vertices[i], 5) + ' '; 292 | } 293 | polygonString += pointToRoundedCAPString(vertices[0], 5); 294 | return polygonString; 295 | } 296 | 297 | function circleToCapXml(circle) { 298 | var centroid = circle.geometry.getCentroid(); 299 | var radius = radiusOfCircle(circle.geometry); 300 | //geographicCentroid = centroid.transform(Mercator, Geographic); 301 | var circleString = pointToRoundedCAPString(centroid, 5) + ' ' + radius; 302 | return circleString; 303 | 304 | } 305 | 306 | // note that this returns the coordinates in CAP (lat,lon) order 307 | function pointToRoundedCAPString(vertex, decimalPoints) { 308 | var geo_point = vertex.transform(Mercator, Geographic); 309 | var string = geo_point.y.toFixed(decimalPoints) + ',' + 310 | geo_point.x.toFixed(decimalPoints); 311 | vertex.transform(Geographic, Mercator); // gotta undo the transform! 312 | return string; 313 | } 314 | 315 | function radiusOfCircle(circle) { 316 | var vertices = circle.getVertices(); 317 | var centroid = circle.getCentroid(); 318 | edgePoint = vertices[0]; 319 | var radius = distanceBetweenPoints(centroid, edgePoint); 320 | return radius; 321 | } 322 | 323 | // both points in Mercator projection 324 | function distanceBetweenPoints(point1, point2) { 325 | return (point1.distanceTo(point2) / 1000).toFixed(5); // in km, geodesic 326 | } 327 | 328 | // display circle radius during draw, handler attached to draw control for 329 | // circle 330 | function showRadius(circle) { 331 | $('#radius').text('Radius: ' + radiusOfCircle(circle) + ' km.'); 332 | } 333 | 334 | // display prompt during polygon draw 335 | function showPrompt() { 336 | $('#radius').text('Double-click to finish.'); 337 | } 338 | 339 | // clear display 340 | function clearPrompt() { 341 | $('#radius').text(''); 342 | } 343 | 344 | // clear the coordinates display, handler attached to "out" event of draw layer 345 | function clearCoords(foo) { 346 | $('#coords').text(''); 347 | } 348 | -------------------------------------------------------------------------------- /js/caplib.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @license 3 | * Copyright (c) 2013, Carnegie Mellon University 4 | * All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without modification, 7 | * are permitted provided that the following conditions are met: 8 | * 9 | * Redistributions of source code must retain the above copyright notice, this 10 | * list of conditions and the following disclaimer. 11 | * 12 | * Redistributions in binary form must reproduce the above copyright notice, this 13 | * list of conditions and the following disclaimer in the documentation and/or 14 | * other materials provided with the distribution. 15 | * 16 | * Neither the name of the Carnegie Mellon University nor the names of its 17 | * contributors may be used to endorse or promote products derived from 18 | * this software without specific prior written permission. 19 | * 20 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 21 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 22 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 24 | * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 25 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 26 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 27 | * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 29 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | * 31 | * caplib.js -- Common Alerting Protocol 1.2 helper library 32 | * version 1.1.3 - 20 April 2015 33 | * 34 | * Copyright (c) 2013, Carnegie Mellon University 35 | * All rights reserved. 36 | * 37 | * See LICENSE.txt for license terms (Modified BSD) 38 | * 39 | * API (see trivial usage example at end of file): 40 | * new Alert() - returns an uninitialized Alert object 41 | * parseCAP2Alert() - turn a CAP XML string into an Alert object 42 | * Alert.getJSON() - returns the Alert (including included Infos, Resources and Elements) as a JSON string 43 | * Alert.getCAP() - returns the Alert as CAP 1.2 XML 44 | * Alert.addInfo() - adds an Info object to the Alert.infos array and returns the new Info object 45 | * Info.addCategory(string) - adds a category value string to the Info.categories array (values are constrained in spec) 46 | * Info.addResponseType(string) - adds a responseType value string to the Info.responseTypes array (values are constrained in spec) 47 | * Info.addEventCode(string, string) - adds an eventCode valueName/value pair to the Info.eventCodes array (values may be constrained in 'valueName' namespace) 48 | * Info.addParameter(string, string) - adds a parameter valueName/value pair to the Info.parameters array (values may be constrained in 'valueName' namespace) 49 | * Alert.addArea(string) - adds an Area object to the Info.areas array, initializes the areaDesc field from argument and returns the new Area object 50 | * Alert.addResource(string) - adds a Resource object to the Info.resources array, initializes the resourceDesk field from argument and returns the new Resource object 51 | * All other properties are populated by direct assignment. All reads are performed by direct reference. 52 | */ 53 | 54 | function xmlEscape(str) { 55 | if (str.indexOf('&') != -1) { 56 | str = str.replace(/&/g, '&'); 57 | } 58 | if (str.indexOf('<') != -1) { 59 | str = str.replace(/') != -1) { 62 | str = str.replace(/>/g, '>'); 63 | } 64 | return str; 65 | } 66 | 67 | function xmlUnescape(str) { 68 | if (str.indexOf('<') != -1) { 69 | str = str.replace(/</g, '<'); 70 | } 71 | if (str.indexOf('>') != -1) { 72 | str = str.replace(/>/g, '>'); 73 | } 74 | if (str.indexOf('&') != -1) { 75 | str = str.replace(/&/g, '&'); 76 | } 77 | return str; 78 | } 79 | 80 | ////////////////////////////////////////////////// 81 | // ALERT Object 82 | var Alert = function() { 83 | this.identifier = ''; // REQUIRED 84 | this.sender = ''; // REQUIRED 85 | this.sent = ''; // REQUIRED 86 | this.status = 'Actual'; // REQUIRED: values Actual, Exercise, System, Test, Draft 87 | this.msgType = 'Alert'; // REQUIRED: values Alert, Update, Cancel, Ack, Error 88 | this.scope = 'Public'; // REQUIRED: values Public, Restricted, Private 89 | this.source = ''; 90 | this.restriction; 91 | this.addresses; 92 | this.code; 93 | this.note = ''; 94 | this.references = ''; 95 | this.incidents; 96 | this.infos = []; 97 | }; 98 | 99 | Alert.prototype.addInfo = function() { 100 | newInfo = new Info(); 101 | this.infos.push(newInfo); 102 | return newInfo; 103 | }; 104 | 105 | Alert.prototype.getJSON = function() { 106 | return JSON.stringify(this, undefined, 2); 107 | }; 108 | 109 | Alert.prototype._xmlTag = function(tagName, value, indent) { 110 | return indent + 111 | '<' + tagName + '>' + 112 | xmlEscape(xmlUnescape(value)) + 113 | '\n'; 114 | } 115 | 116 | Alert.prototype._xmlNameValueTag = function(tagName, name, value, indent) { 117 | var indent2 = indent + ' '; 118 | return indent + '<' + tagName + '>\n' + 119 | this._xmlTag('valueName', name, indent2) + 120 | this._xmlTag('value', value, indent2) + 121 | indent + '\n' 122 | } 123 | 124 | Alert.prototype.getCAP = function() { 125 | var xml = '\n'; 126 | var indent = ' '; 127 | xml += this._xmlTag('identifier', this.identifier, indent); 128 | xml += this._xmlTag('sender', this.sender, indent); 129 | xml += this._xmlTag('sent', this.sent, indent); 130 | xml += this._xmlTag('status', this.status, indent); 131 | xml += this._xmlTag('msgType', this.msgType, indent); 132 | if (this.source && this.source != '') { 133 | xml += this._xmlTag('source', this.source, indent); 134 | } 135 | xml += this._xmlTag('scope', this.scope, indent); 136 | if (this.restriction && this.restriction != '') { 137 | xml += this._xmlTag('restriction', this.restriction, indent); 138 | } 139 | if (this.addresses && this.addresses != '') { 140 | xml += this._xmlTag('addresses', this.addresses, indent); 141 | } 142 | if (this.code && this.code != '') { 143 | xml += this._xmlTag('code', this.code, indent); 144 | } 145 | if (this.note && this.note != '') { 146 | xml += this._xmlTag('note', this.note, indent); 147 | } 148 | if (this.references && this.references != '') { 149 | xml += this._xmlTag('references', this.references, indent); 150 | } 151 | if (this.incidents && this.incidents != '') { 152 | xml += this._xmlTag('incidents', this.incidents, indent); 153 | } 154 | if (this.infos && this.infos.length > 0) { 155 | for (var i = 0; i < this.infos.length; i++) { 156 | var info = this.infos[i]; 157 | xml += indent + '\n'; 158 | indent = ' '; 159 | xml += this._xmlTag('language', info.language, indent); 160 | if (info.categories.length) { 161 | for (var i = 0; i < info.categories.length; i++) { 162 | xml += this._xmlTag('category', info.categories[i], indent); 163 | } 164 | } 165 | xml += this._xmlTag('event', info.event, indent); 166 | if (info.responseTypes && info.responseTypes.length) { 167 | for (var i = 0; i < info.responseTypes.length; i++) { 168 | xml += this._xmlTag('responseType', info.responseTypes[i], indent); 169 | } 170 | } 171 | xml += this._xmlTag('urgency', info.urgency, indent); 172 | xml += this._xmlTag('severity', info.severity, indent); 173 | xml += this._xmlTag('certainty', info.certainty, indent); 174 | if (info.audience && info.audience != '') { 175 | xml += this._xmlTag('audience', info.audience, indent); 176 | } 177 | 178 | if (info.eventCodes && info.eventCodes.length) { 179 | for (var i = 0; i < info.eventCodes.length; i++) { 180 | var ec = info.eventCodes[i]; 181 | xml += this._xmlNameValueTag( 182 | 'eventCode', ec.valueName, ec.value, indent); 183 | } 184 | } 185 | if (info.effective && info.effective != '') { 186 | xml += this._xmlTag('effective', info.effective, indent); 187 | } 188 | if (info.onset && info.onset != '') { 189 | xml += this._xmlTag('onset', info.onset, indent); 190 | } 191 | if (info.expires && info.expires != '') { 192 | xml += this._xmlTag('expires', info.expires, indent); 193 | } 194 | if (info.senderName && info.senderName != '') { 195 | xml += this._xmlTag('senderName', info.senderName, indent); 196 | } 197 | if (info.headline && info.headline != '') { 198 | xml += this._xmlTag('headline', info.headline, indent); 199 | } 200 | if (info.description && info.description != '') { 201 | xml += this._xmlTag('description', info.description, indent); 202 | } 203 | if (info.instruction && info.instruction != '') { 204 | xml += this._xmlTag('instruction', info.instruction, indent); 205 | } 206 | if (info.web && info.web != '') { 207 | xml += this._xmlTag('web', info.web, indent); 208 | } 209 | if (info.contact && info.contact != '') { 210 | xml += this._xmlTag('contact', info.contact, indent); 211 | } 212 | if (info.parameters && info.parameters.length) { 213 | for (var i = 0; i < info.parameters.length; i++) { 214 | var param = info.parameters[i]; 215 | xml += this._xmlNameValueTag( 216 | 'parameter', param.valueName, param.value, indent); 217 | } 218 | } 219 | if (info.resources && info.resources.length > 0) { 220 | for (var i = 0; i < info.resources.length; i++) { 221 | var resource = info.resources[i]; 222 | xml += indent + '\n'; 223 | indent = ' '; 224 | xml += this._xmlTag('resourceDesc', resource.resourceDesc, indent); 225 | if (resource.mimeType && resource.mimeType != '') { 226 | xml += this._xmlTag('mimeType', resource.mimeType, indent); 227 | } 228 | if (resource.size && resource.size != '') { 229 | xml += this._xmlTag('size', resource.size, indent); 230 | } 231 | if (resource.uri && resource.uri != '') { 232 | xml += this._xmlTag('uri', resource.uri, indent); 233 | } 234 | if (resource.digest && resource.digest != '') { 235 | xml += this._xmlTag('digest', resource.digest, indent); 236 | } 237 | indent = ' '; 238 | xml += indent + '\n'; 239 | } 240 | } 241 | if (info.areas && info.areas.length > 0) { 242 | for (var i = 0; i < info.areas.length; i++) { 243 | var area = info.areas[i]; 244 | xml += indent + '\n'; 245 | indent = ' '; 246 | if (area.areaDesc == '') { 247 | area.areaDesc = 'Unspecified Area'; 248 | } 249 | xml += this._xmlTag('areaDesc', area.areaDesc, indent); 250 | if (area.polygons && area.polygons.length) { 251 | for (var i = 0; i < area.polygons.length; i++) { 252 | xml += this._xmlTag('polygon', area.polygons[i], indent); 253 | } 254 | } 255 | if (area.circles && area.circles.length) { 256 | for (var i = 0; i < area.circles.length; i++) { 257 | xml += this._xmlTag('circle', area.circles[i], indent); 258 | } 259 | } 260 | if (area.geocodes && area.geocodes.length) { 261 | for (var i = 0; i < area.geocodes.length; i++) { 262 | var gc = area.geocodes[i]; 263 | xml += this._xmlNameValueTag( 264 | 'geocode', gc.valueName, gc.value, indent); 265 | } 266 | } 267 | if (area.altitude && area.altitude != '') { 268 | xml += this._xmlTag('altitude', area.altitude, indent); 269 | } 270 | if (area.ceiling && area.ceiling != '') { 271 | xml += this._xmlTag('ceiling', area.ceiling, indent); 272 | } 273 | indent = ' '; 274 | xml += indent + '\n'; 275 | } 276 | } 277 | indent = ' '; 278 | xml += indent + '\n'; 279 | } 280 | } 281 | xml += ''; 282 | return xml; 283 | }; 284 | 285 | 286 | ///////////////////////////////////////////// 287 | // INFO Object 288 | var Info = function() { 289 | this.language = ''; 290 | // Values: Geo, Met, Safety, Security, Rescue, Fire, Health, Env, Transport, 291 | // Infra, CBRNE, Other. 292 | this.categories = []; // REQUIRED 293 | this.event = ''; // REQUIRED 294 | this.responseTypes = []; 295 | // Values: Immediate, Expected, Future, Past, Unknown. 296 | this.urgency = ''; // REQUIRED 297 | // Values: Extreme, Severe, Moderate, Minor, Unknown. 298 | this.severity = ''; // REQUIRED 299 | // Values: Observed, Likely, Possible, Unlikely, Unknown. 300 | this.certainty = ''; // REQUIRED 301 | this.audience = ''; 302 | this.eventCodes = []; 303 | this.effective = ''; 304 | this.onset = ''; 305 | this.expires = ''; 306 | this.senderName = ''; 307 | this.headline = ''; 308 | this.description = ''; 309 | this.instruction = ''; 310 | this.web = ''; 311 | this.contact = ''; 312 | this.resources = []; 313 | this.parameters = []; 314 | this.areas = []; 315 | }; 316 | 317 | // Geo, Met, Safety, Security, Rescue, Fire, Health, Env, Transport, Infra, 318 | // CBRNE, Other. 319 | Info.prototype.addCategory = function(category) { 320 | this.categories.push(category); 321 | }; 322 | 323 | // Shelter, Evacuate, Prepare, Execute, Avoid, Monitor, Assess, AllClear 324 | Info.prototype.addResponseType = function(responseType) { 325 | this.responseTypes.push(responseType); 326 | }; 327 | Info.prototype.addEventCode = function(valueName, value) { 328 | var eventCode = new EventCode(valueName, value); 329 | this.eventCodes.push(eventCode); 330 | }; 331 | 332 | Info.prototype.addParameter = function(valueName, value) { 333 | var parameter = new Parameter(valueName, value); 334 | this.parameters.push(parameter); 335 | }; 336 | Info.prototype.addArea = function(areaDesc) { 337 | var area = new Area(areaDesc); 338 | this.areas.push(area); 339 | return area; 340 | }; 341 | Info.prototype.addResource = function(resourceDesc) { 342 | var resource = new Resource(resourceDesc); 343 | this.resources.push(resource); 344 | return resource; 345 | }; 346 | 347 | var EventCode = function(valueName, value) { 348 | this.valueName = valueName; 349 | this.value = value; 350 | }; 351 | 352 | var Parameter = function(valueName, value) { 353 | this.valueName = valueName; 354 | this.value = value; 355 | }; 356 | 357 | 358 | //RESOURCE Object 359 | var Resource = function(resourceDesc) { 360 | this.resourceDesc = resourceDesc; // REQUIRED 361 | this.mimeType; 362 | this.size; 363 | this.uri; 364 | this.digest; 365 | // note: derefURI is not implemented in this tool 366 | }; 367 | Resource.prototype.getJSON = function() { 368 | return JSON.stringify(this); 369 | }; 370 | 371 | 372 | // AREA Object 373 | var Area = function() { 374 | this.areaDesc = ''; // REQUIRED 375 | this.polygons = []; 376 | this.circles = []; 377 | this.geocodes = []; 378 | this.altitude = ''; 379 | this.ceiling = ''; 380 | }; 381 | 382 | Area.prototype.addPolygon = function(polygon) { 383 | this.polygons.push(polygon); 384 | }; 385 | 386 | Area.prototype.addCircle = function(circle) { 387 | this.circles.push(circle); 388 | }; 389 | 390 | Area.prototype.addGeocode = function(valueName, value) { 391 | var geocode = new Geocode(valueName, value); 392 | this.geocodes.push(geocode); 393 | }; 394 | 395 | var Geocode = function(valueName, value) { 396 | this.valueName = valueName; 397 | this.value = value; 398 | }; 399 | 400 | // UTILITIES 401 | 402 | //parse XML string into an Alert object 403 | function parseCAP2Alert(cap_xml) { 404 | var xml = typeof cap_xml === 'object' ? cap_xml : $.parseXML(cap_xml); 405 | // populate new alert with values from XML 406 | var alert = new Alert(); 407 | alert.identifier = $(xml).find('identifier').text(); 408 | alert.sender = $(xml).find('sender').text(); 409 | alert.sent = $(xml).find('sent').text(); 410 | alert.status = $(xml).find('status').text(); 411 | alert.msgType = $(xml).find('msgType').text(); 412 | alert.source = $(xml).find('source').text(); 413 | alert.scope = $(xml).find('scope').text(); 414 | alert.restriction = $(xml).find('restriction').text(); 415 | alert.addresses = $(xml).find('addresses').text(); 416 | alert.code = $(xml).find('code').text(); 417 | alert.note = $(xml).find('note').text(); 418 | alert.references = $(xml).find('references').text(); 419 | alert.incidents = $(xml).find('incidents').text(); 420 | var info = alert.addInfo(); // only one Info is supported in current version! 421 | info.language = $(xml).find('language').text(); 422 | $(xml).find('category').each(function() { 423 | info.addCategory($(this).text()); 424 | }); 425 | info.event = $(xml).find('event').text(); 426 | $(xml).find('responseType').each(function() { 427 | info.addResponseType($(this).text()); 428 | }); 429 | info.urgency = $(xml).find('urgency').text(); 430 | info.severity = $(xml).find('severity').text(); 431 | info.certainty = $(xml).find('certainty').text(); 432 | info.audience = $(xml).find('audience').text(); 433 | $(xml).find('eventCode').each(function() { 434 | info.addEventCode( 435 | $(this).find('valueName').text(), $(this).find('value').text()); 436 | }); 437 | info.effective = $(xml).find('effective').text(); 438 | info.onset = $(xml).find('onset').text(); 439 | info.expires = $(xml).find('expires').text(); 440 | info.senderName = $(xml).find('senderName').text(); 441 | info.headline = $(xml).find('headline').text(); 442 | info.description = $(xml).find('description').text(); 443 | info.instruction = $(xml).find('instruction').text(); 444 | info.web = $(xml).find('web').text(); 445 | info.contact = $(xml).find('contact').text(); 446 | $(xml).find('resource').each(function() { 447 | var resourceDesc = $(this).find('resourceDesc').text(); 448 | var resource = info.addResource(resourceDesc); 449 | resource.mimeType = $(this).find('mimeType').text(); 450 | resource.size = $(this).find('size').text(); 451 | resource.uri = $(this).find('uri').text(); 452 | resource.digest = $(this).find('digest').text(); 453 | }); 454 | $(xml).find('parameter').each(function() { 455 | var parameter = info.addParameter( 456 | $(this).find('valueName').text(), $(this).find('value').text()); 457 | }); 458 | var area = info.addArea(); // Only one Area is supported in current version! 459 | area.areaDesc = $(xml).find('areaDesc').text(); 460 | $(xml).find('polygon').each(function() { 461 | area.addPolygon($(this).text()); 462 | }); 463 | $(xml).find('circle').each(function() { 464 | area.addCircle($(this).text()); 465 | }); 466 | $(xml).find('geocode').each(function() { 467 | var geocode = area.addGeocode( 468 | $(this).find('valueName').text(), $(this).find('value').text()); 469 | }); 470 | area.altitude = $(xml).find('altitude').text(); 471 | area.ceiling = $(xml).find('ceiling').text(); 472 | return alert; 473 | } 474 | 475 | 476 | ////////////////////////////////////////////////////// 477 | // trivial example code (uncomment to test from command line) 478 | /* 479 | newAlert = new Alert(); 480 | info = newAlert.addInfo(); 481 | info.addParameter("parameter_type", "silly"); 482 | // testing unicode, that's Thai for Bangkok 483 | area = info.addArea("กรุงเทพมหานคร"); 484 | area.addCircle("100.54386,13.81390 30.99990"); 485 | alert(newAlert.getJSON()); 486 | */ 487 | -------------------------------------------------------------------------------- /js/caplib_test.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @fileoverview caplib.js unit tests. 3 | */ 4 | 5 | 6 | /* Define unit test module for caplib tests. */ 7 | QUnit.module('caplib.js Unit Tests'); 8 | 9 | /** Test to/from XML */ 10 | QUnit.test('Test to/from XML', function(assert) { 11 | var expected = getFullAlertXml(); 12 | var alert = parseCAP2Alert(expected); 13 | var actual = alert.getCAP(); 14 | assert.equal(expected, actual); 15 | }); 16 | 17 | function getFullAlertXml(value) { 18 | return '\n' + 19 | ' id2\n' + 20 | ' test@example.com\n' + 21 | ' 2015-04-21T19:19:08+00:00\n' + 22 | ' Actual\n' + 23 | ' Update\n' + 24 | ' source\n' + 25 | ' Public\n' + 26 | ' restriction\n' + 27 | ' addresses\n' + 28 | ' code\n' + 29 | ' note\n' + 30 | ' test@example.com,id1,2015-04-21T19:15:00+00:00\n' + 31 | ' incidents\n' + 32 | ' \n' + 33 | ' en-US\n' + 34 | ' Met\n' + 35 | ' Safety\n' + 36 | ' Event\n' + 37 | ' Evacuate\n' + 38 | ' Immediate\n' + 39 | ' Extreme\n' + 40 | ' Observed\n' + 41 | ' audience\n' + 42 | ' \n' + 43 | ' eventCodeName\n' + 44 | ' eventCodeValue\n' + 45 | ' \n' + 46 | ' \n' + 47 | ' eventCodeName2\n' + 48 | ' eventCodeValue2\n' + 49 | ' \n' + 50 | ' 2015-04-21T19:19:09+00:00\n' + 51 | ' 2015-04-21T19:19:00+00:00\n' + 52 | ' 2015-04-21T20:20:08+00:00\n' + 53 | ' Sender Name\n' + 54 | ' Headline\n' + 55 | ' Description & stuff\n' + 56 | ' Instruction\n' + 57 | ' http://www.example.com\n' + 58 | ' contact\n' + 59 | ' \n' + 60 | ' paramName\n' + 61 | ' paramValue\n' + 62 | ' \n' + 63 | ' \n' + 64 | ' paramName2\n' + 65 | ' paramValue2\n' + 66 | ' \n' + 67 | ' \n' + 68 | ' resourceDesc\n' + 69 | ' text/html\n' + 70 | ' 1024\n' + 71 | ' http://example.com/foo\n' + 72 | ' digest\n' + 73 | ' \n' + 74 | ' \n' + 75 | ' resourceDesc2\n' + 76 | ' text/html\n' + 77 | ' http://example.com/foo2\n' + 78 | ' \n' + 79 | ' \n' + 80 | ' areaDesc2\n' + 81 | ' 1,1 2,1 2,2 1,2 1,1\n' + 82 | ' 0,0,1000\n' + 83 | ' \n' + 84 | ' geoName2\n' + 85 | ' geoValue2\n' + 86 | ' \n' + 87 | ' \n' + 88 | ' geoName3\n' + 89 | ' geoValue3\n' + 90 | ' \n' + 91 | ' 500\n' + 92 | ' 800\n' + 93 | ' \n' + 94 | ' \n' + 95 | ''; 96 | } 97 | -------------------------------------------------------------------------------- /js/config.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @license 3 | * Copyright (c) 2013, Carnegie Mellon University 4 | * All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without modification, 7 | * are permitted provided that the following conditions are met: 8 | * 9 | * Redistributions of source code must retain the above copyright notice, this 10 | * list of conditions and the following disclaimer. 11 | * 12 | * Redistributions in binary form must reproduce the above copyright notice, this 13 | * list of conditions and the following disclaimer in the documentation and/or 14 | * other materials provided with the distribution. 15 | * 16 | * Neither the name of the Carnegie Mellon University nor the names of its 17 | * contributors may be used to endorse or promote products derived from 18 | * this software without specific prior written permission. 19 | * 20 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 21 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 22 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 24 | * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 25 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 26 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 27 | * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 29 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | * 31 | * config.js -- configuration variables for CAPCreator 32 | * version 0.9.3 - 12 June 2014 33 | * 34 | * Copyright (c) 2013, Carnegie Mellon University 35 | * All rights reserved. 36 | * 37 | * See LICENSE.txt for license terms (Modified BSD) 38 | */ 39 | 40 | 41 | /** 42 | * Set these for local configuration. 43 | * @param {{centerLat: float, centerLon: float, 44 | * centerZoom: int}} mapDefaultViewport 45 | * @param {string} defaultExpiresDurationMinutes Default expires 46 | * duration in mintues. Can set to "Others" if input manually. 47 | * @param {boolean} useDatetimePicker Whether to use datetime picker for 48 | * for alert expiration field when "Other" option is selected. 49 | * @param {string} timeZone The time zone used for this installation. 50 | * @constructor 51 | */ 52 | var CAPCreatorConfiguration = function(mapDefaultViewport, 53 | defaultExpiresDurationMinutes, 54 | useDatetimePicker, 55 | timeZone) { 56 | this.CAPCollectorBaseURL = "/"; 57 | this.CAPCollectorSubmitURL = "/post/"; 58 | /* Relative path to the "img" subdirectory of OpenLayers install. */ 59 | this.OpenLayersImgPath = "client/img/"; 60 | this.mapDefaultViewport = mapDefaultViewport; 61 | this.atomUrl = "/feed.xml"; 62 | this.defaultExpiresDurationMinutes = defaultExpiresDurationMinutes; 63 | this.maxHeadlineLength = 140; 64 | this.versionID = "0.9.3"; 65 | this.useDatetimePicker = useDatetimePicker; 66 | this.timeZone = timeZone; 67 | } 68 | -------------------------------------------------------------------------------- /js/moment.min.js: -------------------------------------------------------------------------------- 1 | // moment.js 2 | // version : 2.1.0 3 | // author : Tim Wood 4 | // license : MIT 5 | // momentjs.com 6 | !function(t){function e(t,e){return function(n){return u(t.call(this,n),e)}}function n(t,e){return function(n){return this.lang().ordinal(t.call(this,n),e)}}function s(){}function i(t){a(this,t)}function r(t){var e=t.years||t.year||t.y||0,n=t.months||t.month||t.M||0,s=t.weeks||t.week||t.w||0,i=t.days||t.day||t.d||0,r=t.hours||t.hour||t.h||0,a=t.minutes||t.minute||t.m||0,o=t.seconds||t.second||t.s||0,u=t.milliseconds||t.millisecond||t.ms||0;this._input=t,this._milliseconds=u+1e3*o+6e4*a+36e5*r,this._days=i+7*s,this._months=n+12*e,this._data={},this._bubble()}function a(t,e){for(var n in e)e.hasOwnProperty(n)&&(t[n]=e[n]);return t}function o(t){return 0>t?Math.ceil(t):Math.floor(t)}function u(t,e){for(var n=t+"";n.lengthn;n++)~~t[n]!==~~e[n]&&r++;return r+i}function f(t){return t?ie[t]||t.toLowerCase().replace(/(.)s$/,"$1"):t}function l(t,e){return e.abbr=t,x[t]||(x[t]=new s),x[t].set(e),x[t]}function _(t){if(!t)return H.fn._lang;if(!x[t]&&A)try{require("./lang/"+t)}catch(e){return H.fn._lang}return x[t]}function m(t){return t.match(/\[.*\]/)?t.replace(/^\[|\]$/g,""):t.replace(/\\/g,"")}function y(t){var e,n,s=t.match(E);for(e=0,n=s.length;n>e;e++)s[e]=ue[s[e]]?ue[s[e]]:m(s[e]);return function(i){var r="";for(e=0;n>e;e++)r+=s[e]instanceof Function?s[e].call(i,t):s[e];return r}}function M(t,e){function n(e){return t.lang().longDateFormat(e)||e}for(var s=5;s--&&N.test(e);)e=e.replace(N,n);return re[e]||(re[e]=y(e)),re[e](t)}function g(t,e){switch(t){case"DDDD":return V;case"YYYY":return X;case"YYYYY":return $;case"S":case"SS":case"SSS":case"DDD":return I;case"MMM":case"MMMM":case"dd":case"ddd":case"dddd":return R;case"a":case"A":return _(e._l)._meridiemParse;case"X":return B;case"Z":case"ZZ":return j;case"T":return q;case"MM":case"DD":case"YY":case"HH":case"hh":case"mm":case"ss":case"M":case"D":case"d":case"H":case"h":case"m":case"s":return J;default:return new RegExp(t.replace("\\",""))}}function p(t){var e=(j.exec(t)||[])[0],n=(e+"").match(ee)||["-",0,0],s=+(60*n[1])+~~n[2];return"+"===n[0]?-s:s}function D(t,e,n){var s,i=n._a;switch(t){case"M":case"MM":i[1]=null==e?0:~~e-1;break;case"MMM":case"MMMM":s=_(n._l).monthsParse(e),null!=s?i[1]=s:n._isValid=!1;break;case"D":case"DD":case"DDD":case"DDDD":null!=e&&(i[2]=~~e);break;case"YY":i[0]=~~e+(~~e>68?1900:2e3);break;case"YYYY":case"YYYYY":i[0]=~~e;break;case"a":case"A":n._isPm=_(n._l).isPM(e);break;case"H":case"HH":case"h":case"hh":i[3]=~~e;break;case"m":case"mm":i[4]=~~e;break;case"s":case"ss":i[5]=~~e;break;case"S":case"SS":case"SSS":i[6]=~~(1e3*("0."+e));break;case"X":n._d=new Date(1e3*parseFloat(e));break;case"Z":case"ZZ":n._useUTC=!0,n._tzm=p(e)}null==e&&(n._isValid=!1)}function Y(t){var e,n,s=[];if(!t._d){for(e=0;7>e;e++)t._a[e]=s[e]=null==t._a[e]?2===e?1:0:t._a[e];s[3]+=~~((t._tzm||0)/60),s[4]+=~~((t._tzm||0)%60),n=new Date(0),t._useUTC?(n.setUTCFullYear(s[0],s[1],s[2]),n.setUTCHours(s[3],s[4],s[5],s[6])):(n.setFullYear(s[0],s[1],s[2]),n.setHours(s[3],s[4],s[5],s[6])),t._d=n}}function w(t){var e,n,s=t._f.match(E),i=t._i;for(t._a=[],e=0;eo&&(u=o,s=n);a(t,s)}function v(t){var e,n=t._i,s=K.exec(n);if(s){for(t._f="YYYY-MM-DD"+(s[2]||" "),e=0;4>e;e++)if(te[e][1].exec(n)){t._f+=te[e][0];break}j.exec(n)&&(t._f+=" Z"),w(t)}else t._d=new Date(n)}function T(e){var n=e._i,s=G.exec(n);n===t?e._d=new Date:s?e._d=new Date(+s[1]):"string"==typeof n?v(e):d(n)?(e._a=n.slice(0),Y(e)):e._d=n instanceof Date?new Date(+n):new Date(n)}function b(t,e,n,s,i){return i.relativeTime(e||1,!!n,t,s)}function S(t,e,n){var s=W(Math.abs(t)/1e3),i=W(s/60),r=W(i/60),a=W(r/24),o=W(a/365),u=45>s&&["s",s]||1===i&&["m"]||45>i&&["mm",i]||1===r&&["h"]||22>r&&["hh",r]||1===a&&["d"]||25>=a&&["dd",a]||45>=a&&["M"]||345>a&&["MM",W(a/30)]||1===o&&["y"]||["yy",o];return u[2]=e,u[3]=t>0,u[4]=n,b.apply({},u)}function F(t,e,n){var s,i=n-e,r=n-t.day();return r>i&&(r-=7),i-7>r&&(r+=7),s=H(t).add("d",r),{week:Math.ceil(s.dayOfYear()/7),year:s.year()}}function O(t){var e=t._i,n=t._f;return null===e||""===e?null:("string"==typeof e&&(t._i=e=_().preparse(e)),H.isMoment(e)?(t=a({},e),t._d=new Date(+e._d)):n?d(n)?k(t):w(t):T(t),new i(t))}function z(t,e){H.fn[t]=H.fn[t+"s"]=function(t){var n=this._isUTC?"UTC":"";return null!=t?(this._d["set"+n+e](t),H.updateOffset(this),this):this._d["get"+n+e]()}}function C(t){H.duration.fn[t]=function(){return this._data[t]}}function L(t,e){H.duration.fn["as"+t]=function(){return+this/e}}for(var H,P,U="2.1.0",W=Math.round,x={},A="undefined"!=typeof module&&module.exports,G=/^\/?Date\((\-?\d+)/i,Z=/(\-)?(\d*)?\.?(\d+)\:(\d+)\:(\d+)\.?(\d{3})?/,E=/(\[[^\[]*\])|(\\)?(Mo|MM?M?M?|Do|DDDo|DD?D?D?|ddd?d?|do?|w[o|w]?|W[o|W]?|YYYYY|YYYY|YY|gg(ggg?)?|GG(GGG?)?|e|E|a|A|hh?|HH?|mm?|ss?|SS?S?|X|zz?|ZZ?|.)/g,N=/(\[[^\[]*\])|(\\)?(LT|LL?L?L?|l{1,4})/g,J=/\d\d?/,I=/\d{1,3}/,V=/\d{3}/,X=/\d{1,4}/,$=/[+\-]?\d{1,6}/,R=/[0-9]*['a-z\u00A0-\u05FF\u0700-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]+|[\u0600-\u06FF\/]+(\s*?[\u0600-\u06FF]+){1,2}/i,j=/Z|[\+\-]\d\d:?\d\d/i,q=/T/i,B=/[\+\-]?\d+(\.\d{1,3})?/,K=/^\s*\d{4}-\d\d-\d\d((T| )(\d\d(:\d\d(:\d\d(\.\d\d?\d?)?)?)?)?([\+\-]\d\d:?\d\d)?)?/,Q="YYYY-MM-DDTHH:mm:ssZ",te=[["HH:mm:ss.S",/(T| )\d\d:\d\d:\d\d\.\d{1,3}/],["HH:mm:ss",/(T| )\d\d:\d\d:\d\d/],["HH:mm",/(T| )\d\d:\d\d/],["HH",/(T| )\d\d/]],ee=/([\+\-]|\d\d)/gi,ne="Date|Hours|Minutes|Seconds|Milliseconds".split("|"),se={Milliseconds:1,Seconds:1e3,Minutes:6e4,Hours:36e5,Days:864e5,Months:2592e6,Years:31536e6},ie={ms:"millisecond",s:"second",m:"minute",h:"hour",d:"day",w:"week",M:"month",y:"year"},re={},ae="DDD w W M D d".split(" "),oe="M D H h m s w W".split(" "),ue={M:function(){return this.month()+1},MMM:function(t){return this.lang().monthsShort(this,t)},MMMM:function(t){return this.lang().months(this,t)},D:function(){return this.date()},DDD:function(){return this.dayOfYear()},d:function(){return this.day()},dd:function(t){return this.lang().weekdaysMin(this,t)},ddd:function(t){return this.lang().weekdaysShort(this,t)},dddd:function(t){return this.lang().weekdays(this,t)},w:function(){return this.week()},W:function(){return this.isoWeek()},YY:function(){return u(this.year()%100,2)},YYYY:function(){return u(this.year(),4)},YYYYY:function(){return u(this.year(),5)},gg:function(){return u(this.weekYear()%100,2)},gggg:function(){return this.weekYear()},ggggg:function(){return u(this.weekYear(),5)},GG:function(){return u(this.isoWeekYear()%100,2)},GGGG:function(){return this.isoWeekYear()},GGGGG:function(){return u(this.isoWeekYear(),5)},e:function(){return this.weekday()},E:function(){return this.isoWeekday()},a:function(){return this.lang().meridiem(this.hours(),this.minutes(),!0)},A:function(){return this.lang().meridiem(this.hours(),this.minutes(),!1)},H:function(){return this.hours()},h:function(){return this.hours()%12||12},m:function(){return this.minutes()},s:function(){return this.seconds()},S:function(){return~~(this.milliseconds()/100)},SS:function(){return u(~~(this.milliseconds()/10),2)},SSS:function(){return u(this.milliseconds(),3)},Z:function(){var t=-this.zone(),e="+";return 0>t&&(t=-t,e="-"),e+u(~~(t/60),2)+":"+u(~~t%60,2)},ZZ:function(){var t=-this.zone(),e="+";return 0>t&&(t=-t,e="-"),e+u(~~(10*t/6),4)},z:function(){return this.zoneAbbr()},zz:function(){return this.zoneName()},X:function(){return this.unix()}};ae.length;)P=ae.pop(),ue[P+"o"]=n(ue[P],P);for(;oe.length;)P=oe.pop(),ue[P+P]=e(ue[P],2);for(ue.DDDD=e(ue.DDD,3),s.prototype={set:function(t){var e,n;for(n in t)e=t[n],"function"==typeof e?this[n]=e:this["_"+n]=e},_months:"January_February_March_April_May_June_July_August_September_October_November_December".split("_"),months:function(t){return this._months[t.month()]},_monthsShort:"Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec".split("_"),monthsShort:function(t){return this._monthsShort[t.month()]},monthsParse:function(t){var e,n,s;for(this._monthsParse||(this._monthsParse=[]),e=0;12>e;e++)if(this._monthsParse[e]||(n=H([2e3,e]),s="^"+this.months(n,"")+"|^"+this.monthsShort(n,""),this._monthsParse[e]=new RegExp(s.replace(".",""),"i")),this._monthsParse[e].test(t))return e},_weekdays:"Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_"),weekdays:function(t){return this._weekdays[t.day()]},_weekdaysShort:"Sun_Mon_Tue_Wed_Thu_Fri_Sat".split("_"),weekdaysShort:function(t){return this._weekdaysShort[t.day()]},_weekdaysMin:"Su_Mo_Tu_We_Th_Fr_Sa".split("_"),weekdaysMin:function(t){return this._weekdaysMin[t.day()]},weekdaysParse:function(t){var e,n,s;for(this._weekdaysParse||(this._weekdaysParse=[]),e=0;7>e;e++)if(this._weekdaysParse[e]||(n=H([2e3,1]).day(e),s="^"+this.weekdays(n,"")+"|^"+this.weekdaysShort(n,"")+"|^"+this.weekdaysMin(n,""),this._weekdaysParse[e]=new RegExp(s.replace(".",""),"i")),this._weekdaysParse[e].test(t))return e},_longDateFormat:{LT:"h:mm A",L:"MM/DD/YYYY",LL:"MMMM D YYYY",LLL:"MMMM D YYYY LT",LLLL:"dddd, MMMM D YYYY LT"},longDateFormat:function(t){var e=this._longDateFormat[t];return!e&&this._longDateFormat[t.toUpperCase()]&&(e=this._longDateFormat[t.toUpperCase()].replace(/MMMM|MM|DD|dddd/g,function(t){return t.slice(1)}),this._longDateFormat[t]=e),e},isPM:function(t){return"p"===(t+"").toLowerCase()[0]},_meridiemParse:/[ap]\.?m?\.?/i,meridiem:function(t,e,n){return t>11?n?"pm":"PM":n?"am":"AM"},_calendar:{sameDay:"[Today at] LT",nextDay:"[Tomorrow at] LT",nextWeek:"dddd [at] LT",lastDay:"[Yesterday at] LT",lastWeek:"[Last] dddd [at] LT",sameElse:"L"},calendar:function(t,e){var n=this._calendar[t];return"function"==typeof n?n.apply(e):n},_relativeTime:{future:"in %s",past:"%s ago",s:"a few seconds",m:"a minute",mm:"%d minutes",h:"an hour",hh:"%d hours",d:"a day",dd:"%d days",M:"a month",MM:"%d months",y:"a year",yy:"%d years"},relativeTime:function(t,e,n,s){var i=this._relativeTime[n];return"function"==typeof i?i(t,e,n,s):i.replace(/%d/i,t)},pastFuture:function(t,e){var n=this._relativeTime[t>0?"future":"past"];return"function"==typeof n?n(e):n.replace(/%s/i,e)},ordinal:function(t){return this._ordinal.replace("%d",t)},_ordinal:"%d",preparse:function(t){return t},postformat:function(t){return t},week:function(t){return F(t,this._week.dow,this._week.doy).week},_week:{dow:0,doy:6}},H=function(t,e,n){return O({_i:t,_f:e,_l:n,_isUTC:!1})},H.utc=function(t,e,n){return O({_useUTC:!0,_isUTC:!0,_l:n,_i:t,_f:e})},H.unix=function(t){return H(1e3*t)},H.duration=function(t,e){var n,s,i=H.isDuration(t),a="number"==typeof t,o=i?t._input:a?{}:t,u=Z.exec(t);return a?e?o[e]=t:o.milliseconds=t:u&&(n="-"===u[1]?-1:1,o={y:0,d:~~u[2]*n,h:~~u[3]*n,m:~~u[4]*n,s:~~u[5]*n,ms:~~u[6]*n}),s=new r(o),i&&t.hasOwnProperty("_lang")&&(s._lang=t._lang),s},H.version=U,H.defaultFormat=Q,H.updateOffset=function(){},H.lang=function(t,e){return t?(e?l(t,e):x[t]||_(t),H.duration.fn._lang=H.fn._lang=_(t),void 0):H.fn._lang._abbr},H.langData=function(t){return t&&t._lang&&t._lang._abbr&&(t=t._lang._abbr),_(t)},H.isMoment=function(t){return t instanceof i},H.isDuration=function(t){return t instanceof r},H.fn=i.prototype={clone:function(){return H(this)},valueOf:function(){return+this._d+6e4*(this._offset||0)},unix:function(){return Math.floor(+this/1e3)},toString:function(){return this.format("ddd MMM DD YYYY HH:mm:ss [GMT]ZZ")},toDate:function(){return this._offset?new Date(+this):this._d},toISOString:function(){return M(H(this).utc(),"YYYY-MM-DD[T]HH:mm:ss.SSS[Z]")},toArray:function(){var t=this;return[t.year(),t.month(),t.date(),t.hours(),t.minutes(),t.seconds(),t.milliseconds()]},isValid:function(){return null==this._isValid&&(this._isValid=this._a?!c(this._a,(this._isUTC?H.utc(this._a):H(this._a)).toArray()):!isNaN(this._d.getTime())),!!this._isValid},utc:function(){return this.zone(0)},local:function(){return this.zone(0),this._isUTC=!1,this},format:function(t){var e=M(this,t||H.defaultFormat);return this.lang().postformat(e)},add:function(t,e){var n;return n="string"==typeof t?H.duration(+e,t):H.duration(t,e),h(this,n,1),this},subtract:function(t,e){var n;return n="string"==typeof t?H.duration(+e,t):H.duration(t,e),h(this,n,-1),this},diff:function(t,e,n){var s,i,r=this._isUTC?H(t).zone(this._offset||0):H(t).local(),a=6e4*(this.zone()-r.zone());return e=f(e),"year"===e||"month"===e?(s=432e5*(this.daysInMonth()+r.daysInMonth()),i=12*(this.year()-r.year())+(this.month()-r.month()),i+=(this-H(this).startOf("month")-(r-H(r).startOf("month")))/s,i-=6e4*(this.zone()-H(this).startOf("month").zone()-(r.zone()-H(r).startOf("month").zone()))/s,"year"===e&&(i/=12)):(s=this-r,i="second"===e?s/1e3:"minute"===e?s/6e4:"hour"===e?s/36e5:"day"===e?(s-a)/864e5:"week"===e?(s-a)/6048e5:s),n?i:o(i)},from:function(t,e){return H.duration(this.diff(t)).lang(this.lang()._abbr).humanize(!e)},fromNow:function(t){return this.from(H(),t)},calendar:function(){var t=this.diff(H().startOf("day"),"days",!0),e=-6>t?"sameElse":-1>t?"lastWeek":0>t?"lastDay":1>t?"sameDay":2>t?"nextDay":7>t?"nextWeek":"sameElse";return this.format(this.lang().calendar(e,this))},isLeapYear:function(){var t=this.year();return 0===t%4&&0!==t%100||0===t%400},isDST:function(){return this.zone()+H(t).startOf(e)},isBefore:function(t,e){return e="undefined"!=typeof e?e:"millisecond",+this.clone().startOf(e)<+H(t).startOf(e)},isSame:function(t,e){return e="undefined"!=typeof e?e:"millisecond",+this.clone().startOf(e)===+H(t).startOf(e)},min:function(t){return t=H.apply(null,arguments),this>t?this:t},max:function(t){return t=H.apply(null,arguments),t>this?this:t},zone:function(t){var e=this._offset||0;return null==t?this._isUTC?e:this._d.getTimezoneOffset():("string"==typeof t&&(t=p(t)),Math.abs(t)<16&&(t=60*t),this._offset=t,this._isUTC=!0,e!==t&&h(this,H.duration(e-t,"m"),1,!0),this)},zoneAbbr:function(){return this._isUTC?"UTC":""},zoneName:function(){return this._isUTC?"Coordinated Universal Time":""},daysInMonth:function(){return H.utc([this.year(),this.month()+1,0]).date()},dayOfYear:function(t){var e=W((H(this).startOf("day")-H(this).startOf("year"))/864e5)+1;return null==t?e:this.add("d",t-e)},weekYear:function(t){var e=F(this,this.lang()._week.dow,this.lang()._week.doy).year;return null==t?e:this.add("y",t-e)},isoWeekYear:function(t){var e=F(this,1,4).year;return null==t?e:this.add("y",t-e)},week:function(t){var e=this.lang().week(this);return null==t?e:this.add("d",7*(t-e))},isoWeek:function(t){var e=F(this,1,4).week;return null==t?e:this.add("d",7*(t-e))},weekday:function(t){var e=(this._d.getDay()+7-this.lang()._week.dow)%7;return null==t?e:this.add("d",t-e)},isoWeekday:function(t){return null==t?this.day()||7:this.day(this.day()%7?t:t-7)},lang:function(e){return e===t?this._lang:(this._lang=_(e),this)}},P=0;P 7 | 8 | 9 | 10 | 11 | 12 | CAPCreator Unit Tests 13 | 14 | 15 | 16 | 17 |
18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 |
51 | 56 |
57 |
58 |
59 | 60 | 64 | 65 | 66 | -------------------------------------------------------------------------------- /js/theme/default/google.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2013, Carnegie Mellon University 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without modification, 6 | * are permitted provided that the following conditions are met: 7 | * 8 | * Redistributions of source code must retain the above copyright notice, this 9 | * list of conditions and the following disclaimer. 10 | * 11 | * Redistributions in binary form must reproduce the above copyright notice, this 12 | * list of conditions and the following disclaimer in the documentation and/or 13 | * other materials provided with the distribution. 14 | * 15 | * Neither the name of the Carnegie Mellon University nor the names of its 16 | * contributors may be used to endorse or promote products derived from 17 | * this software without specific prior written permission. 18 | * 19 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 20 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 21 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 22 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 23 | * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 24 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 25 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 26 | * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 28 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | */ 30 | 31 | .olLayerGoogleCopyright { 32 | right: 3px; 33 | bottom: 2px; 34 | left: auto; 35 | } 36 | .olLayerGooglePoweredBy { 37 | left: 2px; 38 | bottom: 2px; 39 | } 40 | -------------------------------------------------------------------------------- /js/theme/default/google.tidy.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2013, Carnegie Mellon University 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without modification, 6 | * are permitted provided that the following conditions are met: 7 | * 8 | * Redistributions of source code must retain the above copyright notice, this 9 | * list of conditions and the following disclaimer. 10 | * 11 | * Redistributions in binary form must reproduce the above copyright notice, this 12 | * list of conditions and the following disclaimer in the documentation and/or 13 | * other materials provided with the distribution. 14 | * 15 | * Neither the name of the Carnegie Mellon University nor the names of its 16 | * contributors may be used to endorse or promote products derived from 17 | * this software without specific prior written permission. 18 | * 19 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 20 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 21 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 22 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 23 | * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 24 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 25 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 26 | * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 28 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | */ 30 | 31 | .olLayerGoogleCopyright{right:3px;bottom:2px;left:auto;}.olLayerGooglePoweredBy{left:2px;bottom:2px;} 32 | -------------------------------------------------------------------------------- /js/theme/default/ie6-style.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2013, Carnegie Mellon University 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without modification, 6 | * are permitted provided that the following conditions are met: 7 | * 8 | * Redistributions of source code must retain the above copyright notice, this 9 | * list of conditions and the following disclaimer. 10 | * 11 | * Redistributions in binary form must reproduce the above copyright notice, this 12 | * list of conditions and the following disclaimer in the documentation and/or 13 | * other materials provided with the distribution. 14 | * 15 | * Neither the name of the Carnegie Mellon University nor the names of its 16 | * contributors may be used to endorse or promote products derived from 17 | * this software without specific prior written permission. 18 | * 19 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 20 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 21 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 22 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 23 | * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 24 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 25 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 26 | * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 28 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | */ 30 | 31 | .olControlZoomPanel div { 32 | background-image: url(img/zoom-panel-NOALPHA.png); 33 | } 34 | .olControlPanPanel div { 35 | background-image: url(img/pan-panel-NOALPHA.png); 36 | } 37 | .olControlEditingToolbar { 38 | width: 200px; 39 | } 40 | 41 | -------------------------------------------------------------------------------- /js/theme/default/ie6-style.tidy.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2013, Carnegie Mellon University 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without modification, 6 | * are permitted provided that the following conditions are met: 7 | * 8 | * Redistributions of source code must retain the above copyright notice, this 9 | * list of conditions and the following disclaimer. 10 | * 11 | * Redistributions in binary form must reproduce the above copyright notice, this 12 | * list of conditions and the following disclaimer in the documentation and/or 13 | * other materials provided with the distribution. 14 | * 15 | * Neither the name of the Carnegie Mellon University nor the names of its 16 | * contributors may be used to endorse or promote products derived from 17 | * this software without specific prior written permission. 18 | * 19 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 20 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 21 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 22 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 23 | * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 24 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 25 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 26 | * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 28 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | */ 30 | 31 | .olControlZoomPanel div{background-image:url(img/zoom-panel-NOALPHA.png);}.olControlPanPanel div{background-image:url(img/pan-panel-NOALPHA.png);}.olControlEditingToolbar{width:200px;} 32 | -------------------------------------------------------------------------------- /js/theme/default/img/add_point_off.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CAPTools/CAPCreator/a4191b4bedaf7c3f7525593a4cce167ef60e29fe/js/theme/default/img/add_point_off.png -------------------------------------------------------------------------------- /js/theme/default/img/add_point_on.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CAPTools/CAPCreator/a4191b4bedaf7c3f7525593a4cce167ef60e29fe/js/theme/default/img/add_point_on.png -------------------------------------------------------------------------------- /js/theme/default/img/blank.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CAPTools/CAPCreator/a4191b4bedaf7c3f7525593a4cce167ef60e29fe/js/theme/default/img/blank.gif -------------------------------------------------------------------------------- /js/theme/default/img/close.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CAPTools/CAPCreator/a4191b4bedaf7c3f7525593a4cce167ef60e29fe/js/theme/default/img/close.gif -------------------------------------------------------------------------------- /js/theme/default/img/drag-rectangle-off.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CAPTools/CAPCreator/a4191b4bedaf7c3f7525593a4cce167ef60e29fe/js/theme/default/img/drag-rectangle-off.png -------------------------------------------------------------------------------- /js/theme/default/img/drag-rectangle-on.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CAPTools/CAPCreator/a4191b4bedaf7c3f7525593a4cce167ef60e29fe/js/theme/default/img/drag-rectangle-on.png -------------------------------------------------------------------------------- /js/theme/default/img/draw_line_off.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CAPTools/CAPCreator/a4191b4bedaf7c3f7525593a4cce167ef60e29fe/js/theme/default/img/draw_line_off.png -------------------------------------------------------------------------------- /js/theme/default/img/draw_line_on.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CAPTools/CAPCreator/a4191b4bedaf7c3f7525593a4cce167ef60e29fe/js/theme/default/img/draw_line_on.png -------------------------------------------------------------------------------- /js/theme/default/img/draw_point_off.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CAPTools/CAPCreator/a4191b4bedaf7c3f7525593a4cce167ef60e29fe/js/theme/default/img/draw_point_off.png -------------------------------------------------------------------------------- /js/theme/default/img/draw_point_on.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CAPTools/CAPCreator/a4191b4bedaf7c3f7525593a4cce167ef60e29fe/js/theme/default/img/draw_point_on.png -------------------------------------------------------------------------------- /js/theme/default/img/draw_polygon_off.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CAPTools/CAPCreator/a4191b4bedaf7c3f7525593a4cce167ef60e29fe/js/theme/default/img/draw_polygon_off.png -------------------------------------------------------------------------------- /js/theme/default/img/draw_polygon_on.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CAPTools/CAPCreator/a4191b4bedaf7c3f7525593a4cce167ef60e29fe/js/theme/default/img/draw_polygon_on.png -------------------------------------------------------------------------------- /js/theme/default/img/editing_tool_bar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CAPTools/CAPCreator/a4191b4bedaf7c3f7525593a4cce167ef60e29fe/js/theme/default/img/editing_tool_bar.png -------------------------------------------------------------------------------- /js/theme/default/img/move_feature_off.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CAPTools/CAPCreator/a4191b4bedaf7c3f7525593a4cce167ef60e29fe/js/theme/default/img/move_feature_off.png -------------------------------------------------------------------------------- /js/theme/default/img/move_feature_on.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CAPTools/CAPCreator/a4191b4bedaf7c3f7525593a4cce167ef60e29fe/js/theme/default/img/move_feature_on.png -------------------------------------------------------------------------------- /js/theme/default/img/navigation_history.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CAPTools/CAPCreator/a4191b4bedaf7c3f7525593a4cce167ef60e29fe/js/theme/default/img/navigation_history.png -------------------------------------------------------------------------------- /js/theme/default/img/overview_replacement.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CAPTools/CAPCreator/a4191b4bedaf7c3f7525593a4cce167ef60e29fe/js/theme/default/img/overview_replacement.gif -------------------------------------------------------------------------------- /js/theme/default/img/pan-panel-NOALPHA.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CAPTools/CAPCreator/a4191b4bedaf7c3f7525593a4cce167ef60e29fe/js/theme/default/img/pan-panel-NOALPHA.png -------------------------------------------------------------------------------- /js/theme/default/img/pan-panel.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CAPTools/CAPCreator/a4191b4bedaf7c3f7525593a4cce167ef60e29fe/js/theme/default/img/pan-panel.png -------------------------------------------------------------------------------- /js/theme/default/img/pan_off.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CAPTools/CAPCreator/a4191b4bedaf7c3f7525593a4cce167ef60e29fe/js/theme/default/img/pan_off.png -------------------------------------------------------------------------------- /js/theme/default/img/pan_on.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CAPTools/CAPCreator/a4191b4bedaf7c3f7525593a4cce167ef60e29fe/js/theme/default/img/pan_on.png -------------------------------------------------------------------------------- /js/theme/default/img/panning-hand-off.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CAPTools/CAPCreator/a4191b4bedaf7c3f7525593a4cce167ef60e29fe/js/theme/default/img/panning-hand-off.png -------------------------------------------------------------------------------- /js/theme/default/img/panning-hand-on.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CAPTools/CAPCreator/a4191b4bedaf7c3f7525593a4cce167ef60e29fe/js/theme/default/img/panning-hand-on.png -------------------------------------------------------------------------------- /js/theme/default/img/remove_point_off.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CAPTools/CAPCreator/a4191b4bedaf7c3f7525593a4cce167ef60e29fe/js/theme/default/img/remove_point_off.png -------------------------------------------------------------------------------- /js/theme/default/img/remove_point_on.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CAPTools/CAPCreator/a4191b4bedaf7c3f7525593a4cce167ef60e29fe/js/theme/default/img/remove_point_on.png -------------------------------------------------------------------------------- /js/theme/default/img/ruler.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CAPTools/CAPCreator/a4191b4bedaf7c3f7525593a4cce167ef60e29fe/js/theme/default/img/ruler.png -------------------------------------------------------------------------------- /js/theme/default/img/save_features_off.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CAPTools/CAPCreator/a4191b4bedaf7c3f7525593a4cce167ef60e29fe/js/theme/default/img/save_features_off.png -------------------------------------------------------------------------------- /js/theme/default/img/save_features_on.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CAPTools/CAPCreator/a4191b4bedaf7c3f7525593a4cce167ef60e29fe/js/theme/default/img/save_features_on.png -------------------------------------------------------------------------------- /js/theme/default/img/view_next_off.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CAPTools/CAPCreator/a4191b4bedaf7c3f7525593a4cce167ef60e29fe/js/theme/default/img/view_next_off.png -------------------------------------------------------------------------------- /js/theme/default/img/view_next_on.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CAPTools/CAPCreator/a4191b4bedaf7c3f7525593a4cce167ef60e29fe/js/theme/default/img/view_next_on.png -------------------------------------------------------------------------------- /js/theme/default/img/view_previous_off.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CAPTools/CAPCreator/a4191b4bedaf7c3f7525593a4cce167ef60e29fe/js/theme/default/img/view_previous_off.png -------------------------------------------------------------------------------- /js/theme/default/img/view_previous_on.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CAPTools/CAPCreator/a4191b4bedaf7c3f7525593a4cce167ef60e29fe/js/theme/default/img/view_previous_on.png -------------------------------------------------------------------------------- /js/theme/default/img/zoom-panel-NOALPHA.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CAPTools/CAPCreator/a4191b4bedaf7c3f7525593a4cce167ef60e29fe/js/theme/default/img/zoom-panel-NOALPHA.png -------------------------------------------------------------------------------- /js/theme/default/img/zoom-panel.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CAPTools/CAPCreator/a4191b4bedaf7c3f7525593a4cce167ef60e29fe/js/theme/default/img/zoom-panel.png -------------------------------------------------------------------------------- /js/theme/default/style.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2013, Carnegie Mellon University 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without modification, 6 | * are permitted provided that the following conditions are met: 7 | * 8 | * Redistributions of source code must retain the above copyright notice, this 9 | * list of conditions and the following disclaimer. 10 | * 11 | * Redistributions in binary form must reproduce the above copyright notice, this 12 | * list of conditions and the following disclaimer in the documentation and/or 13 | * other materials provided with the distribution. 14 | * 15 | * Neither the name of the Carnegie Mellon University nor the names of its 16 | * contributors may be used to endorse or promote products derived from 17 | * this software without specific prior written permission. 18 | * 19 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 20 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 21 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 22 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 23 | * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 24 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 25 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 26 | * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 28 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | */ 30 | 31 | div.olMap { 32 | z-index: 0; 33 | padding: 0 !important; 34 | margin: 0 !important; 35 | cursor: default; 36 | } 37 | 38 | div.olMapViewport { 39 | text-align: left; 40 | -ms-touch-action: none; 41 | } 42 | 43 | div.olLayerDiv { 44 | -moz-user-select: none; 45 | -khtml-user-select: none; 46 | } 47 | 48 | .olLayerGoogleCopyright { 49 | left: 2px; 50 | bottom: 2px; 51 | } 52 | .olLayerGoogleV3.olLayerGoogleCopyright { 53 | right: auto !important; 54 | } 55 | .olLayerGooglePoweredBy { 56 | left: 2px; 57 | bottom: 15px; 58 | } 59 | .olLayerGoogleV3.olLayerGooglePoweredBy { 60 | bottom: 15px !important; 61 | } 62 | /* GMaps should not set styles on its container */ 63 | .olForeignContainer { 64 | opacity: 1 !important; 65 | } 66 | .olControlAttribution { 67 | font-size: smaller; 68 | right: 3px; 69 | bottom: 4.5em; 70 | position: absolute; 71 | display: block; 72 | } 73 | .olControlScale { 74 | right: 3px; 75 | bottom: 3em; 76 | display: block; 77 | position: absolute; 78 | font-size: smaller; 79 | } 80 | .olControlScaleLine { 81 | display: block; 82 | position: absolute; 83 | left: 10px; 84 | bottom: 15px; 85 | font-size: xx-small; 86 | } 87 | .olControlScaleLineBottom { 88 | border: solid 2px black; 89 | border-bottom: none; 90 | margin-top:-2px; 91 | text-align: center; 92 | } 93 | .olControlScaleLineTop { 94 | border: solid 2px black; 95 | border-top: none; 96 | text-align: center; 97 | } 98 | 99 | .olControlPermalink { 100 | right: 3px; 101 | bottom: 1.5em; 102 | display: block; 103 | position: absolute; 104 | font-size: smaller; 105 | } 106 | 107 | div.olControlMousePosition { 108 | bottom: 0; 109 | right: 3px; 110 | display: block; 111 | position: absolute; 112 | font-family: Arial; 113 | font-size: smaller; 114 | } 115 | 116 | .olControlOverviewMapContainer { 117 | position: absolute; 118 | bottom: 0; 119 | right: 0; 120 | } 121 | 122 | .olControlOverviewMapElement { 123 | padding: 10px 18px 10px 10px; 124 | background-color: #00008B; 125 | -moz-border-radius: 1em 0 0 0; 126 | } 127 | 128 | .olControlOverviewMapMinimizeButton, 129 | .olControlOverviewMapMaximizeButton { 130 | height: 18px; 131 | width: 18px; 132 | right: 0; 133 | bottom: 80px; 134 | cursor: pointer; 135 | } 136 | 137 | .olControlOverviewMapExtentRectangle { 138 | overflow: hidden; 139 | background-image: url("img/blank.gif"); 140 | cursor: move; 141 | border: 2px dotted red; 142 | } 143 | .olControlOverviewMapRectReplacement { 144 | overflow: hidden; 145 | cursor: move; 146 | background-image: url("img/overview_replacement.gif"); 147 | background-repeat: no-repeat; 148 | background-position: center; 149 | } 150 | 151 | .olLayerGeoRSSDescription { 152 | float:left; 153 | width:100%; 154 | overflow:auto; 155 | font-size:1.0em; 156 | } 157 | .olLayerGeoRSSClose { 158 | float:right; 159 | color:gray; 160 | font-size:1.2em; 161 | margin-right:6px; 162 | font-family:sans-serif; 163 | } 164 | .olLayerGeoRSSTitle { 165 | float:left;font-size:1.2em; 166 | } 167 | 168 | .olPopupContent { 169 | padding:5px; 170 | overflow: auto; 171 | } 172 | 173 | .olControlNavigationHistory { 174 | background-image: url("img/navigation_history.png"); 175 | background-repeat: no-repeat; 176 | width: 24px; 177 | height: 24px; 178 | 179 | } 180 | .olControlNavigationHistoryPreviousItemActive { 181 | background-position: 0 0; 182 | } 183 | .olControlNavigationHistoryPreviousItemInactive { 184 | background-position: 0 -24px; 185 | } 186 | .olControlNavigationHistoryNextItemActive { 187 | background-position: -24px 0; 188 | } 189 | .olControlNavigationHistoryNextItemInactive { 190 | background-position: -24px -24px; 191 | } 192 | 193 | div.olControlSaveFeaturesItemActive { 194 | background-image: url(img/save_features_on.png); 195 | background-repeat: no-repeat; 196 | background-position: 0 1px; 197 | } 198 | div.olControlSaveFeaturesItemInactive { 199 | background-image: url(img/save_features_off.png); 200 | background-repeat: no-repeat; 201 | background-position: 0 1px; 202 | } 203 | 204 | .olHandlerBoxZoomBox { 205 | border: 2px solid red; 206 | position: absolute; 207 | background-color: white; 208 | opacity: 0.50; 209 | font-size: 1px; 210 | filter: alpha(opacity=50); 211 | } 212 | .olHandlerBoxSelectFeature { 213 | border: 2px solid blue; 214 | position: absolute; 215 | background-color: white; 216 | opacity: 0.50; 217 | font-size: 1px; 218 | filter: alpha(opacity=50); 219 | } 220 | 221 | .olControlPanPanel { 222 | top: 10px; 223 | left: 5px; 224 | } 225 | 226 | .olControlPanPanel div { 227 | background-image: url(img/pan-panel.png); 228 | height: 18px; 229 | width: 18px; 230 | cursor: pointer; 231 | position: absolute; 232 | } 233 | 234 | .olControlPanPanel .olControlPanNorthItemInactive { 235 | top: 0; 236 | left: 9px; 237 | background-position: 0 0; 238 | } 239 | .olControlPanPanel .olControlPanSouthItemInactive { 240 | top: 36px; 241 | left: 9px; 242 | background-position: 18px 0; 243 | } 244 | .olControlPanPanel .olControlPanWestItemInactive { 245 | position: absolute; 246 | top: 18px; 247 | left: 0; 248 | background-position: 0 18px; 249 | } 250 | .olControlPanPanel .olControlPanEastItemInactive { 251 | top: 18px; 252 | left: 18px; 253 | background-position: 18px 18px; 254 | } 255 | 256 | .olControlZoomPanel { 257 | top: 71px; 258 | left: 14px; 259 | } 260 | 261 | .olControlZoomPanel div { 262 | background-image: url(img/zoom-panel.png); 263 | position: absolute; 264 | height: 18px; 265 | width: 18px; 266 | cursor: pointer; 267 | } 268 | 269 | .olControlZoomPanel .olControlZoomInItemInactive { 270 | top: 0; 271 | left: 0; 272 | background-position: 0 0; 273 | } 274 | 275 | .olControlZoomPanel .olControlZoomToMaxExtentItemInactive { 276 | top: 18px; 277 | left: 0; 278 | background-position: 0 -18px; 279 | } 280 | 281 | .olControlZoomPanel .olControlZoomOutItemInactive { 282 | top: 36px; 283 | left: 0; 284 | background-position: 0 18px; 285 | } 286 | 287 | /* 288 | * When a potential text is bigger than the image it move the image 289 | * with some headers (closes #3154) 290 | */ 291 | .olControlPanZoomBar div { 292 | font-size: 1px; 293 | } 294 | 295 | .olPopupCloseBox { 296 | background: url("img/close.gif") no-repeat; 297 | cursor: pointer; 298 | } 299 | 300 | .olFramedCloudPopupContent { 301 | padding: 5px; 302 | overflow: auto; 303 | } 304 | 305 | .olControlNoSelect { 306 | -moz-user-select: none; 307 | -khtml-user-select: none; 308 | } 309 | 310 | .olImageLoadError { 311 | background-color: pink; 312 | opacity: 0.5; 313 | filter: alpha(opacity=50); /* IE */ 314 | } 315 | 316 | /** 317 | * Cursor styles 318 | */ 319 | 320 | .olCursorWait { 321 | cursor: wait; 322 | } 323 | .olDragDown { 324 | cursor: move; 325 | } 326 | .olDrawBox { 327 | cursor: crosshair; 328 | } 329 | .olControlDragFeatureOver { 330 | cursor: move; 331 | } 332 | .olControlDragFeatureActive.olControlDragFeatureOver.olDragDown { 333 | cursor: -moz-grabbing; 334 | } 335 | 336 | /** 337 | * Layer switcher 338 | */ 339 | .olControlLayerSwitcher { 340 | position: absolute; 341 | top: 25px; 342 | right: 0; 343 | width: 20em; 344 | font-family: sans-serif; 345 | font-weight: bold; 346 | margin-top: 3px; 347 | margin-left: 3px; 348 | margin-bottom: 3px; 349 | font-size: smaller; 350 | color: white; 351 | background-color: transparent; 352 | } 353 | 354 | .olControlLayerSwitcher .layersDiv { 355 | padding-top: 5px; 356 | padding-left: 10px; 357 | padding-bottom: 5px; 358 | padding-right: 10px; 359 | background-color: darkblue; 360 | } 361 | 362 | .olControlLayerSwitcher .layersDiv .baseLbl, 363 | .olControlLayerSwitcher .layersDiv .dataLbl { 364 | margin-top: 3px; 365 | margin-left: 3px; 366 | margin-bottom: 3px; 367 | } 368 | 369 | .olControlLayerSwitcher .layersDiv .baseLayersDiv, 370 | .olControlLayerSwitcher .layersDiv .dataLayersDiv { 371 | padding-left: 10px; 372 | } 373 | 374 | .olControlLayerSwitcher .maximizeDiv, 375 | .olControlLayerSwitcher .minimizeDiv { 376 | width: 18px; 377 | height: 18px; 378 | top: 5px; 379 | right: 0; 380 | cursor: pointer; 381 | } 382 | 383 | .olBingAttribution { 384 | color: #DDD; 385 | } 386 | .olBingAttribution.road { 387 | color: #333; 388 | } 389 | 390 | .olGoogleAttribution.hybrid, .olGoogleAttribution.satellite { 391 | color: #EEE; 392 | } 393 | .olGoogleAttribution { 394 | color: #333; 395 | } 396 | span.olGoogleAttribution a { 397 | color: #77C; 398 | } 399 | span.olGoogleAttribution.hybrid a, span.olGoogleAttribution.satellite a { 400 | color: #EEE; 401 | } 402 | 403 | /** 404 | * Editing and navigation icons. 405 | * (using the editing_tool_bar.png sprint image) 406 | */ 407 | .olControlNavToolbar , 408 | .olControlEditingToolbar { 409 | margin: 5px 5px 0 0; 410 | } 411 | .olControlNavToolbar div, 412 | .olControlEditingToolbar div { 413 | background-image: url("img/editing_tool_bar.png"); 414 | background-repeat: no-repeat; 415 | margin: 0 0 5px 5px; 416 | width: 24px; 417 | height: 22px; 418 | cursor: pointer 419 | } 420 | /* positions */ 421 | .olControlEditingToolbar { 422 | right: 0; 423 | top: 0; 424 | } 425 | .olControlNavToolbar { 426 | top: 295px; 427 | left: 9px; 428 | } 429 | /* layouts */ 430 | .olControlEditingToolbar div { 431 | float: right; 432 | } 433 | /* individual controls */ 434 | .olControlNavToolbar .olControlNavigationItemInactive, 435 | .olControlEditingToolbar .olControlNavigationItemInactive { 436 | background-position: -103px -1px; 437 | } 438 | .olControlNavToolbar .olControlNavigationItemActive , 439 | .olControlEditingToolbar .olControlNavigationItemActive { 440 | background-position: -103px -24px; 441 | } 442 | .olControlNavToolbar .olControlZoomBoxItemInactive { 443 | background-position: -128px -1px; 444 | } 445 | .olControlNavToolbar .olControlZoomBoxItemActive { 446 | background-position: -128px -24px; 447 | } 448 | .olControlEditingToolbar .olControlDrawFeaturePointItemInactive { 449 | background-position: -77px -1px; 450 | } 451 | .olControlEditingToolbar .olControlDrawFeaturePointItemActive { 452 | background-position: -77px -24px; 453 | } 454 | .olControlEditingToolbar .olControlDrawFeaturePathItemInactive { 455 | background-position: -51px -1px; 456 | } 457 | .olControlEditingToolbar .olControlDrawFeaturePathItemActive { 458 | background-position: -51px -24px; 459 | } 460 | .olControlEditingToolbar .olControlDrawFeaturePolygonItemInactive{ 461 | background-position: -26px -1px; 462 | } 463 | .olControlEditingToolbar .olControlDrawFeaturePolygonItemActive { 464 | background-position: -26px -24px; 465 | } 466 | 467 | div.olControlZoom { 468 | position: absolute; 469 | top: 8px; 470 | left: 8px; 471 | background: rgba(255,255,255,0.4); 472 | border-radius: 4px; 473 | padding: 2px; 474 | } 475 | div.olControlZoom a { 476 | display: block; 477 | margin: 1px; 478 | padding: 0; 479 | color: white; 480 | font-size: 18px; 481 | font-family: 'Lucida Grande', Verdana, Geneva, Lucida, Arial, Helvetica, sans-serif; 482 | font-weight: bold; 483 | text-decoration: none; 484 | text-align: center; 485 | height: 22px; 486 | width:22px; 487 | line-height: 19px; 488 | background: #130085; /* fallback for IE - IE6 requires background shorthand*/ 489 | background: rgba(0, 60, 136, 0.5); 490 | filter: alpha(opacity=80); 491 | } 492 | div.olControlZoom a:hover { 493 | background: #130085; /* fallback for IE */ 494 | background: rgba(0, 60, 136, 0.7); 495 | filter: alpha(opacity=100); 496 | } 497 | @media only screen and (max-width: 600px) { 498 | div.olControlZoom a:hover { 499 | background: rgba(0, 60, 136, 0.5); 500 | } 501 | } 502 | a.olControlZoomIn { 503 | border-radius: 4px 4px 0 0; 504 | } 505 | a.olControlZoomOut { 506 | border-radius: 0 0 4px 4px; 507 | } 508 | 509 | 510 | /** 511 | * Animations 512 | */ 513 | 514 | .olLayerGrid .olTileImage { 515 | -webkit-transition: opacity 0.2s linear; 516 | -moz-transition: opacity 0.2s linear; 517 | -o-transition: opacity 0.2s linear; 518 | transition: opacity 0.2s linear; 519 | } 520 | 521 | /* Turn on GPU support where available */ 522 | .olTileImage { 523 | -webkit-transform: translateZ(0); 524 | -moz-transform: translateZ(0); 525 | -o-transform: translateZ(0); 526 | -ms-transform: translateZ(0); 527 | transform: translateZ(0); 528 | -webkit-backface-visibility: hidden; 529 | -moz-backface-visibility: hidden; 530 | -ms-backface-visibility: hidden; 531 | backface-visibility: hidden; 532 | -webkit-perspective: 1000; 533 | -moz-perspective: 1000; 534 | -ms-perspective: 1000; 535 | perspective: 1000; 536 | } 537 | 538 | /* when replacing tiles, do not show tile and backbuffer at the same time */ 539 | .olTileReplacing { 540 | display: none; 541 | } 542 | 543 | /* override any max-width image settings (e.g. bootstrap.css) */ 544 | img.olTileImage { 545 | max-width: none; 546 | } 547 | -------------------------------------------------------------------------------- /js/theme/default/style.mobile.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2013, Carnegie Mellon University 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without modification, 6 | * are permitted provided that the following conditions are met: 7 | * 8 | * Redistributions of source code must retain the above copyright notice, this 9 | * list of conditions and the following disclaimer. 10 | * 11 | * Redistributions in binary form must reproduce the above copyright notice, this 12 | * list of conditions and the following disclaimer in the documentation and/or 13 | * other materials provided with the distribution. 14 | * 15 | * Neither the name of the Carnegie Mellon University nor the names of its 16 | * contributors may be used to endorse or promote products derived from 17 | * this software without specific prior written permission. 18 | * 19 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 20 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 21 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 22 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 23 | * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 24 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 25 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 26 | * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 28 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | */ 30 | 31 | div.olControlZoom { 32 | position: absolute; 33 | top: 8px; 34 | left: 8px; 35 | background: rgba(255,255,255,0.4); 36 | border-radius: 4px; 37 | padding: 2px; 38 | } 39 | * { 40 | -webkit-tap-highlight-color: rgba(0, 0, 0, 0); 41 | } 42 | div.olControlZoom a { 43 | display: block; 44 | margin: 1px; 45 | padding: 0; 46 | color: white; 47 | font-size: 28px; 48 | font-family: sans-serif; 49 | font-weight: bold; 50 | text-decoration: none; 51 | text-align: center; 52 | height: 32px; 53 | width: 32px; 54 | line-height: 28px; 55 | text-shadow: 0 0 3px rgba(0,0,0,0.8); 56 | background: #130085; /* fallback for IE - IE6 requires background shorthand*/ 57 | background: rgba(0, 60, 136, 0.5); 58 | filter: alpha(opacity=80); 59 | } 60 | a.olControlZoomIn { 61 | border-radius: 4px 4px 0 0; 62 | } 63 | a.olControlZoomOut { 64 | border-radius: 0 0 4px 4px; 65 | } 66 | div.olControlZoom a:hover { 67 | background: #130085; /* fallback for IE */ 68 | background: rgba(0, 60, 136, 0.7); 69 | filter: alpha(opacity=100); 70 | } 71 | @media only screen and (max-width: 600px) { 72 | div.olControlZoom a:hover { 73 | background: rgba(0, 60, 136, 0.5); 74 | } 75 | } 76 | div.olMapViewport { 77 | -ms-touch-action: none; 78 | } 79 | .olLayerGrid .olTileImage { 80 | -webkit-transition: opacity 0.2s linear; 81 | -moz-transition: opacity 0.2s linear; 82 | -o-transition: opacity 0.2s linear; 83 | transition: opacity 0.2s linear; 84 | } 85 | /* Turn on GPU support where available */ 86 | .olTileImage { 87 | -webkit-transform: translateZ(0); 88 | -moz-transform: translateZ(0); 89 | -o-transform: translateZ(0); 90 | -ms-transform: translateZ(0); 91 | transform: translateZ(0); 92 | -webkit-backface-visibility: hidden; 93 | -moz-backface-visibility: hidden; 94 | -ms-backface-visibility: hidden; 95 | backface-visibility: hidden; 96 | -webkit-perspective: 1000; 97 | -moz-perspective: 1000; 98 | -ms-perspective: 1000; 99 | perspective: 1000; 100 | } 101 | -------------------------------------------------------------------------------- /js/theme/default/style.mobile.tidy.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2013, Carnegie Mellon University 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without modification, 6 | * are permitted provided that the following conditions are met: 7 | * 8 | * Redistributions of source code must retain the above copyright notice, this 9 | * list of conditions and the following disclaimer. 10 | * 11 | * Redistributions in binary form must reproduce the above copyright notice, this 12 | * list of conditions and the following disclaimer in the documentation and/or 13 | * other materials provided with the distribution. 14 | * 15 | * Neither the name of the Carnegie Mellon University nor the names of its 16 | * contributors may be used to endorse or promote products derived from 17 | * this software without specific prior written permission. 18 | * 19 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 20 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 21 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 22 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 23 | * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 24 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 25 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 26 | * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 28 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | */ 30 | 31 | 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);}div.olMapViewport{-ms-touch-action:none;}.olLayerGrid .olTileImage{-webkit-transition:opacity .2s linear;-moz-transition:opacity .2s linear;-o-transition:opacity .2s linear;transition:opacity .2s linear;}.olTileImage{-webkit-transform:translateZ(0);-moz-transform:translateZ(0);-o-transform:translateZ(0);-ms-transform:translateZ(0);transform:translateZ(0);-webkit-backface-visibility:hidden;-moz-backface-visibility:hidden;-ms-backface-visibility:hidden;backface-visibility:hidden;-webkit-perspective:1000;-moz-perspective:1000;-ms-perspective:1000;perspective:1000;}@media only screen and max-width 600px{div.olControlZoom a:hover{background:rgba(0,60,136,0.5);}} 32 | -------------------------------------------------------------------------------- /js/theme/default/style.tidy.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2013, Carnegie Mellon University 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without modification, 6 | * are permitted provided that the following conditions are met: 7 | * 8 | * Redistributions of source code must retain the above copyright notice, this 9 | * list of conditions and the following disclaimer. 10 | * 11 | * Redistributions in binary form must reproduce the above copyright notice, this 12 | * list of conditions and the following disclaimer in the documentation and/or 13 | * other materials provided with the distribution. 14 | * 15 | * Neither the name of the Carnegie Mellon University nor the names of its 16 | * contributors may be used to endorse or promote products derived from 17 | * this software without specific prior written permission. 18 | * 19 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 20 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 21 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 22 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 23 | * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 24 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 25 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 26 | * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 28 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | */ 30 | 31 | div.olMap{z-index:0;cursor:default;margin:0!important;padding:0!important;}div.olMapViewport{text-align:left;-ms-touch-action:none;}.olLayerGoogleCopyright{left:2px;bottom:2px;}.olLayerGoogleV3.olLayerGoogleCopyright{right:auto!important;}.olLayerGooglePoweredBy{left:2px;bottom:15px;}.olLayerGoogleV3.olLayerGooglePoweredBy{bottom:15px!important;}.olForeignContainer{opacity:1!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;}.olTileImage{-webkit-transform:translateZ(0);-moz-transform:translateZ(0);-o-transform:translateZ(0);-ms-transform:translateZ(0);transform:translateZ(0);-webkit-backface-visibility:hidden;-moz-backface-visibility:hidden;-ms-backface-visibility:hidden;backface-visibility:hidden;-webkit-perspective:1000;-moz-perspective:1000;-ms-perspective:1000;perspective:1000;}.olTileReplacing{display:none;}img.olTileImage{max-width:none;}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);}} 32 | -------------------------------------------------------------------------------- /js/widgets.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @license 3 | * Copyright (c) 2013, Carnegie Mellon University 4 | * All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without modification, 7 | * are permitted provided that the following conditions are met: 8 | * 9 | * Redistributions of source code must retain the above copyright notice, this 10 | * list of conditions and the following disclaimer. 11 | * 12 | * Redistributions in binary form must reproduce the above copyright notice, this 13 | * list of conditions and the following disclaimer in the documentation and/or 14 | * other materials provided with the distribution. 15 | * 16 | * Neither the name of the Carnegie Mellon University nor the names of its 17 | * contributors may be used to endorse or promote products derived from 18 | * this software without specific prior written permission. 19 | * 20 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 21 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 22 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 24 | * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 25 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 26 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 27 | * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 29 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | * 31 | * widgets.js -- various for CAPComposer 32 | * version 1.0 - 20 August 2013 33 | * 34 | * Copyright (c) 2013, Carnegie Mellon University 35 | * All rights reserved. 36 | * 37 | * See LICENSE.txt for license terms (Modified BSD) 38 | * 39 | * DEPENDENCIES AND REQUIREMENTS: 40 | * OpenLayers, jQuery, jQuery Mobile and caplib.js must already be loaded 41 | * into the document. 42 | * 43 | */ 44 | 45 | /* Widget for a CAP tuple set element (set of valueName/value pairs, 46 | e.g., parameter, geocode). 47 | */ 48 | 49 | // Utility to escape HTML entities in user-supplied text 50 | function escape_text(rawText) { 51 | return $('
').text(rawText).html(); 52 | } 53 | 54 | var CapButtonWidget = function(label, width) { 55 | label = escape_text(label); 56 | width = escape_text(width); 57 | return $('' + 62 | '' + label + 63 | ''); 64 | }; 65 | 66 | var CapParameterWidget = function(name, placeholder) { 67 | name = escape_text(name); 68 | placeholder = escape_text(placeholder); 69 | return $('
' + 70 | '' + 73 | '
'); 79 | }; 80 | 81 | var CapTupleSetWidget = function(label, area, div, opt_onChange, opt_onDelete) { 82 | this.div = div; 83 | this.area = area; 84 | this.tuples = []; 85 | label = gettext('Add a %s').replace('%s', label); 86 | this.addButton = new CapButtonWidget(label, 200); 87 | this.addButton.on('click', null, this, this.addItem.bind(this)) 88 | .appendTo($(this.div)); 89 | this.customOnChange = opt_onChange; 90 | this.customOnDelete = opt_onDelete; 91 | }; 92 | 93 | 94 | CapTupleSetWidget.prototype = { 95 | changed: function(tuple, previous) { 96 | if (this.customOnChange) { 97 | this.customOnChange(tuple, previous); 98 | } 99 | }, 100 | addItem: function(event) { 101 | var newTuple = new CapTupleWidget(this, 500); 102 | this.tuples.push(newTuple); // add to array 103 | $(this.div).append($(newTuple.div)); // add to screen 104 | newTuple.valueName.focus(); // and put focus on valueName field 105 | }, 106 | addAndPopulate: function(valueName, value) { 107 | var newTuple = new CapTupleWidget(this, 500); 108 | $(newTuple.valueName).find('input').val(valueName); 109 | $(newTuple.value).find('input').val(value); 110 | this.tuples.push(newTuple); // add to array 111 | $(this.div).append($(newTuple.div)); // add to screen 112 | }, 113 | deleteItem: function(event) { 114 | var tuple = event.data; // ref to tuple to be removed 115 | var tupleSet = tuple.tupleSet; // ref to tupleSet 116 | // Remove from array. 117 | tupleSet.tuples.splice($.inArray(tuple, tupleSet.tuples), 1); 118 | $(tuple.div).remove(); // and also remove from screen 119 | if (tupleSet.customOnDelete 120 | && (event.triggerOnDelete === undefined || event.triggerOnDelete)) { 121 | tupleSet.customOnDelete(tuple); 122 | } 123 | }, 124 | deleteByValue: function(valueName, value, triggerOnDelete) { 125 | for (var i = 0; i < this.tuples.length; i++) { 126 | var tuple = this.tuples[i]; 127 | var tupleValue = tuple.getValue(); 128 | if (valueName == tupleValue.valueName && value == tupleValue.value) { 129 | this.deleteItem({data: tuple, triggerOnDelete: triggerOnDelete}); 130 | i--; 131 | } 132 | } 133 | }, 134 | contains: function(valueName, value) { 135 | for (var i = 0; i < this.tuples.length; i++) { 136 | var tupleValue = this.tuples[i].getValue(); 137 | if (tupleValue.valueName == valueName && tupleValue.value == value) { 138 | return true; 139 | } 140 | } 141 | return false; 142 | }, 143 | getAll: function() { // return widget contents as an array of arrays 144 | var items = []; 145 | for (var i = 0; i < this.tuples.length; i++) { 146 | items.push(this.tuples[i].getValue()); 147 | } 148 | return items; 149 | }, 150 | removeAll: function() { // Remove all tuples from the set. 151 | for (var i = this.tuples.length - 1; i >= 0; i--) { 152 | var tuple = this.tuples[i]; 153 | this.tuples.pop(tuple); // Remove from array. 154 | $(tuple.div).remove(); // And also remove from screen. 155 | if (this.customOnDelete) { 156 | this.customOnDelete(tuple); 157 | } 158 | } 159 | } 160 | }; // end CapTupleSetWidget definition 161 | 162 | 163 | // Widget for a CAP tuple (an individual valueName/value pair) 164 | var CapTupleWidget = function(tupleSet, widget_width) { 165 | this.tupleSet = tupleSet; 166 | this.div = $(document.createElement('div')).attr('class', 'tuple_holder'); 167 | $(this.div).width(widget_width); 168 | 169 | this.valueName = CapParameterWidget('valueName', gettext('name')); 170 | this.valueName.appendTo($(this.div)); 171 | this.valueName.previous = ''; 172 | this.valueName.find('input').change(this.onChange.bind(this)); 173 | 174 | this.value = CapParameterWidget('value', gettext('value')); 175 | this.value.appendTo($(this.div)); 176 | this.value.find('input').change(this.onChange.bind(this)); 177 | this.value.previous = ''; 178 | this.delButton = CapButtonWidget(gettext('Delete'), 75); 179 | this.delButton.on('click', null, this, 180 | tupleSet.deleteItem).appendTo($(this.div)); 181 | return this; 182 | }; 183 | 184 | CapTupleWidget.prototype.getValue = function() { 185 | return { 186 | valueName: escape_text(this.valueName.find('input').val()), 187 | value: escape_text(this.value.find('input').val()) 188 | }; 189 | }; 190 | 191 | CapTupleWidget.prototype.onChange = function(event) { 192 | this.tupleSet.changed(this.getValue(), { 193 | valueName: this.valueName.previous, 194 | value: this.value.previous 195 | }); 196 | var newValue = this.getValue(); 197 | this.valueName.previous = newValue.valueName; 198 | this.value.previous = newValue.value; 199 | }; 200 | -------------------------------------------------------------------------------- /js/widgets_test.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @fileoverview widgets.js unit tests. 3 | */ 4 | 5 | 6 | /* Define unit test module for caplib tests. */ 7 | QUnit.module('widgets.js Unit Tests'); 8 | 9 | /** Test CapTupleSetWidget */ 10 | QUnit.test('Test CapTupleSetWidget', function(assert) { 11 | var changes = []; 12 | var previous = []; 13 | var onChange = function(tuple, prevTuple) { 14 | changes.push(tuple); 15 | previous.push(prevTuple); 16 | }; 17 | 18 | var deletes = []; 19 | var onDelete = function(tuple) { 20 | deletes.push(tuple); 21 | }; 22 | 23 | var area = new Area('Fake area'); 24 | 25 | var tupleSet = new CapTupleSetWidget('Fake', area, 26 | $('#tuple-set-container'), onChange, onDelete); 27 | 28 | assert.equal(tupleSet.tuples.length, 0); 29 | tupleSet.addItem(); 30 | assert.equal(tupleSet.tuples.length, 1); 31 | assert.equal(changes.length, 0); 32 | 33 | tupleSet.addAndPopulate('testValueName', 'testValue'); 34 | assert.equal(tupleSet.tuples.length, 2); 35 | 36 | var tuple = tupleSet.tuples[tupleSet.tuples.length - 1]; 37 | tuple.value.find('input').change(); 38 | assert.deepEqual(tuple.getValue(), 39 | {valueName:'testValueName', value: 'testValue'}); 40 | assert.deepEqual(changes[0], 41 | {valueName:'testValueName', value: 'testValue'}); 42 | assert.deepEqual(previous[0], {valueName:'', value: ''}); 43 | assert.equal(tupleSet.contains('testValueName', 'newTestValue'), false); 44 | assert.equal(tupleSet.contains('testValueName', 'testValue'), true); 45 | 46 | tuple.value.find('input').val('newTestValue').change(); 47 | assert.deepEqual(tuple.getValue(), 48 | {valueName:'testValueName', value: 'newTestValue'}); 49 | assert.deepEqual(changes[1], 50 | {valueName:'testValueName', value: 'newTestValue'}); 51 | assert.deepEqual(previous[1], 52 | {valueName:'testValueName', value: 'testValue'}); 53 | assert.equal(tupleSet.contains('testValueName', 'newTestValue'), true); 54 | assert.equal(tupleSet.contains('testValueName', 'testValue'), false); 55 | 56 | tupleSet.deleteItem({data: tuple});assert.equal(1, tupleSet.tuples.length); 57 | assert.equal(tupleSet.tuples.length, 1); 58 | assert.equal(deletes.length, 1); 59 | 60 | tuple = tupleSet.tuples[0]; 61 | tuple.valueName.find('input').val('foo').change(); 62 | tuple.value.find('input').val('bar').change(); 63 | assert.deepEqual(tuple.getValue(), {valueName:'foo', value: 'bar'}); 64 | tupleSet.deleteByValue('foo', 'bar'); 65 | assert.equal(tupleSet.tuples.length, 0); 66 | assert.equal(deletes.length, 2); 67 | }); 68 | -------------------------------------------------------------------------------- /templates/area/cmu_b19.xml: -------------------------------------------------------------------------------- 1 | 2 | pending 3 | unverified 4 | 2014-02-25T23:07:03+00:00 5 | Exercise 6 | Alert 7 | Restricted 8 | 9 | en-us 10 | Env 11 | This is a test of myPAWS 12 | Assess 13 | Immediate 14 | Extreme 15 | Observed 16 | 2014-02-26T02:07:03+00:00 17 | Martin/DMI 18 | This is a test of myPAWS 19 | Just fooling around 20 | Log and ignore 21 | Hakan Erdogmus -:) 22 | 23 | CMU B19 24 | 37.41316,-122.05868 0.24472 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /templates/area/index.json: -------------------------------------------------------------------------------- 1 | { 2 | "templates" : [ 3 | { "label": "Test Area 1", "link":"client/templates/area/test_area1.xml" }, 4 | { "label": "Test Area 2", "link":"client/templates/area/test_area2.xml" }, 5 | { "label": "CMU B19", "link":"client/templates/area/cmu_b19.xml" } 6 | ] 7 | } 8 | -------------------------------------------------------------------------------- /templates/area/test_area1.xml: -------------------------------------------------------------------------------- 1 | 2 | ca15f4ac-7ee9-4391-a9da-a75f1c3374dd 3 | none@incident.com 4 | 2013-08-20T21:50:52+00:00 5 | Actual 6 | Update 7 | Public 8 | Created using CAPCreator0.9 9 | none@incident.com,7e43a818-ce4f-4a70-81e3-c0e1abe5f6d2,2013-08-20T20:01:50+00:00 10 | 11 | en-us 12 | Env 13 | 14 | AllClear 15 | Expected 16 | Severe 17 | Possible 18 | 2013-08-20T22:50:52+00:00 19 | This is the description 20 | This is the instruction 21 | 22 | LinkedIn Office 23 | 37.37878,-122.08917 0.08809 24 | 25 | geo_code1 26 | geo_code_value1 27 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /templates/area/test_area2.xml: -------------------------------------------------------------------------------- 1 | 2 | ca15f4ac-7ee9-4391-a9da-a75f1c3374dd 3 | none@incident.com 4 | 2013-08-20T21:50:52+00:00 5 | Actual 6 | Update 7 | Public 8 | Created using CAPCreator0.9 9 | none@incident.com,7e43a818-ce4f-4a70-81e3-c0e1abe5f6d2,2013-08-20T20:01:50+00:00 10 | 11 | en-us 12 | Env 13 | 14 | AllClear 15 | Expected 16 | Severe 17 | Possible 18 | 2013-08-20T22:50:52+00:00 19 | This is the description 20 | This is the instruction 21 | 22 | Walmart 23 | 37.37878,-122.08917 0.08809 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /templates/message/exercise.xml: -------------------------------------------------------------------------------- 1 | 2 | pending 3 | unverified 4 | 2014-02-25T23:07:03+00:00 5 | Exercise 6 | Alert 7 | Restricted 8 | 9 | en-us 10 | Env 11 | This is a test of myPAWS 12 | Assess 13 | Immediate 14 | Extreme 15 | Observed 16 | 2014-02-26T02:07:03+00:00 17 | Martin/DMI 18 | This is a test of myPAWS 19 | Just fooling around 20 | Log and ignore 21 | Haka Erdogmus -:) 22 | 23 | CMU B19 24 | 37.41316,-122.05868 0.24472 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /templates/message/index.json: -------------------------------------------------------------------------------- 1 | { 2 | "templates" : [ 3 | { "label":"Test Message - Fire", "link":"client/templates/message/test_msg1.xml" }, 4 | { "label":"Exercise", "link":"client/templates/message/exercise.xml" }, 5 | { "label":"Test Message - Avalanche", "link":"client/templates/message/test_msg2.xml" }, 6 | { "label":"Test Message - Civil Danger", "link":"client/templates/message/test_msg3.xml" }, 7 | { "label":"Test Message - Earthquake", "link":"client/templates/message/test_msg4.xml" }, 8 | { "label":"Test Message - CAE", "link":"client/templates/message/test_msg5.xml" } 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /templates/message/test_msg1.xml: -------------------------------------------------------------------------------- 1 | 2 | Actual 3 | Alert 4 | Public 5 | Example of a message template 6 | 7 | en-us 8 | Fire 9 | FRW 10 | Shelter 11 | Expected 12 | Severe 13 | Likely 14 | Fire Hazard 15 | Take Shelter 16 | 17 | 18 | -------------------------------------------------------------------------------- /templates/message/test_msg2.xml: -------------------------------------------------------------------------------- 1 | 2 | Actual 3 | Alert 4 | Public 5 | Example of a message template 6 | 7 | en-us 8 | Geo 9 | AVW 10 | Evacuate 11 | Expected 12 | Severe 13 | Likely 14 | Geophysical Hazard 15 | Evacuate Immediately 16 | 17 | 18 | -------------------------------------------------------------------------------- /templates/message/test_msg3.xml: -------------------------------------------------------------------------------- 1 | 2 | Actual 3 | Alert 4 | Public 5 | Message template 3 6 | 7 | en-us 8 | Safety 9 | CDW 10 | Prepare 11 | Immediate 12 | Severe 13 | Observed 14 | Public Safety Hazard 15 | Make Preparations for Evacuation 16 | 17 | 18 | -------------------------------------------------------------------------------- /templates/message/test_msg4.xml: -------------------------------------------------------------------------------- 1 | 2 | Actual 3 | Alert 4 | Public 5 | Message template 3 6 | 7 | en-us 8 | Geo 9 | EQW 10 | Execute 11 | Immediate 12 | Severe 13 | Likely 14 | Geophysical Hazard 15 | Execute Pre-planned Action 16 | 17 | 18 | -------------------------------------------------------------------------------- /templates/message/test_msg5.xml: -------------------------------------------------------------------------------- 1 | 2 | Actual 3 | Alert 4 | Public 5 | Message template 3 6 | 7 | en-us 8 | Rescue 9 | CAE 10 | Evacuate 11 | Expected 12 | Severe 13 | Possible 14 | Rescue Type Hazard 15 | Evacuate the Area 16 | 17 | 18 | --------------------------------------------------------------------------------