├── .gitignore ├── LICENSE ├── README.md ├── css ├── images │ ├── ui-bg_flat_0_aaaaaa_40x100.png │ ├── ui-bg_flat_75_ffffff_40x100.png │ ├── ui-bg_glass_55_fbf9ee_1x400.png │ ├── ui-bg_glass_65_ffffff_1x400.png │ ├── ui-bg_glass_75_dadada_1x400.png │ ├── ui-bg_glass_75_e6e6e6_1x400.png │ ├── ui-bg_glass_95_fef1ec_1x400.png │ └── ui-bg_highlight-soft_75_cccccc_1x100.png └── jquery-ui.css ├── index.html └── js ├── cron.js ├── dateFormat.js ├── jquery-ui.js ├── jquery.croneditor.js └── jquery.js /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014 Marak Squires 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

Visual Cron Editor

2 | 3 | A jQuery plugin which creates a visual editor for [Cron Jobs](http://en.wikipedia.org/wiki/Cron). 4 | 5 |

Live Demo

6 | 7 | 8 | 9 | The cron format supports seconds. If your cron editor doesn't support seconds you can ignore this parameters. 10 | 11 | 12 |

Example Code

13 | 14 | ``` js 15 | // be sure to include all required files ( see index.html file ) 16 | $(document).ready(function(){ 17 | // turn the div into a cron editor 18 | $('.myDiv').croneditor({ 19 | value: "* * * * *" 20 | }); 21 | }); 22 | ``` 23 | 24 |

License

25 | 26 | Copyright (c) 2014 Marak Squires 27 | 28 | Permission is hereby granted, free of charge, to any person obtaining a copy 29 | of this software and associated documentation files (the "Software"), to deal 30 | in the Software without restriction, including without limitation the rights 31 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 32 | copies of the Software, and to permit persons to whom the Software is 33 | furnished to do so, subject to the following conditions: 34 | 35 | The above copyright notice and this permission notice shall be included in 36 | all copies or substantial portions of the Software. 37 | 38 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 39 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 40 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 41 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 42 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 43 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 44 | THE SOFTWARE. -------------------------------------------------------------------------------- /css/images/ui-bg_flat_0_aaaaaa_40x100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Marak/cron-editor/e97ab1fa063bc16d1de3f2e86a3690ea3a42f339/css/images/ui-bg_flat_0_aaaaaa_40x100.png -------------------------------------------------------------------------------- /css/images/ui-bg_flat_75_ffffff_40x100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Marak/cron-editor/e97ab1fa063bc16d1de3f2e86a3690ea3a42f339/css/images/ui-bg_flat_75_ffffff_40x100.png -------------------------------------------------------------------------------- /css/images/ui-bg_glass_55_fbf9ee_1x400.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Marak/cron-editor/e97ab1fa063bc16d1de3f2e86a3690ea3a42f339/css/images/ui-bg_glass_55_fbf9ee_1x400.png -------------------------------------------------------------------------------- /css/images/ui-bg_glass_65_ffffff_1x400.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Marak/cron-editor/e97ab1fa063bc16d1de3f2e86a3690ea3a42f339/css/images/ui-bg_glass_65_ffffff_1x400.png -------------------------------------------------------------------------------- /css/images/ui-bg_glass_75_dadada_1x400.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Marak/cron-editor/e97ab1fa063bc16d1de3f2e86a3690ea3a42f339/css/images/ui-bg_glass_75_dadada_1x400.png -------------------------------------------------------------------------------- /css/images/ui-bg_glass_75_e6e6e6_1x400.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Marak/cron-editor/e97ab1fa063bc16d1de3f2e86a3690ea3a42f339/css/images/ui-bg_glass_75_e6e6e6_1x400.png -------------------------------------------------------------------------------- /css/images/ui-bg_glass_95_fef1ec_1x400.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Marak/cron-editor/e97ab1fa063bc16d1de3f2e86a3690ea3a42f339/css/images/ui-bg_glass_95_fef1ec_1x400.png -------------------------------------------------------------------------------- /css/images/ui-bg_highlight-soft_75_cccccc_1x100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Marak/cron-editor/e97ab1fa063bc16d1de3f2e86a3690ea3a42f339/css/images/ui-bg_highlight-soft_75_cccccc_1x100.png -------------------------------------------------------------------------------- /css/jquery-ui.css: -------------------------------------------------------------------------------- 1 | /*! jQuery UI - v1.11.2 - 2014-11-13 2 | * http://jqueryui.com 3 | * Includes: core.css, button.css, slider.css, tabs.css, theme.css 4 | * To view and modify this theme, visit http://jqueryui.com/themeroller/?ffDefault=Verdana%2CArial%2Csans-serif&fwDefault=normal&fsDefault=1.1em&cornerRadius=4px&bgColorHeader=cccccc&bgTextureHeader=highlight_soft&bgImgOpacityHeader=75&borderColorHeader=aaaaaa&fcHeader=222222&iconColorHeader=222222&bgColorContent=ffffff&bgTextureContent=flat&bgImgOpacityContent=75&borderColorContent=aaaaaa&fcContent=222222&iconColorContent=222222&bgColorDefault=e6e6e6&bgTextureDefault=glass&bgImgOpacityDefault=75&borderColorDefault=d3d3d3&fcDefault=555555&iconColorDefault=888888&bgColorHover=dadada&bgTextureHover=glass&bgImgOpacityHover=75&borderColorHover=999999&fcHover=212121&iconColorHover=454545&bgColorActive=ffffff&bgTextureActive=glass&bgImgOpacityActive=65&borderColorActive=aaaaaa&fcActive=212121&iconColorActive=454545&bgColorHighlight=fbf9ee&bgTextureHighlight=glass&bgImgOpacityHighlight=55&borderColorHighlight=fcefa1&fcHighlight=363636&iconColorHighlight=2e83ff&bgColorError=fef1ec&bgTextureError=glass&bgImgOpacityError=95&borderColorError=cd0a0a&fcError=cd0a0a&iconColorError=cd0a0a&bgColorOverlay=aaaaaa&bgTextureOverlay=flat&bgImgOpacityOverlay=0&opacityOverlay=30&bgColorShadow=aaaaaa&bgTextureShadow=flat&bgImgOpacityShadow=0&opacityShadow=30&thicknessShadow=8px&offsetTopShadow=-8px&offsetLeftShadow=-8px&cornerRadiusShadow=8px 5 | * Copyright 2014 jQuery Foundation and other contributors; Licensed MIT */ 6 | 7 | /* Layout helpers 8 | ----------------------------------*/ 9 | .ui-helper-hidden { 10 | display: none; 11 | } 12 | .ui-helper-hidden-accessible { 13 | border: 0; 14 | clip: rect(0 0 0 0); 15 | height: 1px; 16 | margin: -1px; 17 | overflow: hidden; 18 | padding: 0; 19 | position: absolute; 20 | width: 1px; 21 | } 22 | .ui-helper-reset { 23 | margin: 0; 24 | padding: 0; 25 | border: 0; 26 | outline: 0; 27 | line-height: 1.3; 28 | text-decoration: none; 29 | font-size: 100%; 30 | list-style: none; 31 | } 32 | .ui-helper-clearfix:before, 33 | .ui-helper-clearfix:after { 34 | content: ""; 35 | display: table; 36 | border-collapse: collapse; 37 | } 38 | .ui-helper-clearfix:after { 39 | clear: both; 40 | } 41 | .ui-helper-clearfix { 42 | min-height: 0; /* support: IE7 */ 43 | } 44 | .ui-helper-zfix { 45 | width: 100%; 46 | height: 100%; 47 | top: 0; 48 | left: 0; 49 | position: absolute; 50 | opacity: 0; 51 | filter:Alpha(Opacity=0); /* support: IE8 */ 52 | } 53 | 54 | .ui-front { 55 | z-index: 100; 56 | } 57 | 58 | 59 | /* Interaction Cues 60 | ----------------------------------*/ 61 | .ui-state-disabled { 62 | cursor: default !important; 63 | } 64 | 65 | 66 | /* Icons 67 | ----------------------------------*/ 68 | 69 | /* states and images */ 70 | .ui-icon { 71 | display: block; 72 | text-indent: -99999px; 73 | overflow: hidden; 74 | background-repeat: no-repeat; 75 | } 76 | 77 | 78 | /* Misc visuals 79 | ----------------------------------*/ 80 | 81 | /* Overlays */ 82 | .ui-widget-overlay { 83 | position: fixed; 84 | top: 0; 85 | left: 0; 86 | width: 100%; 87 | height: 100%; 88 | } 89 | .ui-button { 90 | display: inline-block; 91 | position: relative; 92 | padding: 0; 93 | line-height: normal; 94 | margin-right: .1em; 95 | cursor: pointer; 96 | vertical-align: middle; 97 | text-align: center; 98 | overflow: visible; /* removes extra width in IE */ 99 | } 100 | .ui-button, 101 | .ui-button:link, 102 | .ui-button:visited, 103 | .ui-button:hover, 104 | .ui-button:active { 105 | text-decoration: none; 106 | } 107 | /* to make room for the icon, a width needs to be set here */ 108 | .ui-button-icon-only { 109 | width: 2.2em; 110 | } 111 | /* button elements seem to need a little more width */ 112 | button.ui-button-icon-only { 113 | width: 2.4em; 114 | } 115 | .ui-button-icons-only { 116 | width: 3.4em; 117 | } 118 | button.ui-button-icons-only { 119 | width: 3.7em; 120 | } 121 | 122 | /* button text element */ 123 | .ui-button .ui-button-text { 124 | display: block; 125 | line-height: normal; 126 | } 127 | .ui-button-text-only .ui-button-text { 128 | padding: .4em 1em; 129 | } 130 | .ui-button-icon-only .ui-button-text, 131 | .ui-button-icons-only .ui-button-text { 132 | padding: .4em; 133 | text-indent: -9999999px; 134 | } 135 | .ui-button-text-icon-primary .ui-button-text, 136 | .ui-button-text-icons .ui-button-text { 137 | padding: .4em 1em .4em 2.1em; 138 | } 139 | .ui-button-text-icon-secondary .ui-button-text, 140 | .ui-button-text-icons .ui-button-text { 141 | padding: .4em 2.1em .4em 1em; 142 | } 143 | .ui-button-text-icons .ui-button-text { 144 | padding-left: 2.1em; 145 | padding-right: 2.1em; 146 | } 147 | /* no icon support for input elements, provide padding by default */ 148 | input.ui-button { 149 | padding: .4em 1em; 150 | } 151 | 152 | /* button icon element(s) */ 153 | .ui-button-icon-only .ui-icon, 154 | .ui-button-text-icon-primary .ui-icon, 155 | .ui-button-text-icon-secondary .ui-icon, 156 | .ui-button-text-icons .ui-icon, 157 | .ui-button-icons-only .ui-icon { 158 | position: absolute; 159 | top: 50%; 160 | margin-top: -8px; 161 | } 162 | .ui-button-icon-only .ui-icon { 163 | left: 50%; 164 | margin-left: -8px; 165 | } 166 | .ui-button-text-icon-primary .ui-button-icon-primary, 167 | .ui-button-text-icons .ui-button-icon-primary, 168 | .ui-button-icons-only .ui-button-icon-primary { 169 | left: .5em; 170 | } 171 | .ui-button-text-icon-secondary .ui-button-icon-secondary, 172 | .ui-button-text-icons .ui-button-icon-secondary, 173 | .ui-button-icons-only .ui-button-icon-secondary { 174 | right: .5em; 175 | } 176 | 177 | /* button sets */ 178 | .ui-buttonset { 179 | margin-right: 7px; 180 | } 181 | .ui-buttonset .ui-button { 182 | margin-left: 0; 183 | margin-right: -.3em; 184 | } 185 | 186 | /* workarounds */ 187 | /* reset extra padding in Firefox, see h5bp.com/l */ 188 | input.ui-button::-moz-focus-inner, 189 | button.ui-button::-moz-focus-inner { 190 | border: 0; 191 | padding: 0; 192 | } 193 | .ui-slider { 194 | position: relative; 195 | text-align: left; 196 | } 197 | .ui-slider .ui-slider-handle { 198 | position: absolute; 199 | z-index: 2; 200 | width: 1.2em; 201 | height: 1.2em; 202 | cursor: default; 203 | -ms-touch-action: none; 204 | touch-action: none; 205 | } 206 | .ui-slider .ui-slider-range { 207 | position: absolute; 208 | z-index: 1; 209 | font-size: .7em; 210 | display: block; 211 | border: 0; 212 | background-position: 0 0; 213 | } 214 | 215 | /* support: IE8 - See #6727 */ 216 | .ui-slider.ui-state-disabled .ui-slider-handle, 217 | .ui-slider.ui-state-disabled .ui-slider-range { 218 | filter: inherit; 219 | } 220 | 221 | .ui-slider-horizontal { 222 | height: .8em; 223 | } 224 | .ui-slider-horizontal .ui-slider-handle { 225 | top: -.3em; 226 | margin-left: -.6em; 227 | } 228 | .ui-slider-horizontal .ui-slider-range { 229 | top: 0; 230 | height: 100%; 231 | } 232 | .ui-slider-horizontal .ui-slider-range-min { 233 | left: 0; 234 | } 235 | .ui-slider-horizontal .ui-slider-range-max { 236 | right: 0; 237 | } 238 | 239 | .ui-slider-vertical { 240 | width: .8em; 241 | height: 100px; 242 | } 243 | .ui-slider-vertical .ui-slider-handle { 244 | left: -.3em; 245 | margin-left: 0; 246 | margin-bottom: -.6em; 247 | } 248 | .ui-slider-vertical .ui-slider-range { 249 | left: 0; 250 | width: 100%; 251 | } 252 | .ui-slider-vertical .ui-slider-range-min { 253 | bottom: 0; 254 | } 255 | .ui-slider-vertical .ui-slider-range-max { 256 | top: 0; 257 | } 258 | .ui-tabs { 259 | position: relative;/* position: relative prevents IE scroll bug (element with position: relative inside container with overflow: auto appear as "fixed") */ 260 | padding: .2em; 261 | } 262 | .ui-tabs .ui-tabs-nav { 263 | margin: 0; 264 | padding: .2em .2em 0; 265 | } 266 | .ui-tabs .ui-tabs-nav li { 267 | list-style: none; 268 | float: left; 269 | position: relative; 270 | top: 0; 271 | margin: 1px .2em 0 0; 272 | border-bottom-width: 0; 273 | padding: 0; 274 | white-space: nowrap; 275 | } 276 | .ui-tabs .ui-tabs-nav .ui-tabs-anchor { 277 | float: left; 278 | padding: .5em 1em; 279 | text-decoration: none; 280 | } 281 | .ui-tabs .ui-tabs-nav li.ui-tabs-active { 282 | margin-bottom: -1px; 283 | padding-bottom: 1px; 284 | } 285 | .ui-tabs .ui-tabs-nav li.ui-tabs-active .ui-tabs-anchor, 286 | .ui-tabs .ui-tabs-nav li.ui-state-disabled .ui-tabs-anchor, 287 | .ui-tabs .ui-tabs-nav li.ui-tabs-loading .ui-tabs-anchor { 288 | cursor: text; 289 | } 290 | .ui-tabs-collapsible .ui-tabs-nav li.ui-tabs-active .ui-tabs-anchor { 291 | cursor: pointer; 292 | } 293 | .ui-tabs .ui-tabs-panel { 294 | display: block; 295 | border-width: 0; 296 | padding: 1em 1.4em; 297 | background: none; 298 | } 299 | 300 | /* Component containers 301 | ----------------------------------*/ 302 | .ui-widget { 303 | font-family: Verdana,Arial,sans-serif; 304 | font-size: 1.1em; 305 | } 306 | .ui-widget .ui-widget { 307 | font-size: 1em; 308 | } 309 | .ui-widget input, 310 | .ui-widget select, 311 | .ui-widget textarea, 312 | .ui-widget button { 313 | font-family: Verdana,Arial,sans-serif; 314 | font-size: 1em; 315 | } 316 | .ui-widget-content { 317 | border: 1px solid #aaaaaa; 318 | background: #ffffff url("images/ui-bg_flat_75_ffffff_40x100.png") 50% 50% repeat-x; 319 | color: #222222; 320 | } 321 | .ui-widget-content a { 322 | color: #222222; 323 | } 324 | .ui-widget-header { 325 | border: 1px solid #aaaaaa; 326 | background: #cccccc url("images/ui-bg_highlight-soft_75_cccccc_1x100.png") 50% 50% repeat-x; 327 | color: #222222; 328 | font-weight: bold; 329 | } 330 | .ui-widget-header a { 331 | color: #222222; 332 | } 333 | 334 | /* Interaction states 335 | ----------------------------------*/ 336 | .ui-state-default, 337 | .ui-widget-content .ui-state-default, 338 | .ui-widget-header .ui-state-default { 339 | border: 1px solid #d3d3d3; 340 | background: #e6e6e6 url("images/ui-bg_glass_75_e6e6e6_1x400.png") 50% 50% repeat-x; 341 | font-weight: normal; 342 | color: #555555; 343 | } 344 | .ui-state-default a, 345 | .ui-state-default a:link, 346 | .ui-state-default a:visited { 347 | color: #555555; 348 | text-decoration: none; 349 | } 350 | .ui-state-hover, 351 | .ui-widget-content .ui-state-hover, 352 | .ui-widget-header .ui-state-hover, 353 | .ui-state-focus, 354 | .ui-widget-content .ui-state-focus, 355 | .ui-widget-header .ui-state-focus { 356 | border: 1px solid #999999; 357 | background: #dadada url("images/ui-bg_glass_75_dadada_1x400.png") 50% 50% repeat-x; 358 | font-weight: normal; 359 | color: #212121; 360 | } 361 | .ui-state-hover a, 362 | .ui-state-hover a:hover, 363 | .ui-state-hover a:link, 364 | .ui-state-hover a:visited, 365 | .ui-state-focus a, 366 | .ui-state-focus a:hover, 367 | .ui-state-focus a:link, 368 | .ui-state-focus a:visited { 369 | color: #212121; 370 | text-decoration: none; 371 | } 372 | .ui-state-active, 373 | .ui-widget-content .ui-state-active, 374 | .ui-widget-header .ui-state-active { 375 | border: 1px solid #aaaaaa; 376 | background: #ffffff url("images/ui-bg_glass_65_ffffff_1x400.png") 50% 50% repeat-x; 377 | font-weight: normal; 378 | color: #212121; 379 | } 380 | .ui-state-active a, 381 | .ui-state-active a:link, 382 | .ui-state-active a:visited { 383 | color: #212121; 384 | text-decoration: none; 385 | } 386 | 387 | /* Interaction Cues 388 | ----------------------------------*/ 389 | .ui-state-highlight, 390 | .ui-widget-content .ui-state-highlight, 391 | .ui-widget-header .ui-state-highlight { 392 | border: 1px solid #fcefa1; 393 | background: #fbf9ee url("images/ui-bg_glass_55_fbf9ee_1x400.png") 50% 50% repeat-x; 394 | color: #363636; 395 | } 396 | .ui-state-highlight a, 397 | .ui-widget-content .ui-state-highlight a, 398 | .ui-widget-header .ui-state-highlight a { 399 | color: #363636; 400 | } 401 | .ui-state-error, 402 | .ui-widget-content .ui-state-error, 403 | .ui-widget-header .ui-state-error { 404 | border: 1px solid #cd0a0a; 405 | background: #fef1ec url("images/ui-bg_glass_95_fef1ec_1x400.png") 50% 50% repeat-x; 406 | color: #cd0a0a; 407 | } 408 | .ui-state-error a, 409 | .ui-widget-content .ui-state-error a, 410 | .ui-widget-header .ui-state-error a { 411 | color: #cd0a0a; 412 | } 413 | .ui-state-error-text, 414 | .ui-widget-content .ui-state-error-text, 415 | .ui-widget-header .ui-state-error-text { 416 | color: #cd0a0a; 417 | } 418 | .ui-priority-primary, 419 | .ui-widget-content .ui-priority-primary, 420 | .ui-widget-header .ui-priority-primary { 421 | font-weight: bold; 422 | } 423 | .ui-priority-secondary, 424 | .ui-widget-content .ui-priority-secondary, 425 | .ui-widget-header .ui-priority-secondary { 426 | opacity: .7; 427 | filter:Alpha(Opacity=70); /* support: IE8 */ 428 | font-weight: normal; 429 | } 430 | .ui-state-disabled, 431 | .ui-widget-content .ui-state-disabled, 432 | .ui-widget-header .ui-state-disabled { 433 | opacity: .35; 434 | filter:Alpha(Opacity=35); /* support: IE8 */ 435 | background-image: none; 436 | } 437 | .ui-state-disabled .ui-icon { 438 | filter:Alpha(Opacity=35); /* support: IE8 - See #6059 */ 439 | } 440 | 441 | /* Icons 442 | ----------------------------------*/ 443 | 444 | /* states and images */ 445 | .ui-icon { 446 | width: 16px; 447 | height: 16px; 448 | } 449 | .ui-icon, 450 | .ui-widget-content .ui-icon { 451 | background-image: url("images/ui-icons_222222_256x240.png"); 452 | } 453 | .ui-widget-header .ui-icon { 454 | background-image: url("images/ui-icons_222222_256x240.png"); 455 | } 456 | .ui-state-default .ui-icon { 457 | background-image: url("images/ui-icons_888888_256x240.png"); 458 | } 459 | .ui-state-hover .ui-icon, 460 | .ui-state-focus .ui-icon { 461 | background-image: url("images/ui-icons_454545_256x240.png"); 462 | } 463 | .ui-state-active .ui-icon { 464 | background-image: url("images/ui-icons_454545_256x240.png"); 465 | } 466 | .ui-state-highlight .ui-icon { 467 | background-image: url("images/ui-icons_2e83ff_256x240.png"); 468 | } 469 | .ui-state-error .ui-icon, 470 | .ui-state-error-text .ui-icon { 471 | background-image: url("images/ui-icons_cd0a0a_256x240.png"); 472 | } 473 | 474 | /* positioning */ 475 | .ui-icon-blank { background-position: 16px 16px; } 476 | .ui-icon-carat-1-n { background-position: 0 0; } 477 | .ui-icon-carat-1-ne { background-position: -16px 0; } 478 | .ui-icon-carat-1-e { background-position: -32px 0; } 479 | .ui-icon-carat-1-se { background-position: -48px 0; } 480 | .ui-icon-carat-1-s { background-position: -64px 0; } 481 | .ui-icon-carat-1-sw { background-position: -80px 0; } 482 | .ui-icon-carat-1-w { background-position: -96px 0; } 483 | .ui-icon-carat-1-nw { background-position: -112px 0; } 484 | .ui-icon-carat-2-n-s { background-position: -128px 0; } 485 | .ui-icon-carat-2-e-w { background-position: -144px 0; } 486 | .ui-icon-triangle-1-n { background-position: 0 -16px; } 487 | .ui-icon-triangle-1-ne { background-position: -16px -16px; } 488 | .ui-icon-triangle-1-e { background-position: -32px -16px; } 489 | .ui-icon-triangle-1-se { background-position: -48px -16px; } 490 | .ui-icon-triangle-1-s { background-position: -64px -16px; } 491 | .ui-icon-triangle-1-sw { background-position: -80px -16px; } 492 | .ui-icon-triangle-1-w { background-position: -96px -16px; } 493 | .ui-icon-triangle-1-nw { background-position: -112px -16px; } 494 | .ui-icon-triangle-2-n-s { background-position: -128px -16px; } 495 | .ui-icon-triangle-2-e-w { background-position: -144px -16px; } 496 | .ui-icon-arrow-1-n { background-position: 0 -32px; } 497 | .ui-icon-arrow-1-ne { background-position: -16px -32px; } 498 | .ui-icon-arrow-1-e { background-position: -32px -32px; } 499 | .ui-icon-arrow-1-se { background-position: -48px -32px; } 500 | .ui-icon-arrow-1-s { background-position: -64px -32px; } 501 | .ui-icon-arrow-1-sw { background-position: -80px -32px; } 502 | .ui-icon-arrow-1-w { background-position: -96px -32px; } 503 | .ui-icon-arrow-1-nw { background-position: -112px -32px; } 504 | .ui-icon-arrow-2-n-s { background-position: -128px -32px; } 505 | .ui-icon-arrow-2-ne-sw { background-position: -144px -32px; } 506 | .ui-icon-arrow-2-e-w { background-position: -160px -32px; } 507 | .ui-icon-arrow-2-se-nw { background-position: -176px -32px; } 508 | .ui-icon-arrowstop-1-n { background-position: -192px -32px; } 509 | .ui-icon-arrowstop-1-e { background-position: -208px -32px; } 510 | .ui-icon-arrowstop-1-s { background-position: -224px -32px; } 511 | .ui-icon-arrowstop-1-w { background-position: -240px -32px; } 512 | .ui-icon-arrowthick-1-n { background-position: 0 -48px; } 513 | .ui-icon-arrowthick-1-ne { background-position: -16px -48px; } 514 | .ui-icon-arrowthick-1-e { background-position: -32px -48px; } 515 | .ui-icon-arrowthick-1-se { background-position: -48px -48px; } 516 | .ui-icon-arrowthick-1-s { background-position: -64px -48px; } 517 | .ui-icon-arrowthick-1-sw { background-position: -80px -48px; } 518 | .ui-icon-arrowthick-1-w { background-position: -96px -48px; } 519 | .ui-icon-arrowthick-1-nw { background-position: -112px -48px; } 520 | .ui-icon-arrowthick-2-n-s { background-position: -128px -48px; } 521 | .ui-icon-arrowthick-2-ne-sw { background-position: -144px -48px; } 522 | .ui-icon-arrowthick-2-e-w { background-position: -160px -48px; } 523 | .ui-icon-arrowthick-2-se-nw { background-position: -176px -48px; } 524 | .ui-icon-arrowthickstop-1-n { background-position: -192px -48px; } 525 | .ui-icon-arrowthickstop-1-e { background-position: -208px -48px; } 526 | .ui-icon-arrowthickstop-1-s { background-position: -224px -48px; } 527 | .ui-icon-arrowthickstop-1-w { background-position: -240px -48px; } 528 | .ui-icon-arrowreturnthick-1-w { background-position: 0 -64px; } 529 | .ui-icon-arrowreturnthick-1-n { background-position: -16px -64px; } 530 | .ui-icon-arrowreturnthick-1-e { background-position: -32px -64px; } 531 | .ui-icon-arrowreturnthick-1-s { background-position: -48px -64px; } 532 | .ui-icon-arrowreturn-1-w { background-position: -64px -64px; } 533 | .ui-icon-arrowreturn-1-n { background-position: -80px -64px; } 534 | .ui-icon-arrowreturn-1-e { background-position: -96px -64px; } 535 | .ui-icon-arrowreturn-1-s { background-position: -112px -64px; } 536 | .ui-icon-arrowrefresh-1-w { background-position: -128px -64px; } 537 | .ui-icon-arrowrefresh-1-n { background-position: -144px -64px; } 538 | .ui-icon-arrowrefresh-1-e { background-position: -160px -64px; } 539 | .ui-icon-arrowrefresh-1-s { background-position: -176px -64px; } 540 | .ui-icon-arrow-4 { background-position: 0 -80px; } 541 | .ui-icon-arrow-4-diag { background-position: -16px -80px; } 542 | .ui-icon-extlink { background-position: -32px -80px; } 543 | .ui-icon-newwin { background-position: -48px -80px; } 544 | .ui-icon-refresh { background-position: -64px -80px; } 545 | .ui-icon-shuffle { background-position: -80px -80px; } 546 | .ui-icon-transfer-e-w { background-position: -96px -80px; } 547 | .ui-icon-transferthick-e-w { background-position: -112px -80px; } 548 | .ui-icon-folder-collapsed { background-position: 0 -96px; } 549 | .ui-icon-folder-open { background-position: -16px -96px; } 550 | .ui-icon-document { background-position: -32px -96px; } 551 | .ui-icon-document-b { background-position: -48px -96px; } 552 | .ui-icon-note { background-position: -64px -96px; } 553 | .ui-icon-mail-closed { background-position: -80px -96px; } 554 | .ui-icon-mail-open { background-position: -96px -96px; } 555 | .ui-icon-suitcase { background-position: -112px -96px; } 556 | .ui-icon-comment { background-position: -128px -96px; } 557 | .ui-icon-person { background-position: -144px -96px; } 558 | .ui-icon-print { background-position: -160px -96px; } 559 | .ui-icon-trash { background-position: -176px -96px; } 560 | .ui-icon-locked { background-position: -192px -96px; } 561 | .ui-icon-unlocked { background-position: -208px -96px; } 562 | .ui-icon-bookmark { background-position: -224px -96px; } 563 | .ui-icon-tag { background-position: -240px -96px; } 564 | .ui-icon-home { background-position: 0 -112px; } 565 | .ui-icon-flag { background-position: -16px -112px; } 566 | .ui-icon-calendar { background-position: -32px -112px; } 567 | .ui-icon-cart { background-position: -48px -112px; } 568 | .ui-icon-pencil { background-position: -64px -112px; } 569 | .ui-icon-clock { background-position: -80px -112px; } 570 | .ui-icon-disk { background-position: -96px -112px; } 571 | .ui-icon-calculator { background-position: -112px -112px; } 572 | .ui-icon-zoomin { background-position: -128px -112px; } 573 | .ui-icon-zoomout { background-position: -144px -112px; } 574 | .ui-icon-search { background-position: -160px -112px; } 575 | .ui-icon-wrench { background-position: -176px -112px; } 576 | .ui-icon-gear { background-position: -192px -112px; } 577 | .ui-icon-heart { background-position: -208px -112px; } 578 | .ui-icon-star { background-position: -224px -112px; } 579 | .ui-icon-link { background-position: -240px -112px; } 580 | .ui-icon-cancel { background-position: 0 -128px; } 581 | .ui-icon-plus { background-position: -16px -128px; } 582 | .ui-icon-plusthick { background-position: -32px -128px; } 583 | .ui-icon-minus { background-position: -48px -128px; } 584 | .ui-icon-minusthick { background-position: -64px -128px; } 585 | .ui-icon-close { background-position: -80px -128px; } 586 | .ui-icon-closethick { background-position: -96px -128px; } 587 | .ui-icon-key { background-position: -112px -128px; } 588 | .ui-icon-lightbulb { background-position: -128px -128px; } 589 | .ui-icon-scissors { background-position: -144px -128px; } 590 | .ui-icon-clipboard { background-position: -160px -128px; } 591 | .ui-icon-copy { background-position: -176px -128px; } 592 | .ui-icon-contact { background-position: -192px -128px; } 593 | .ui-icon-image { background-position: -208px -128px; } 594 | .ui-icon-video { background-position: -224px -128px; } 595 | .ui-icon-script { background-position: -240px -128px; } 596 | .ui-icon-alert { background-position: 0 -144px; } 597 | .ui-icon-info { background-position: -16px -144px; } 598 | .ui-icon-notice { background-position: -32px -144px; } 599 | .ui-icon-help { background-position: -48px -144px; } 600 | .ui-icon-check { background-position: -64px -144px; } 601 | .ui-icon-bullet { background-position: -80px -144px; } 602 | .ui-icon-radio-on { background-position: -96px -144px; } 603 | .ui-icon-radio-off { background-position: -112px -144px; } 604 | .ui-icon-pin-w { background-position: -128px -144px; } 605 | .ui-icon-pin-s { background-position: -144px -144px; } 606 | .ui-icon-play { background-position: 0 -160px; } 607 | .ui-icon-pause { background-position: -16px -160px; } 608 | .ui-icon-seek-next { background-position: -32px -160px; } 609 | .ui-icon-seek-prev { background-position: -48px -160px; } 610 | .ui-icon-seek-end { background-position: -64px -160px; } 611 | .ui-icon-seek-start { background-position: -80px -160px; } 612 | /* ui-icon-seek-first is deprecated, use ui-icon-seek-start instead */ 613 | .ui-icon-seek-first { background-position: -80px -160px; } 614 | .ui-icon-stop { background-position: -96px -160px; } 615 | .ui-icon-eject { background-position: -112px -160px; } 616 | .ui-icon-volume-off { background-position: -128px -160px; } 617 | .ui-icon-volume-on { background-position: -144px -160px; } 618 | .ui-icon-power { background-position: 0 -176px; } 619 | .ui-icon-signal-diag { background-position: -16px -176px; } 620 | .ui-icon-signal { background-position: -32px -176px; } 621 | .ui-icon-battery-0 { background-position: -48px -176px; } 622 | .ui-icon-battery-1 { background-position: -64px -176px; } 623 | .ui-icon-battery-2 { background-position: -80px -176px; } 624 | .ui-icon-battery-3 { background-position: -96px -176px; } 625 | .ui-icon-circle-plus { background-position: 0 -192px; } 626 | .ui-icon-circle-minus { background-position: -16px -192px; } 627 | .ui-icon-circle-close { background-position: -32px -192px; } 628 | .ui-icon-circle-triangle-e { background-position: -48px -192px; } 629 | .ui-icon-circle-triangle-s { background-position: -64px -192px; } 630 | .ui-icon-circle-triangle-w { background-position: -80px -192px; } 631 | .ui-icon-circle-triangle-n { background-position: -96px -192px; } 632 | .ui-icon-circle-arrow-e { background-position: -112px -192px; } 633 | .ui-icon-circle-arrow-s { background-position: -128px -192px; } 634 | .ui-icon-circle-arrow-w { background-position: -144px -192px; } 635 | .ui-icon-circle-arrow-n { background-position: -160px -192px; } 636 | .ui-icon-circle-zoomin { background-position: -176px -192px; } 637 | .ui-icon-circle-zoomout { background-position: -192px -192px; } 638 | .ui-icon-circle-check { background-position: -208px -192px; } 639 | .ui-icon-circlesmall-plus { background-position: 0 -208px; } 640 | .ui-icon-circlesmall-minus { background-position: -16px -208px; } 641 | .ui-icon-circlesmall-close { background-position: -32px -208px; } 642 | .ui-icon-squaresmall-plus { background-position: -48px -208px; } 643 | .ui-icon-squaresmall-minus { background-position: -64px -208px; } 644 | .ui-icon-squaresmall-close { background-position: -80px -208px; } 645 | .ui-icon-grip-dotted-vertical { background-position: 0 -224px; } 646 | .ui-icon-grip-dotted-horizontal { background-position: -16px -224px; } 647 | .ui-icon-grip-solid-vertical { background-position: -32px -224px; } 648 | .ui-icon-grip-solid-horizontal { background-position: -48px -224px; } 649 | .ui-icon-gripsmall-diagonal-se { background-position: -64px -224px; } 650 | .ui-icon-grip-diagonal-se { background-position: -80px -224px; } 651 | 652 | 653 | /* Misc visuals 654 | ----------------------------------*/ 655 | 656 | /* Corner radius */ 657 | .ui-corner-all, 658 | .ui-corner-top, 659 | .ui-corner-left, 660 | .ui-corner-tl { 661 | border-top-left-radius: 4px; 662 | } 663 | .ui-corner-all, 664 | .ui-corner-top, 665 | .ui-corner-right, 666 | .ui-corner-tr { 667 | border-top-right-radius: 4px; 668 | } 669 | .ui-corner-all, 670 | .ui-corner-bottom, 671 | .ui-corner-left, 672 | .ui-corner-bl { 673 | border-bottom-left-radius: 4px; 674 | } 675 | .ui-corner-all, 676 | .ui-corner-bottom, 677 | .ui-corner-right, 678 | .ui-corner-br { 679 | border-bottom-right-radius: 4px; 680 | } 681 | 682 | /* Overlays */ 683 | .ui-widget-overlay { 684 | background: #aaaaaa url("images/ui-bg_flat_0_aaaaaa_40x100.png") 50% 50% repeat-x; 685 | opacity: .3; 686 | filter: Alpha(Opacity=30); /* support: IE8 */ 687 | } 688 | .ui-widget-shadow { 689 | margin: -8px 0 0 -8px; 690 | padding: 8px; 691 | background: #aaaaaa url("images/ui-bg_flat_0_aaaaaa_40x100.png") 50% 50% repeat-x; 692 | opacity: .3; 693 | filter: Alpha(Opacity=30); /* support: IE8 */ 694 | border-radius: 8px; 695 | } 696 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Visual Cron Editor - Edit and Create Cron Jobs With Ease 9 | 10 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 58 | 59 | 60 |

Visual Cron Editor

61 |
62 | Fork me on GitHub 63 | 64 | -------------------------------------------------------------------------------- /js/cron.js: -------------------------------------------------------------------------------- 1 | !function(e){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=e();else if("function"==typeof define&&define.amd)define([],e);else{var f;"undefined"!=typeof window?f=window:"undefined"!=typeof global?f=global:"undefined"!=typeof self&&(f=self),f.cron=e()}}(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o 24 * 60 * 60 * 1000 ? 0 : date.getHours() + 1); 154 | date.setMinutes(0); 155 | continue; 156 | } 157 | 158 | if (!(date.getMinutes() in this.minute)) { 159 | date.setMinutes(date.getMinutes() == 59 && diff > 60 * 60 * 1000 ? 0 : date.getMinutes() + 1); 160 | date.setSeconds(0); 161 | continue; 162 | } 163 | 164 | if (!(date.getSeconds() in this.second)) { 165 | date.setSeconds(date.getSeconds() == 59 && diff > 60 * 1000 ? 0 : date.getSeconds() + 1); 166 | continue; 167 | } 168 | 169 | break; 170 | } 171 | 172 | return date; 173 | }, 174 | 175 | /** 176 | * wildcard, or all params in array (for to string) 177 | */ 178 | _wcOrAll: function(type) { 179 | if (this._hasAll(type)) return '*'; 180 | 181 | var all = []; 182 | for (var time in this[type]) { 183 | all.push(time); 184 | } 185 | 186 | return all.join(','); 187 | }, 188 | 189 | /** 190 | */ 191 | _hasAll: function(type) { 192 | var constrain = CronTime.constraints[CronTime.map.indexOf(type)]; 193 | 194 | for (var i = constrain[0], n = constrain[1]; i < n; i++) { 195 | if (!(i in this[type])) return false; 196 | } 197 | 198 | return true; 199 | }, 200 | 201 | 202 | /** 203 | * Parse the cron syntax. 204 | */ 205 | _parse: function() { 206 | var aliases = CronTime.aliases, 207 | source = this.source.replace(/[a-z]{1,3}/ig, function(alias) { 208 | alias = alias.toLowerCase(); 209 | 210 | if (alias in aliases) { 211 | return aliases[alias]; 212 | } 213 | 214 | throw new Error('Unknown alias: ' + alias); 215 | }), 216 | split = source.replace(/^\s\s*|\s\s*$/g, '').split(/\s+/), 217 | cur, i = 0, 218 | len = CronTime.map.length; 219 | 220 | for (; i < CronTime.map.length; i++) { 221 | // If the split source string doesn't contain all digits, 222 | // assume defaults for first n missing digits. 223 | // This adds support for 5-digit standard cron syntax 224 | cur = split[i - (len - split.length)] || CronTime.parseDefaults[i]; 225 | this._parseField(cur, CronTime.map[i], CronTime.constraints[i]); 226 | } 227 | }, 228 | 229 | /** 230 | * Parse a field from the cron syntax. 231 | */ 232 | _parseField: function(field, type, constraints) { 233 | 234 | //var rangePattern = /^(\*)(?:\/(\d+))?$|(\d+)(?:-(\d+))?(?:\/(\d+))?(?:,|$)/g 235 | var rangePattern = /^(\d+)(?:-(\d+))?(?:\/(\d+))?$/g, 236 | typeObj = this[type], 237 | diff, pointer, 238 | low = constraints[0], 239 | high = constraints[1]; 240 | 241 | // * is a shortcut to [lower-upper] range 242 | field = field.replace(/\*/g, low + '-' + high); 243 | 244 | //commas separate information, so split based on those 245 | var allRanges = field.split(','); 246 | 247 | for (var i = 0; i < allRanges.length; i++) { 248 | if (allRanges[i].match(rangePattern)) { 249 | allRanges[i].replace(rangePattern, function($0, lower, upper, step) { 250 | step = parseInt(step) || 1; 251 | // Positive integer higher than constraints[0] 252 | lower = Math.max(low, ~~Math.abs(lower)); 253 | 254 | // Positive integer lower than constraints[1] 255 | upper = upper ? Math.min(high, ~~Math.abs(upper)) : lower; 256 | 257 | // Count from the lower barrier to the upper 258 | pointer = lower; 259 | 260 | do { 261 | typeObj[pointer] = true 262 | pointer += step; 263 | } while (pointer <= upper); 264 | 265 | }); 266 | } else { 267 | throw new Error('Field (' + field + ') cannot be parsed'); 268 | } 269 | } 270 | } 271 | }; 272 | 273 | 274 | 275 | function CronJob(cronTime, onTick, onComplete, start, timeZone, context) { 276 | if (typeof cronTime != "string" && arguments.length == 1) { 277 | //crontime is an object... 278 | onTick = cronTime.onTick; 279 | onComplete = cronTime.onComplete; 280 | context = cronTime.context; 281 | start = cronTime.start; 282 | timeZone = cronTime.timeZone; 283 | cronTime = cronTime.cronTime; 284 | } 285 | 286 | if (timeZone && !(CronDate.prototype.setTimezone)) console.log('You specified a Timezone but have not included the `time` module. Timezone functionality is disabled. Please install the `time` module to use Timezones in your application.'); 287 | 288 | this.context = (context || this); 289 | this._callbacks = []; 290 | this.onComplete = onComplete; 291 | this.cronTime = new CronTime(cronTime, timeZone); 292 | 293 | this.addCallback(onTick); 294 | 295 | if (start) this.start(); 296 | 297 | return this; 298 | } 299 | 300 | CronJob.prototype = { 301 | /** 302 | * Add a method to fire onTick 303 | */ 304 | addCallback: function(callback) { 305 | //only functions 306 | if (typeof callback == 'function') this._callbacks.push(callback); 307 | }, 308 | 309 | /** 310 | * Fire all callbacks registered. 311 | */ 312 | _callback: function() { 313 | for (var i = (this._callbacks.length - 1); i >= 0; i--) { 314 | 315 | //send this so the callback can call this.stop(); 316 | this._callbacks[i].call(this.context, this.onComplete); 317 | } 318 | }, 319 | 320 | /** 321 | * Manually set the time of a job 322 | */ 323 | setTime: function(time) { 324 | if (!(time instanceof CronTime)) throw '\'time\' must be an instance of CronTime.'; 325 | this.stop(); 326 | this.cronTime = time; 327 | }, 328 | 329 | /** 330 | * Return the next scheduled date for a job 331 | */ 332 | nextDate: function() { 333 | return this.cronTime.sendAt(); 334 | }, 335 | 336 | /** 337 | * Start the cronjob. 338 | */ 339 | start: function() { 340 | if (this.running) return; 341 | 342 | var MAXDELAY = 2147483647; // The maximum number of milliseconds setTimeout will wait. 343 | var self = this; 344 | var timeout = this.cronTime.getTimeout(); 345 | var remaining = 0; 346 | 347 | if (this.cronTime.realDate) this.runOnce = true; 348 | 349 | // The callback wrapper checks if it needs to sleep another period or not 350 | // and does the real callback logic when it's time. 351 | 352 | function callbackWrapper() { 353 | 354 | // If there is sleep time remaining, calculate how long and go to sleep 355 | // again. This processing might make us miss the deadline by a few ms 356 | // times the number of sleep sessions. Given a MAXDELAY of almost a 357 | // month, this should be no issue. 358 | 359 | if (remaining) { 360 | if (remaining > MAXDELAY) { 361 | remaining -= MAXDELAY; 362 | timeout = MAXDELAY; 363 | } else { 364 | timeout = remaining; 365 | remaining = 0; 366 | } 367 | 368 | self._timeout = setTimeout(callbackWrapper, timeout); 369 | } else { 370 | 371 | // We have arrived at the correct point in time. 372 | 373 | self.running = false; 374 | 375 | //start before calling back so the callbacks have the ability to stop the cron job 376 | if (!(self.runOnce)) self.start(); 377 | 378 | self._callback(); 379 | } 380 | } 381 | 382 | if (timeout >= 0) { 383 | this.running = true; 384 | 385 | // Don't try to sleep more than MAXDELAY ms at a time. 386 | 387 | if (timeout > MAXDELAY) { 388 | remaining = timeout - MAXDELAY; 389 | timeout = MAXDELAY; 390 | } 391 | 392 | this._timeout = setTimeout(callbackWrapper, timeout); 393 | } else { 394 | this.stop(); 395 | } 396 | }, 397 | 398 | /** 399 | * Stop the cronjob. 400 | */ 401 | stop: function() { 402 | clearTimeout(this._timeout); 403 | this.running = false; 404 | if (this.onComplete) this.onComplete(); 405 | } 406 | }; 407 | 408 | if (exports) { 409 | exports.job = function(cronTime, onTick, onComplete) { 410 | return new CronJob(cronTime, onTick, onComplete); 411 | } 412 | 413 | exports.time = function(cronTime, timeZone) { 414 | return new CronTime(cronTime, timeZone); 415 | } 416 | 417 | exports.sendAt = function(cronTime) { 418 | return exports.time(cronTime).sendAt(); 419 | } 420 | 421 | exports.timeout = function(cronTime) { 422 | return exports.time(cronTime).getTimeout(); 423 | } 424 | 425 | exports.CronJob = CronJob; 426 | exports.CronTime = CronTime; 427 | } 428 | 429 | },{"time":2}],2:[function(require,module,exports){ 430 | (function (process){ 431 | /** 432 | * Module dependencies. 433 | */ 434 | 435 | var debug = require('debug')('time') 436 | , fs = require('fs') 437 | , path = require('path') 438 | , bindings = require('bindings')('time.node') 439 | , MILLIS_PER_SECOND = 1000 440 | , DAYS_OF_WEEK = ['Sunday','Monday','Tuesday','Wednesday','Thursday','Friday','Saturday'] 441 | , MONTHS = ['January','February','March','April','May','June','July','August','September','October','November','December'] 442 | , TZ_BLACKLIST = [ 'SystemV', 'Etc' ]; 443 | 444 | /** 445 | * Extends a "Date" constructor with node-time's extensions. 446 | * By default, `time.Date` is extended with this function. 447 | * If you want the global your your module-specific Date to be extended, 448 | * then invoke this function on the Date constructor. 449 | */ 450 | 451 | exports = module.exports = function (Date) { 452 | debug('extending Date constructor'); 453 | var p = Date.prototype; 454 | p.getTimezone = getTimezone; 455 | p.setTimezone = setTimezone; 456 | p.getTimezoneAbbr = getTimezoneAbbr; 457 | return exports; 458 | } 459 | 460 | /** 461 | * The initial timezone of the process. This env var may initially be undefined, 462 | * in which case node-time will attempt to resolve and set the variable. 463 | */ 464 | 465 | exports.currentTimezone = process.env.TZ; 466 | 467 | /** 468 | * Export the raw functions from the bindings. 469 | */ 470 | 471 | exports.time = bindings.time; 472 | exports.localtime = bindings.localtime; 473 | exports.mktime = bindings.mktime; 474 | 475 | /** 476 | * A "hack" of sorts to force getting our own Date instance. 477 | * Otherwise, in normal cases, the global Natives are shared between 478 | * contexts (not what we want)... 479 | */ 480 | 481 | var _Date = process.env.NODE_MODULE_CONTEXTS 482 | ? Date 483 | : require('vm').runInNewContext("Date"); 484 | 485 | /** 486 | * Add the node-time extensions (setTimezone(), etc.) 487 | */ 488 | 489 | exports(_Date); 490 | 491 | /** 492 | * During startup, we synchronously attempt to determine the location of the 493 | * timezone dir, or TZDIR on some systems. This isn't necessary for the 494 | * C bindings, however it's needed for the `listTimezones()` function and for 495 | * resolving the 'initial' timezone to use. 496 | */ 497 | 498 | debug('attempting to resolve timezone directory.'); 499 | var possibleTzdirs = [ 500 | '/usr/share/zoneinfo' 501 | , '/usr/lib/zoneinfo' 502 | , '/usr/share/lib/zoneinfo' 503 | ]; 504 | var TZDIR = process.env.TZDIR; 505 | if (TZDIR) { 506 | debug('got env-defined TZDIR:', TZDIR); 507 | possibleTzdirs.unshift(TZDIR); 508 | } 509 | while (possibleTzdirs.length > 0) { 510 | var d = possibleTzdirs.shift(); 511 | debug('checking if directory exists:', d); 512 | try { 513 | if (fs.statSync(d).isDirectory()) { 514 | TZDIR = d; 515 | break; 516 | } 517 | } catch (e) { 518 | debug(e); 519 | } 520 | } 521 | possibleTzdirs = null; // garbage collect 522 | if (TZDIR) { 523 | debug('found timezone directory at:', TZDIR); 524 | } else { 525 | debug('WARN: Could not find timezone directory. listTimezones() won\'t work'); 526 | } 527 | 528 | /** 529 | * Older versions of node-time would require the user to have the TZ 530 | * environment variable set, otherwise undesirable results would happen. Now 531 | * node-time tries to automatically determine the current timezone for you. 532 | */ 533 | 534 | if (!exports.currentTimezone) { 535 | debug('`process.env.TZ` not initially set, attempting to resolve'); 536 | try { 537 | var currentTimezonePath = fs.readlinkSync('/etc/localtime'); 538 | if (currentTimezonePath.substring(0, TZDIR.length) === TZDIR) { 539 | // Got It! 540 | var zone = currentTimezonePath.substring(TZDIR.length + 1); 541 | exports.currentTimezone = process.env.TZ = zone; 542 | debug('resolved initial timezone:', zone); 543 | } 544 | } catch (e) { 545 | debug(e); 546 | } 547 | } 548 | 549 | if (!exports.currentTimezone) { 550 | debug('"currentTimezone" still not set. Checking "/etc/timezone"'); 551 | try { 552 | var zone = fs.readFileSync('/etc/timezone', 'utf8').trim(); 553 | exports.currentTimezone = process.env.TZ = zone; 554 | debug('resolved initial timezone:', zone); 555 | } catch (e) { 556 | debug(e); 557 | } 558 | } 559 | 560 | /** 561 | * The user-facing 'tzset' function is a thin wrapper around the native binding to 562 | * 'tzset()'. This function accepts a timezone String to set the process' timezone 563 | * to. Returns an object with the zoneinfo for the timezone. 564 | * 565 | * Throws (on *some* platforms) when the desired timezone could not be loaded. 566 | * 567 | * Sets the `currentTimezone` property on the exports. 568 | */ 569 | 570 | function tzset (tz) { 571 | if (tz) { 572 | process.env.TZ = tz; 573 | } 574 | var usedTz = process.env.TZ; 575 | var rtn = bindings.tzset(); 576 | debug('set the current timezone to:', usedTz); 577 | if (!rtn.tzname[1] && rtn.timezone === 0) { 578 | debug('got bad zoneinfo object:', rtn); 579 | var err = new Error("Unknown Timezone: '" + usedTz + "'"); 580 | for (var i in rtn) { 581 | err[i] = rtn[i]; 582 | } 583 | throw err; 584 | } 585 | exports.currentTimezone = usedTz; 586 | exports._currentZoneinfo = rtn; 587 | return rtn; 588 | } 589 | exports.tzset = tzset; 590 | 591 | /** 592 | * Lists the timezones that the current system can accept. It does this by going 593 | * on a recursive walk through the timezone dir and collecting filenames. 594 | */ 595 | 596 | function listTimezones () { 597 | if (arguments.length == 0) { 598 | throw new Error("You must set a callback"); 599 | } 600 | if (typeof arguments[arguments.length - 1] != "function") { 601 | throw new Error("You must set a callback"); 602 | } 603 | var cb = arguments[arguments.length - 1] 604 | , subset = (arguments.length > 1 ? arguments[0] : null) 605 | 606 | return listTimezonesFolder(subset ? subset + "/" : "", subset ? path.join(TZDIR, "/" + subset) : TZDIR, function (err, tzs) { 607 | if (err) return cb(err); 608 | cb(null, tzs.sort()); 609 | }); 610 | } 611 | exports.listTimezones = listTimezones; 612 | 613 | function listTimezonesFolder(prefix, folder, cb) { 614 | var timezones = []; 615 | 616 | fs.readdir(folder, function (err, files) { 617 | if (err) return cb(err); 618 | 619 | var pending_stats = files.length; 620 | 621 | for (var i = 0; i < files.length; i++) { 622 | if (~TZ_BLACKLIST.indexOf(files[i]) 623 | || files[i].indexOf(".") >= 0 624 | || files[i][0].toUpperCase() != files[i][0]) { 625 | pending_stats--; 626 | continue 627 | } 628 | fs.stat(path.join(folder, files[i]), (function (file) { 629 | return function (err, stats) { 630 | if (!err) { 631 | if (stats.isDirectory()) { 632 | listTimezonesFolder(prefix + file + "/", path.join(folder, file), function (err, tzs) { 633 | if (!err) { 634 | timezones = timezones.concat(tzs); 635 | } 636 | pending_stats--; 637 | if (pending_stats == 0) cb(null, timezones); 638 | }); 639 | return; 640 | } 641 | if (prefix.length > 0) timezones.push(prefix + file); 642 | } 643 | pending_stats--; 644 | if (pending_stats == 0) cb(null, timezones); 645 | }; 646 | })(files[i])); 647 | } 648 | }); 649 | } 650 | 651 | /** 652 | * The "setTimezone" function is the "entry point" for a Date instance. 653 | * It must be called after an instance has been created. After, the 'getSeconds()', 654 | * 'getHours()', 'getDays()', etc. functions will return values relative 655 | * to the time zone specified. 656 | */ 657 | 658 | function setTimezone (timezone, relative) { 659 | debug('Date#setTimezone(%s, %s)', timezone, relative); 660 | 661 | // If `true` is passed in as the second argument, then the Date instance 662 | // will have it's timezone set, but it's current local values will remain 663 | // the same (i.e. the Date's internal time value will be changed) 664 | var ms, s, m, h, d, mo, y 665 | if (relative) { 666 | y = this.getFullYear() 667 | mo = this.getMonth() 668 | d = this.getDate() 669 | h = this.getHours() 670 | m = this.getMinutes() 671 | s = this.getSeconds() 672 | ms = this.getMilliseconds() 673 | } 674 | 675 | // If the current process timezone doesn't match the desired timezone, then call 676 | // tzset() to change the current timezone of the process. 677 | var oldTz = exports.currentTimezone 678 | , tz = exports._currentZoneinfo; 679 | if (!tz || oldTz !== timezone) { 680 | debug('current timezone is not "%s", calling tzset()', timezone); 681 | tz = exports.tzset(timezone); 682 | } 683 | 684 | // Get the zoneinfo for this Date instance's time value 685 | var zoneInfo = exports.localtime(this.getTime() / 1000); 686 | 687 | // Change the timezone back if we changed it originally 688 | if (oldTz != timezone) { 689 | debug('setting timezone back to "%s"', oldTz); 690 | exports.tzset(oldTz); 691 | } 692 | oldTz = null; 693 | 694 | // If we got to here without throwing an Error, then 695 | // a valid timezone was requested, and we should have 696 | // a valid zoneInfo Object. 697 | this.getTimezone = function getTimezone() { 698 | return timezone; 699 | } 700 | 701 | // Returns the day of the month (1-31) for the specified date according to local time. 702 | this.getDate = function getDate() { 703 | return zoneInfo.dayOfMonth; 704 | } 705 | // Returns the day of the week (0-6) for the specified date according to local time. 706 | this.getDay = function getDay() { 707 | return zoneInfo.dayOfWeek; 708 | } 709 | // Deprecated. Returns the year (usually 2-3 digits) in the specified date according 710 | // to local time. Use `getFullYear()` instead. 711 | this.getYear = function getYear() { 712 | return zoneInfo.year; 713 | } 714 | // Returns the year (4 digits for 4-digit years) of the specified date according to local time. 715 | this.getFullYear = function getFullYear() { 716 | return zoneInfo.year + 1900; 717 | } 718 | // Returns the hour (0-23) in the specified date according to local time. 719 | this.getHours = function getHours() { 720 | return zoneInfo.hours; 721 | } 722 | // Returns the minutes (0-59) in the specified date according to local time. 723 | this.getMinutes = function getMinutes() { 724 | return zoneInfo.minutes; 725 | } 726 | // Returns the month (0-11) in the specified date according to local time. 727 | this.getMonth = function getMonth() { 728 | return zoneInfo.month; 729 | } 730 | // Returns the seconds (0-59) in the specified date according to local time. 731 | this.getSeconds = function getSeconds() { 732 | return zoneInfo.seconds; 733 | } 734 | // Returns the timezone offset from GMT the Date instance currently is in, 735 | // in minutes. Also, left of GMT is positive, right of GMT is negative. 736 | this.getTimezoneOffset = function getTimezoneOffset() { 737 | return -zoneInfo.gmtOffset / 60; 738 | } 739 | // NON-STANDARD: Returns the abbreviation (e.g. EST, EDT) for the specified time zone. 740 | this.getTimezoneAbbr = function getTimezoneAbbr() { 741 | return tz.tzname[zoneInfo.isDaylightSavings ? 1 : 0]; 742 | } 743 | 744 | // Sets day, month and year at once 745 | this.setAllDateFields = function setAllDateFields(y,mo,d) { 746 | return this.setFullYear(y,mo,d); 747 | } 748 | // Sets the day of the month (from 1-31) in the current timezone 749 | this.setDate = function setDate(d) { 750 | zoneInfo.dayOfMonth = d; 751 | return mktime.call(this); 752 | } 753 | // Sets the year (four digits) in the current timezone 754 | this.setFullYear = function setFullYear(y,mo,d) { 755 | zoneInfo.year = y - 1900; 756 | if(arguments.length > 1) 757 | zoneInfo.month = mo; 758 | if(arguments.length > 2) 759 | zoneInfo.dayOfMonth = d; 760 | return mktime.call(this); 761 | } 762 | // Sets the hour (from 0-23) in the current timezone 763 | this.setHours = function setHours(h,m,s,ms) { 764 | zoneInfo.hours = h; 765 | if(arguments.length > 1) 766 | zoneInfo.minutes = m; 767 | if(arguments.length > 2) 768 | zoneInfo.seconds = s; 769 | if(arguments.length > 3) { 770 | mktime.call(this); 771 | var diff = ms - this.getMilliseconds(); 772 | return this.setTime(this.getTime() + diff); 773 | } else 774 | return mktime.call(this); 775 | } 776 | // Sets the milliseconds (from 0-999) in the current timezone 777 | this.setMilliseconds = function setMilliseconds(ms) { 778 | var diff = ms - this.getMilliseconds(); 779 | return this.setTime(this.getTime() + diff); 780 | } 781 | // Set the minutes (from 0-59) in the current timezone 782 | this.setMinutes = function setMinutes(m,s,ms) { 783 | zoneInfo.minutes = m; 784 | if(arguments.length > 1) 785 | zoneInfo.seconds = s; 786 | if(arguments.length > 2) { 787 | mktime.call(this); 788 | var diff = ms - this.getMilliseconds(); 789 | return this.setTime(this.getTime() + diff); 790 | } else 791 | return mktime.call(this); 792 | } 793 | // Sets the month (from 0-11) in the current timezone 794 | this.setMonth = function setMonth(mo,d) { 795 | zoneInfo.month = mo; 796 | if(arguments.length > 1) 797 | zoneInfo.dayOfMonth = d; 798 | return mktime.call(this); 799 | } 800 | // Sets the seconds (from 0-59) in the current timezone 801 | this.setSeconds = function setSeconds(s,ms) { 802 | zoneInfo.seconds = s; 803 | if(arguments.length > 1) { 804 | mktime.call(this); 805 | var diff = ms - this.getMilliseconds(); 806 | return this.setTime(this.getTime() + diff); 807 | } else 808 | return mktime.call(this); 809 | } 810 | // Sets a date and time by adding or subtracting a specified number of 811 | // milliseconds to/from midnight January 1, 1970. 812 | this.setTime = function setTime(v) { 813 | var rtn = _Date.prototype.setTime.call(this, v); 814 | // Since this function changes the internal UTC epoch date value, we need to 815 | // re-setup these timezone translation functions to reflect the new value 816 | reset.call(this); 817 | return rtn; 818 | } 819 | // Sets the day of the month, according to universal time (from 1-31) 820 | this.setUTCDate = function setUTCDate(d) { 821 | var rtn = _Date.prototype.setUTCDate.call(this, d); 822 | reset.call(this); 823 | return rtn; 824 | } 825 | // Sets the year, according to universal time (four digits) 826 | this.setUTCFullYear = function setUTCFullYear(y,mo,d) { 827 | var rtn; 828 | switch(arguments.length) { 829 | case 1: 830 | rtn = _Date.prototype.setUTCFullYear.call(this, y); break; 831 | case 2: 832 | rtn = _Date.prototype.setUTCFullYear.call(this, y,mo); break; 833 | case 3: 834 | rtn = _Date.prototype.setUTCFullYear.call(this, y,mo,d); break; 835 | } 836 | reset.call(this); 837 | return rtn; 838 | } 839 | // Sets the hour, according to universal time (from 0-23) 840 | this.setUTCHours = function setUTCHours(h,m,s,ms) { 841 | var rtn; 842 | switch(arguments.length) { 843 | case 1: 844 | rtn = _Date.prototype.setUTCHours.call(this, h); break; 845 | case 2: 846 | rtn = _Date.prototype.setUTCHours.call(this, h,m); break; 847 | case 3: 848 | rtn = _Date.prototype.setUTCHours.call(this, h,m,s); break; 849 | case 4: 850 | rtn = _Date.prototype.setUTCHours.call(this, h,m,s,ms); break; 851 | } 852 | reset.call(this); 853 | return rtn; 854 | } 855 | // Sets the milliseconds, according to universal time (from 0-999) 856 | this.setUTCMilliseconds = function setUTCMillseconds(ms) { 857 | var rtn = _Date.prototype.setUTCMilliseconds.call(this, ms); 858 | reset.call(this); 859 | return rtn; 860 | } 861 | // Set the minutes, according to universal time (from 0-59) 862 | this.setUTCMinutes = function setUTCMinutes(m,s,ms) { 863 | var rtn; 864 | switch(arguments.length) { 865 | case 1: 866 | rtn = _Date.prototype.setUTCMinutes.call(this, m); break; 867 | case 2: 868 | rtn = _Date.prototype.setUTCMinutes.call(this, m,s); break; 869 | case 3: 870 | rtn = _Date.prototype.setUTCMinutes.call(this, m,s,ms); break; 871 | } 872 | reset.call(this); 873 | return rtn; 874 | } 875 | // Sets the month, according to universal time (from 0-11) 876 | this.setUTCMonth = function setUTCMonth(mo,d) { 877 | var rtn; 878 | switch(arguments.length) { 879 | case 1: 880 | rtn = _Date.prototype.setUTCMonth.call(this, mo); break; 881 | case 2: 882 | rtn = _Date.prototype.setUTCMonth.call(this, mo,d); break; 883 | } 884 | reset.call(this); 885 | return rtn; 886 | } 887 | // Set the seconds, according to universal time (from 0-59) 888 | this.setUTCSeconds = function setUTCSeconds(s,ms) { 889 | var rtn; 890 | switch(arguments.length) { 891 | case 1: 892 | rtn = _Date.prototype.setUTCSeconds.call(this, s); break; 893 | case 2: 894 | rtn = _Date.prototype.setUTCSeconds.call(this, s,ms); break; 895 | } 896 | reset.call(this); 897 | return rtn; 898 | } 899 | 900 | this.toDateString = function toDateString() { 901 | return DAYS_OF_WEEK[this.getDay()].substring(0, 3) + ' ' + MONTHS[this.getMonth()].substring(0, 3) + ' ' + pad(this.getDate(), 2) + ' ' + this.getFullYear(); 902 | } 903 | 904 | this.toTimeString = function toTimeString() { 905 | var offset = Math.abs(zoneInfo.gmtOffset / 60); // total minutes 906 | // split into HHMM: 907 | var hours = pad(Math.floor(offset / 60), 2); 908 | var minutes = pad(offset % 60, 2); 909 | return this.toLocaleTimeString() + ' GMT' + (zoneInfo.gmtOffset >= 0 ? '+' : '-') + hours + minutes 910 | + ' (' + tz.tzname[zoneInfo.isDaylightSavings ? 1 : 0] + ')'; 911 | } 912 | 913 | this.toString = function toString() { 914 | return this.toDateString() + ' ' + this.toTimeString(); 915 | } 916 | 917 | this.toLocaleDateString = function toLocaleDateString() { 918 | return DAYS_OF_WEEK[this.getDay()] + ', ' + MONTHS[this.getMonth()] + ' ' + pad(this.getDate(), 2) + ', ' + this.getFullYear(); 919 | } 920 | 921 | this.toLocaleTimeString = function toLocaleTimeString() { 922 | return pad(this.getHours(), 2) + ':' + pad(this.getMinutes(), 2) + ':' + pad(this.getSeconds(), 2); 923 | } 924 | 925 | this.toLocaleString = this.toString; 926 | 927 | if (relative) { 928 | this.setAllDateFields(y,mo,d) 929 | this.setHours(h) 930 | this.setMinutes(m) 931 | this.setSeconds(s) 932 | this.setMilliseconds(ms) 933 | ms = s = m = h = d = mo = y = null 934 | } 935 | 936 | 937 | // Used internally by the 'set*' functions above... 938 | function reset () { 939 | this.setTimezone(this.getTimezone()); 940 | } 941 | // 'mktime' calls 'reset' implicitly through 'setTime()' 942 | function mktime () { 943 | var oldTz = process.env.TZ; 944 | exports.tzset(this.getTimezone()); 945 | zoneInfo.isDaylightSavings = -1; // Auto-detect the timezone 946 | var t = exports.mktime(zoneInfo); 947 | if (oldTz) { 948 | exports.tzset(oldTz); 949 | oldTz = null; 950 | } 951 | return this.setTime( (t * MILLIS_PER_SECOND) + this.getMilliseconds() ); 952 | } 953 | 954 | return this; 955 | } 956 | 957 | // Returns a "String" of the last value set in "setTimezone". 958 | // TODO: Return something when 'setTimezone' hasn't been called yet. 959 | function getTimezone () { 960 | throw new Error('You must call "setTimezone(tz)" before "getTimezone()" may be called'); 961 | } 962 | 963 | // NON-STANDARD: Returns the abbreviated timezone name, also taking daylight 964 | // savings into consideration. Useful for the presentation layer of a Date 965 | // instance. 966 | function getTimezoneAbbr () { 967 | var str = this.toString().match(/\([A-Z]+\)/)[0]; 968 | return str.substring(1, str.length-1); 969 | } 970 | 971 | // Export the modified 'Date' instance. Users should either use this with the 972 | // 'new' operator, or extend an already existing Date instance with 'extend()'. 973 | // An optional, NON-STANDARD, "timezone" argument may be appended as the final 974 | // argument, in order to specify the initial timezone the Date instance should 975 | // be created with. 976 | function Date (year, month, day, hour, minute, second, millisecond, timezone) { 977 | if (!(this instanceof Date)) { 978 | return new Date(year, month, day, hour, minute, second, millisecond, timezone).toString(); 979 | } 980 | var argc = arguments.length 981 | , d; 982 | // So that we don't have to do the switch block below twice! 983 | while (argc > 0 && typeof arguments[argc-1] === 'undefined') { 984 | argc--; 985 | } 986 | // An optional 'timezone' argument may be passed as the final argument 987 | if (argc >= 2 && typeof arguments[argc - 1] === 'string') { 988 | timezone = arguments[argc - 1]; 989 | argc--; 990 | } 991 | // Ugly, but the native Date constructor depends on arguments.length in order 992 | // to create a Date instance in the intended fashion. 993 | switch (argc) { 994 | case 0: 995 | d = new _Date(); break; 996 | case 1: 997 | d = new _Date(year); break; 998 | case 2: 999 | d = new _Date(year, month); break; 1000 | case 3: 1001 | d = new _Date(year, month, day); break; 1002 | case 4: 1003 | d = new _Date(year, month, day, hour); break; 1004 | case 5: 1005 | d = new _Date(year, month, day, hour, minute); break; 1006 | case 6: 1007 | d = new _Date(year, month, day, hour, minute, second); break; 1008 | case 7: 1009 | d = new _Date(year, month, day, hour, minute, second, millisecond); break; 1010 | } 1011 | if (timezone) { 1012 | // set time given timezone relative to the currently set local time 1013 | // (changing the internal "time" milliseconds value unless ms specified) 1014 | d.setTimezone(timezone, !(argc == 1 && typeof year === 'number')); 1015 | } else { 1016 | d.setTimezone(exports.currentTimezone); 1017 | } 1018 | return d; 1019 | } 1020 | Date.prototype = _Date.prototype; 1021 | exports.Date = Date; 1022 | 1023 | 1024 | // We also overwrite `Date.parse()`. It can accept an optional 'timezone' 1025 | // second argument. 1026 | function parse (dateStr, timezone) { 1027 | return new Date(dateStr, timezone).getTime(); 1028 | } 1029 | exports.parse = parse; 1030 | 1031 | // 'now()', 'parse()', and 'UTC()' all need to be re-defined on Date as don't enum 1032 | Object.defineProperty(Date, 'now', { value: _Date.now, writable: true, enumerable: false }); 1033 | Object.defineProperty(Date, 'parse', { value: parse, writable: true, enumerable: false }); 1034 | Object.defineProperty(Date, 'UTC', { value: _Date.UTC, writable: true, enumerable: false }); 1035 | 1036 | 1037 | 1038 | // Turns a "regular" Date instance into one of our "extended" Date instances. 1039 | // The return value is negligible, as the original Date instance is modified. 1040 | // DEPRECATED: Just extend the Date's prototype using the Date-extend function. 1041 | exports.extend = function extend (date) { 1042 | if (!date) return date; 1043 | date.getTimezone = getTimezone; 1044 | date.setTimezone = setTimezone; 1045 | date.getTimezoneAbbr = getTimezoneAbbr; 1046 | return date; 1047 | } 1048 | 1049 | 1050 | /** 1051 | * Pads a number with 0s if required. 1052 | */ 1053 | 1054 | function pad (num, padLen) { 1055 | var padding = '0000'; 1056 | num = String(num); 1057 | return padding.substring(0, padLen - num.length) + num; 1058 | } 1059 | 1060 | }).call(this,require('_process')) 1061 | },{"_process":7,"bindings":3,"debug":4,"fs":5,"path":6,"vm":8}],3:[function(require,module,exports){ 1062 | (function (process,__filename){ 1063 | 1064 | /** 1065 | * Module dependencies. 1066 | */ 1067 | 1068 | var fs = require('fs') 1069 | , path = require('path') 1070 | , join = path.join 1071 | , dirname = path.dirname 1072 | , exists = fs.existsSync || path.existsSync 1073 | , defaults = { 1074 | arrow: process.env.NODE_BINDINGS_ARROW || ' → ' 1075 | , compiled: process.env.NODE_BINDINGS_COMPILED_DIR || 'compiled' 1076 | , platform: process.platform 1077 | , arch: process.arch 1078 | , version: process.versions.node 1079 | , bindings: 'bindings.node' 1080 | , try: [ 1081 | // node-gyp's linked version in the "build" dir 1082 | [ 'module_root', 'build', 'bindings' ] 1083 | // node-waf and gyp_addon (a.k.a node-gyp) 1084 | , [ 'module_root', 'build', 'Debug', 'bindings' ] 1085 | , [ 'module_root', 'build', 'Release', 'bindings' ] 1086 | // Debug files, for development (legacy behavior, remove for node v0.9) 1087 | , [ 'module_root', 'out', 'Debug', 'bindings' ] 1088 | , [ 'module_root', 'Debug', 'bindings' ] 1089 | // Release files, but manually compiled (legacy behavior, remove for node v0.9) 1090 | , [ 'module_root', 'out', 'Release', 'bindings' ] 1091 | , [ 'module_root', 'Release', 'bindings' ] 1092 | // Legacy from node-waf, node <= 0.4.x 1093 | , [ 'module_root', 'build', 'default', 'bindings' ] 1094 | // Production "Release" buildtype binary (meh...) 1095 | , [ 'module_root', 'compiled', 'version', 'platform', 'arch', 'bindings' ] 1096 | ] 1097 | } 1098 | 1099 | /** 1100 | * The main `bindings()` function loads the compiled bindings for a given module. 1101 | * It uses V8's Error API to determine the parent filename that this function is 1102 | * being invoked from, which is then used to find the root directory. 1103 | */ 1104 | 1105 | function bindings (opts) { 1106 | 1107 | // Argument surgery 1108 | if (typeof opts == 'string') { 1109 | opts = { bindings: opts } 1110 | } else if (!opts) { 1111 | opts = {} 1112 | } 1113 | opts.__proto__ = defaults 1114 | 1115 | // Get the module root 1116 | if (!opts.module_root) { 1117 | opts.module_root = exports.getRoot(exports.getFileName()) 1118 | } 1119 | 1120 | // Ensure the given bindings name ends with .node 1121 | if (path.extname(opts.bindings) != '.node') { 1122 | opts.bindings += '.node' 1123 | } 1124 | 1125 | var tries = [] 1126 | , i = 0 1127 | , l = opts.try.length 1128 | , n 1129 | , b 1130 | , err 1131 | 1132 | for (; i= hour) return (ms / hour).toFixed(1) + 'h'; 1328 | if (ms >= min) return (ms / min).toFixed(1) + 'm'; 1329 | if (ms >= sec) return (ms / sec | 0) + 's'; 1330 | return ms + 'ms'; 1331 | }; 1332 | 1333 | /** 1334 | * Returns true if the given mode name is enabled, false otherwise. 1335 | * 1336 | * @param {String} name 1337 | * @return {Boolean} 1338 | * @api public 1339 | */ 1340 | 1341 | debug.enabled = function(name) { 1342 | for (var i = 0, len = debug.skips.length; i < len; i++) { 1343 | if (debug.skips[i].test(name)) { 1344 | return false; 1345 | } 1346 | } 1347 | for (var i = 0, len = debug.names.length; i < len; i++) { 1348 | if (debug.names[i].test(name)) { 1349 | return true; 1350 | } 1351 | } 1352 | return false; 1353 | }; 1354 | 1355 | /** 1356 | * Coerce `val`. 1357 | */ 1358 | 1359 | function coerce(val) { 1360 | if (val instanceof Error) return val.stack || val.message; 1361 | return val; 1362 | } 1363 | 1364 | // persist 1365 | 1366 | try { 1367 | if (window.localStorage) debug.enable(localStorage.debug); 1368 | } catch(e){} 1369 | 1370 | },{}],5:[function(require,module,exports){ 1371 | 1372 | },{}],6:[function(require,module,exports){ 1373 | (function (process){ 1374 | // Copyright Joyent, Inc. and other Node contributors. 1375 | // 1376 | // Permission is hereby granted, free of charge, to any person obtaining a 1377 | // copy of this software and associated documentation files (the 1378 | // "Software"), to deal in the Software without restriction, including 1379 | // without limitation the rights to use, copy, modify, merge, publish, 1380 | // distribute, sublicense, and/or sell copies of the Software, and to permit 1381 | // persons to whom the Software is furnished to do so, subject to the 1382 | // following conditions: 1383 | // 1384 | // The above copyright notice and this permission notice shall be included 1385 | // in all copies or substantial portions of the Software. 1386 | // 1387 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 1388 | // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 1389 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN 1390 | // NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 1391 | // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 1392 | // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 1393 | // USE OR OTHER DEALINGS IN THE SOFTWARE. 1394 | 1395 | // resolves . and .. elements in a path array with directory names there 1396 | // must be no slashes, empty elements, or device names (c:\) in the array 1397 | // (so also no leading and trailing slashes - it does not distinguish 1398 | // relative and absolute paths) 1399 | function normalizeArray(parts, allowAboveRoot) { 1400 | // if the path tries to go above the root, `up` ends up > 0 1401 | var up = 0; 1402 | for (var i = parts.length - 1; i >= 0; i--) { 1403 | var last = parts[i]; 1404 | if (last === '.') { 1405 | parts.splice(i, 1); 1406 | } else if (last === '..') { 1407 | parts.splice(i, 1); 1408 | up++; 1409 | } else if (up) { 1410 | parts.splice(i, 1); 1411 | up--; 1412 | } 1413 | } 1414 | 1415 | // if the path is allowed to go above the root, restore leading ..s 1416 | if (allowAboveRoot) { 1417 | for (; up--; up) { 1418 | parts.unshift('..'); 1419 | } 1420 | } 1421 | 1422 | return parts; 1423 | } 1424 | 1425 | // Split a filename into [root, dir, basename, ext], unix version 1426 | // 'root' is just a slash, or nothing. 1427 | var splitPathRe = 1428 | /^(\/?|)([\s\S]*?)((?:\.{1,2}|[^\/]+?|)(\.[^.\/]*|))(?:[\/]*)$/; 1429 | var splitPath = function(filename) { 1430 | return splitPathRe.exec(filename).slice(1); 1431 | }; 1432 | 1433 | // path.resolve([from ...], to) 1434 | // posix version 1435 | exports.resolve = function() { 1436 | var resolvedPath = '', 1437 | resolvedAbsolute = false; 1438 | 1439 | for (var i = arguments.length - 1; i >= -1 && !resolvedAbsolute; i--) { 1440 | var path = (i >= 0) ? arguments[i] : process.cwd(); 1441 | 1442 | // Skip empty and invalid entries 1443 | if (typeof path !== 'string') { 1444 | throw new TypeError('Arguments to path.resolve must be strings'); 1445 | } else if (!path) { 1446 | continue; 1447 | } 1448 | 1449 | resolvedPath = path + '/' + resolvedPath; 1450 | resolvedAbsolute = path.charAt(0) === '/'; 1451 | } 1452 | 1453 | // At this point the path should be resolved to a full absolute path, but 1454 | // handle relative paths to be safe (might happen when process.cwd() fails) 1455 | 1456 | // Normalize the path 1457 | resolvedPath = normalizeArray(filter(resolvedPath.split('/'), function(p) { 1458 | return !!p; 1459 | }), !resolvedAbsolute).join('/'); 1460 | 1461 | return ((resolvedAbsolute ? '/' : '') + resolvedPath) || '.'; 1462 | }; 1463 | 1464 | // path.normalize(path) 1465 | // posix version 1466 | exports.normalize = function(path) { 1467 | var isAbsolute = exports.isAbsolute(path), 1468 | trailingSlash = substr(path, -1) === '/'; 1469 | 1470 | // Normalize the path 1471 | path = normalizeArray(filter(path.split('/'), function(p) { 1472 | return !!p; 1473 | }), !isAbsolute).join('/'); 1474 | 1475 | if (!path && !isAbsolute) { 1476 | path = '.'; 1477 | } 1478 | if (path && trailingSlash) { 1479 | path += '/'; 1480 | } 1481 | 1482 | return (isAbsolute ? '/' : '') + path; 1483 | }; 1484 | 1485 | // posix version 1486 | exports.isAbsolute = function(path) { 1487 | return path.charAt(0) === '/'; 1488 | }; 1489 | 1490 | // posix version 1491 | exports.join = function() { 1492 | var paths = Array.prototype.slice.call(arguments, 0); 1493 | return exports.normalize(filter(paths, function(p, index) { 1494 | if (typeof p !== 'string') { 1495 | throw new TypeError('Arguments to path.join must be strings'); 1496 | } 1497 | return p; 1498 | }).join('/')); 1499 | }; 1500 | 1501 | 1502 | // path.relative(from, to) 1503 | // posix version 1504 | exports.relative = function(from, to) { 1505 | from = exports.resolve(from).substr(1); 1506 | to = exports.resolve(to).substr(1); 1507 | 1508 | function trim(arr) { 1509 | var start = 0; 1510 | for (; start < arr.length; start++) { 1511 | if (arr[start] !== '') break; 1512 | } 1513 | 1514 | var end = arr.length - 1; 1515 | for (; end >= 0; end--) { 1516 | if (arr[end] !== '') break; 1517 | } 1518 | 1519 | if (start > end) return []; 1520 | return arr.slice(start, end - start + 1); 1521 | } 1522 | 1523 | var fromParts = trim(from.split('/')); 1524 | var toParts = trim(to.split('/')); 1525 | 1526 | var length = Math.min(fromParts.length, toParts.length); 1527 | var samePartsLength = length; 1528 | for (var i = 0; i < length; i++) { 1529 | if (fromParts[i] !== toParts[i]) { 1530 | samePartsLength = i; 1531 | break; 1532 | } 1533 | } 1534 | 1535 | var outputParts = []; 1536 | for (var i = samePartsLength; i < fromParts.length; i++) { 1537 | outputParts.push('..'); 1538 | } 1539 | 1540 | outputParts = outputParts.concat(toParts.slice(samePartsLength)); 1541 | 1542 | return outputParts.join('/'); 1543 | }; 1544 | 1545 | exports.sep = '/'; 1546 | exports.delimiter = ':'; 1547 | 1548 | exports.dirname = function(path) { 1549 | var result = splitPath(path), 1550 | root = result[0], 1551 | dir = result[1]; 1552 | 1553 | if (!root && !dir) { 1554 | // No dirname whatsoever 1555 | return '.'; 1556 | } 1557 | 1558 | if (dir) { 1559 | // It has a dirname, strip trailing slash 1560 | dir = dir.substr(0, dir.length - 1); 1561 | } 1562 | 1563 | return root + dir; 1564 | }; 1565 | 1566 | 1567 | exports.basename = function(path, ext) { 1568 | var f = splitPath(path)[2]; 1569 | // TODO: make this comparison case-insensitive on windows? 1570 | if (ext && f.substr(-1 * ext.length) === ext) { 1571 | f = f.substr(0, f.length - ext.length); 1572 | } 1573 | return f; 1574 | }; 1575 | 1576 | 1577 | exports.extname = function(path) { 1578 | return splitPath(path)[3]; 1579 | }; 1580 | 1581 | function filter (xs, f) { 1582 | if (xs.filter) return xs.filter(f); 1583 | var res = []; 1584 | for (var i = 0; i < xs.length; i++) { 1585 | if (f(xs[i], i, xs)) res.push(xs[i]); 1586 | } 1587 | return res; 1588 | } 1589 | 1590 | // String.prototype.substr - negative index don't work in IE8 1591 | var substr = 'ab'.substr(-1) === 'b' 1592 | ? function (str, start, len) { return str.substr(start, len) } 1593 | : function (str, start, len) { 1594 | if (start < 0) start = str.length + start; 1595 | return str.substr(start, len); 1596 | } 1597 | ; 1598 | 1599 | }).call(this,require('_process')) 1600 | },{"_process":7}],7:[function(require,module,exports){ 1601 | // shim for using process in browser 1602 | 1603 | var process = module.exports = {}; 1604 | 1605 | process.nextTick = (function () { 1606 | var canSetImmediate = typeof window !== 'undefined' 1607 | && window.setImmediate; 1608 | var canMutationObserver = typeof window !== 'undefined' 1609 | && window.MutationObserver; 1610 | var canPost = typeof window !== 'undefined' 1611 | && window.postMessage && window.addEventListener 1612 | ; 1613 | 1614 | if (canSetImmediate) { 1615 | return function (f) { return window.setImmediate(f) }; 1616 | } 1617 | 1618 | var queue = []; 1619 | 1620 | if (canMutationObserver) { 1621 | var hiddenDiv = document.createElement("div"); 1622 | var observer = new MutationObserver(function () { 1623 | var queueList = queue.slice(); 1624 | queue.length = 0; 1625 | queueList.forEach(function (fn) { 1626 | fn(); 1627 | }); 1628 | }); 1629 | 1630 | observer.observe(hiddenDiv, { attributes: true }); 1631 | 1632 | return function nextTick(fn) { 1633 | if (!queue.length) { 1634 | hiddenDiv.setAttribute('yes', 'no'); 1635 | } 1636 | queue.push(fn); 1637 | }; 1638 | } 1639 | 1640 | if (canPost) { 1641 | window.addEventListener('message', function (ev) { 1642 | var source = ev.source; 1643 | if ((source === window || source === null) && ev.data === 'process-tick') { 1644 | ev.stopPropagation(); 1645 | if (queue.length > 0) { 1646 | var fn = queue.shift(); 1647 | fn(); 1648 | } 1649 | } 1650 | }, true); 1651 | 1652 | return function nextTick(fn) { 1653 | queue.push(fn); 1654 | window.postMessage('process-tick', '*'); 1655 | }; 1656 | } 1657 | 1658 | return function nextTick(fn) { 1659 | setTimeout(fn, 0); 1660 | }; 1661 | })(); 1662 | 1663 | process.title = 'browser'; 1664 | process.browser = true; 1665 | process.env = {}; 1666 | process.argv = []; 1667 | 1668 | function noop() {} 1669 | 1670 | process.on = noop; 1671 | process.addListener = noop; 1672 | process.once = noop; 1673 | process.off = noop; 1674 | process.removeListener = noop; 1675 | process.removeAllListeners = noop; 1676 | process.emit = noop; 1677 | 1678 | process.binding = function (name) { 1679 | throw new Error('process.binding is not supported'); 1680 | }; 1681 | 1682 | // TODO(shtylman) 1683 | process.cwd = function () { return '/' }; 1684 | process.chdir = function (dir) { 1685 | throw new Error('process.chdir is not supported'); 1686 | }; 1687 | 1688 | },{}],8:[function(require,module,exports){ 1689 | var indexOf = require('indexof'); 1690 | 1691 | var Object_keys = function (obj) { 1692 | if (Object.keys) return Object.keys(obj) 1693 | else { 1694 | var res = []; 1695 | for (var key in obj) res.push(key) 1696 | return res; 1697 | } 1698 | }; 1699 | 1700 | var forEach = function (xs, fn) { 1701 | if (xs.forEach) return xs.forEach(fn) 1702 | else for (var i = 0; i < xs.length; i++) { 1703 | fn(xs[i], i, xs); 1704 | } 1705 | }; 1706 | 1707 | var defineProp = (function() { 1708 | try { 1709 | Object.defineProperty({}, '_', {}); 1710 | return function(obj, name, value) { 1711 | Object.defineProperty(obj, name, { 1712 | writable: true, 1713 | enumerable: false, 1714 | configurable: true, 1715 | value: value 1716 | }) 1717 | }; 1718 | } catch(e) { 1719 | return function(obj, name, value) { 1720 | obj[name] = value; 1721 | }; 1722 | } 1723 | }()); 1724 | 1725 | var globals = ['Array', 'Boolean', 'Date', 'Error', 'EvalError', 'Function', 1726 | 'Infinity', 'JSON', 'Math', 'NaN', 'Number', 'Object', 'RangeError', 1727 | 'ReferenceError', 'RegExp', 'String', 'SyntaxError', 'TypeError', 'URIError', 1728 | 'decodeURI', 'decodeURIComponent', 'encodeURI', 'encodeURIComponent', 'escape', 1729 | 'eval', 'isFinite', 'isNaN', 'parseFloat', 'parseInt', 'undefined', 'unescape']; 1730 | 1731 | function Context() {} 1732 | Context.prototype = {}; 1733 | 1734 | var Script = exports.Script = function NodeScript (code) { 1735 | if (!(this instanceof Script)) return new Script(code); 1736 | this.code = code; 1737 | }; 1738 | 1739 | Script.prototype.runInContext = function (context) { 1740 | if (!(context instanceof Context)) { 1741 | throw new TypeError("needs a 'context' argument."); 1742 | } 1743 | 1744 | var iframe = document.createElement('iframe'); 1745 | if (!iframe.style) iframe.style = {}; 1746 | iframe.style.display = 'none'; 1747 | 1748 | document.body.appendChild(iframe); 1749 | 1750 | var win = iframe.contentWindow; 1751 | var wEval = win.eval, wExecScript = win.execScript; 1752 | 1753 | if (!wEval && wExecScript) { 1754 | // win.eval() magically appears when this is called in IE: 1755 | wExecScript.call(win, 'null'); 1756 | wEval = win.eval; 1757 | } 1758 | 1759 | forEach(Object_keys(context), function (key) { 1760 | win[key] = context[key]; 1761 | }); 1762 | forEach(globals, function (key) { 1763 | if (context[key]) { 1764 | win[key] = context[key]; 1765 | } 1766 | }); 1767 | 1768 | var winKeys = Object_keys(win); 1769 | 1770 | var res = wEval.call(win, this.code); 1771 | 1772 | forEach(Object_keys(win), function (key) { 1773 | // Avoid copying circular objects like `top` and `window` by only 1774 | // updating existing context properties or new properties in the `win` 1775 | // that was only introduced after the eval. 1776 | if (key in context || indexOf(winKeys, key) === -1) { 1777 | context[key] = win[key]; 1778 | } 1779 | }); 1780 | 1781 | forEach(globals, function (key) { 1782 | if (!(key in context)) { 1783 | defineProp(context, key, win[key]); 1784 | } 1785 | }); 1786 | 1787 | document.body.removeChild(iframe); 1788 | 1789 | return res; 1790 | }; 1791 | 1792 | Script.prototype.runInThisContext = function () { 1793 | return eval(this.code); // maybe... 1794 | }; 1795 | 1796 | Script.prototype.runInNewContext = function (context) { 1797 | var ctx = Script.createContext(context); 1798 | var res = this.runInContext(ctx); 1799 | 1800 | forEach(Object_keys(ctx), function (key) { 1801 | context[key] = ctx[key]; 1802 | }); 1803 | 1804 | return res; 1805 | }; 1806 | 1807 | forEach(Object_keys(Script.prototype), function (name) { 1808 | exports[name] = Script[name] = function (code) { 1809 | var s = Script(code); 1810 | return s[name].apply(s, [].slice.call(arguments, 1)); 1811 | }; 1812 | }); 1813 | 1814 | exports.createScript = function (code) { 1815 | return exports.Script(code); 1816 | }; 1817 | 1818 | exports.createContext = Script.createContext = function (context) { 1819 | var copy = new Context(); 1820 | if(typeof context === 'object') { 1821 | forEach(Object_keys(context), function (key) { 1822 | copy[key] = context[key]; 1823 | }); 1824 | } 1825 | return copy; 1826 | }; 1827 | 1828 | },{"indexof":9}],9:[function(require,module,exports){ 1829 | 1830 | var indexOf = [].indexOf; 1831 | 1832 | module.exports = function(arr, obj){ 1833 | if (indexOf) return arr.indexOf(obj); 1834 | for (var i = 0; i < arr.length; ++i) { 1835 | if (arr[i] === obj) return i; 1836 | } 1837 | return -1; 1838 | }; 1839 | },{}]},{},[1])(1) 1840 | }); -------------------------------------------------------------------------------- /js/dateFormat.js: -------------------------------------------------------------------------------- 1 | !function(e){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=e();else if("function"==typeof define&&define.amd)define([],e);else{var f;"undefined"!=typeof window?f=window:"undefined"!=typeof global?f=global:"undefined"!=typeof self&&(f=self),f.dateformat=e()}}(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o 5 | * MIT license 6 | * 7 | * Includes enhancements by Scott Trenda 8 | * and Kris Kowal 9 | * 10 | * Accepts a date, a mask, or a date and a mask. 11 | * Returns a formatted version of the given date. 12 | * The date defaults to the current date/time. 13 | * The mask defaults to dateFormat.masks.default. 14 | */ 15 | 16 | var dateFormat = function () { 17 | var token = /d{1,4}|m{1,4}|yy(?:yy)?|([HhMsTt])\1?|[LloSZWN]|"[^"]*"|'[^']*'/g, 18 | timezone = /\b(?:[PMCEA][SDP]T|(?:Pacific|Mountain|Central|Eastern|Atlantic) (?:Standard|Daylight|Prevailing) Time|(?:GMT|UTC)(?:[-+]\d{4})?)\b/g, 19 | timezoneClip = /[^-+\dA-Z]/g, 20 | pad = function (val, len) { 21 | val = String(val); 22 | len = len || 2; 23 | while (val.length < len) val = "0" + val; 24 | return val; 25 | }, 26 | /** 27 | * Get the ISO 8601 week number 28 | * Based on comments from 29 | * http://techblog.procurios.nl/k/n618/news/view/33796/14863/Calculate-ISO-8601-week-and-year-in-javascript.html 30 | */ 31 | getWeek = function (date) { 32 | // Remove time components of date 33 | var targetThursday = new Date(date.getFullYear(), date.getMonth(), date.getDate()); 34 | 35 | // Change date to Thursday same week 36 | targetThursday.setDate(targetThursday.getDate() - ((targetThursday.getDay() + 6) % 7) + 3); 37 | 38 | // Take January 4th as it is always in week 1 (see ISO 8601) 39 | var firstThursday = new Date(targetThursday.getFullYear(), 0, 4); 40 | 41 | // Change date to Thursday same week 42 | firstThursday.setDate(firstThursday.getDate() - ((firstThursday.getDay() + 6) % 7) + 3); 43 | 44 | // Check if daylight-saving-time-switch occured and correct for it 45 | var ds = targetThursday.getTimezoneOffset() - firstThursday.getTimezoneOffset(); 46 | targetThursday.setHours(targetThursday.getHours() - ds); 47 | 48 | // Number of weeks between target Thursday and first Thursday 49 | var weekDiff = (targetThursday - firstThursday) / (86400000*7); 50 | return 1 + Math.floor(weekDiff); 51 | }, 52 | 53 | /** 54 | * Get ISO-8601 numeric representation of the day of the week 55 | * 1 (for Monday) through 7 (for Sunday) 56 | */ 57 | 58 | getDayOfWeek = function(date){ 59 | var dow = date.getDay(); 60 | if(dow === 0) dow = 7; 61 | return dow; 62 | }; 63 | 64 | // Regexes and supporting functions are cached through closure 65 | return function (date, mask, utc, gmt) { 66 | var dF = dateFormat; 67 | 68 | // You can't provide utc if you skip other args (use the "UTC:" mask prefix) 69 | if (arguments.length == 1 && Object.prototype.toString.call(date) == "[object String]" && !/\d/.test(date)) { 70 | mask = date; 71 | date = undefined; 72 | } 73 | 74 | date = date || new Date; 75 | 76 | if(!(date instanceof Date)) { 77 | date = new Date(date); 78 | } 79 | 80 | if (isNaN(date)) { 81 | throw TypeError("Invalid date"); 82 | } 83 | 84 | mask = String(dF.masks[mask] || mask || dF.masks["default"]); 85 | 86 | // Allow setting the utc/gmt argument via the mask 87 | var maskSlice = mask.slice(0, 4); 88 | if (maskSlice == "UTC:" || maskSlice == "GMT:") { 89 | mask = mask.slice(4); 90 | utc = true; 91 | if (maskSlice == "GMT:") { 92 | gmt = true; 93 | } 94 | } 95 | 96 | var _ = utc ? "getUTC" : "get", 97 | d = date[_ + "Date"](), 98 | D = date[_ + "Day"](), 99 | m = date[_ + "Month"](), 100 | y = date[_ + "FullYear"](), 101 | H = date[_ + "Hours"](), 102 | M = date[_ + "Minutes"](), 103 | s = date[_ + "Seconds"](), 104 | L = date[_ + "Milliseconds"](), 105 | o = utc ? 0 : date.getTimezoneOffset(), 106 | W = getWeek(date), 107 | N = getDayOfWeek(date), 108 | flags = { 109 | d: d, 110 | dd: pad(d), 111 | ddd: dF.i18n.dayNames[D], 112 | dddd: dF.i18n.dayNames[D + 7], 113 | m: m + 1, 114 | mm: pad(m + 1), 115 | mmm: dF.i18n.monthNames[m], 116 | mmmm: dF.i18n.monthNames[m + 12], 117 | yy: String(y).slice(2), 118 | yyyy: y, 119 | h: H % 12 || 12, 120 | hh: pad(H % 12 || 12), 121 | H: H, 122 | HH: pad(H), 123 | M: M, 124 | MM: pad(M), 125 | s: s, 126 | ss: pad(s), 127 | l: pad(L, 3), 128 | L: pad(Math.round(L / 10)), 129 | t: H < 12 ? "a" : "p", 130 | tt: H < 12 ? "am" : "pm", 131 | T: H < 12 ? "A" : "P", 132 | TT: H < 12 ? "AM" : "PM", 133 | Z: gmt ? "GMT" : utc ? "UTC" : (String(date).match(timezone) || [""]).pop().replace(timezoneClip, ""), 134 | o: (o > 0 ? "-" : "+") + pad(Math.floor(Math.abs(o) / 60) * 100 + Math.abs(o) % 60, 4), 135 | S: ["th", "st", "nd", "rd"][d % 10 > 3 ? 0 : (d % 100 - d % 10 != 10) * d % 10], 136 | W: W, 137 | N: N 138 | }; 139 | 140 | return mask.replace(token, function ($0) { 141 | return $0 in flags ? flags[$0] : $0.slice(1, $0.length - 1); 142 | }); 143 | }; 144 | }(); 145 | 146 | // Some common format strings 147 | dateFormat.masks = { 148 | "default": "ddd mmm dd yyyy HH:MM:ss", 149 | shortDate: "m/d/yy", 150 | mediumDate: "mmm d, yyyy", 151 | longDate: "mmmm d, yyyy", 152 | fullDate: "dddd, mmmm d, yyyy", 153 | shortTime: "h:MM TT", 154 | mediumTime: "h:MM:ss TT", 155 | longTime: "h:MM:ss TT Z", 156 | isoDate: "yyyy-mm-dd", 157 | isoTime: "HH:MM:ss", 158 | isoDateTime: "yyyy-mm-dd'T'HH:MM:ss", 159 | isoUtcDateTime: "UTC:yyyy-mm-dd'T'HH:MM:ss'Z'", 160 | expiresHeaderFormat: "ddd, dd mmm yyyy HH:MM:ss Z" 161 | }; 162 | 163 | // Internationalization strings 164 | dateFormat.i18n = { 165 | dayNames: [ 166 | "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", 167 | "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" 168 | ], 169 | monthNames: [ 170 | "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", 171 | "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December" 172 | ] 173 | }; 174 | 175 | /* 176 | // For convenience... 177 | Date.prototype.format = function (mask, utc) { 178 | return dateFormat(this, mask, utc); 179 | }; 180 | */ 181 | 182 | if (typeof exports !== "undefined") { 183 | module.exports = dateFormat; 184 | } 185 | 186 | },{}]},{},[1])(1) 187 | }); -------------------------------------------------------------------------------- /js/jquery-ui.js: -------------------------------------------------------------------------------- 1 | /*! jQuery UI - v1.11.2 - 2014-11-13 2 | * http://jqueryui.com 3 | * Includes: core.js, widget.js, mouse.js, button.js, slider.js, tabs.js 4 | * Copyright 2014 jQuery Foundation and other contributors; Licensed MIT */ 5 | 6 | (function( factory ) { 7 | if ( typeof define === "function" && define.amd ) { 8 | 9 | // AMD. Register as an anonymous module. 10 | define([ "jquery" ], factory ); 11 | } else { 12 | 13 | // Browser globals 14 | factory( jQuery ); 15 | } 16 | }(function( $ ) { 17 | /*! 18 | * jQuery UI Core 1.11.2 19 | * http://jqueryui.com 20 | * 21 | * Copyright 2014 jQuery Foundation and other contributors 22 | * Released under the MIT license. 23 | * http://jquery.org/license 24 | * 25 | * http://api.jqueryui.com/category/ui-core/ 26 | */ 27 | 28 | 29 | // $.ui might exist from components with no dependencies, e.g., $.ui.position 30 | $.ui = $.ui || {}; 31 | 32 | $.extend( $.ui, { 33 | version: "1.11.2", 34 | 35 | keyCode: { 36 | BACKSPACE: 8, 37 | COMMA: 188, 38 | DELETE: 46, 39 | DOWN: 40, 40 | END: 35, 41 | ENTER: 13, 42 | ESCAPE: 27, 43 | HOME: 36, 44 | LEFT: 37, 45 | PAGE_DOWN: 34, 46 | PAGE_UP: 33, 47 | PERIOD: 190, 48 | RIGHT: 39, 49 | SPACE: 32, 50 | TAB: 9, 51 | UP: 38 52 | } 53 | }); 54 | 55 | // plugins 56 | $.fn.extend({ 57 | scrollParent: function( includeHidden ) { 58 | var position = this.css( "position" ), 59 | excludeStaticParent = position === "absolute", 60 | overflowRegex = includeHidden ? /(auto|scroll|hidden)/ : /(auto|scroll)/, 61 | scrollParent = this.parents().filter( function() { 62 | var parent = $( this ); 63 | if ( excludeStaticParent && parent.css( "position" ) === "static" ) { 64 | return false; 65 | } 66 | return overflowRegex.test( parent.css( "overflow" ) + parent.css( "overflow-y" ) + parent.css( "overflow-x" ) ); 67 | }).eq( 0 ); 68 | 69 | return position === "fixed" || !scrollParent.length ? $( this[ 0 ].ownerDocument || document ) : scrollParent; 70 | }, 71 | 72 | uniqueId: (function() { 73 | var uuid = 0; 74 | 75 | return function() { 76 | return this.each(function() { 77 | if ( !this.id ) { 78 | this.id = "ui-id-" + ( ++uuid ); 79 | } 80 | }); 81 | }; 82 | })(), 83 | 84 | removeUniqueId: function() { 85 | return this.each(function() { 86 | if ( /^ui-id-\d+$/.test( this.id ) ) { 87 | $( this ).removeAttr( "id" ); 88 | } 89 | }); 90 | } 91 | }); 92 | 93 | // selectors 94 | function focusable( element, isTabIndexNotNaN ) { 95 | var map, mapName, img, 96 | nodeName = element.nodeName.toLowerCase(); 97 | if ( "area" === nodeName ) { 98 | map = element.parentNode; 99 | mapName = map.name; 100 | if ( !element.href || !mapName || map.nodeName.toLowerCase() !== "map" ) { 101 | return false; 102 | } 103 | img = $( "img[usemap='#" + mapName + "']" )[ 0 ]; 104 | return !!img && visible( img ); 105 | } 106 | return ( /input|select|textarea|button|object/.test( nodeName ) ? 107 | !element.disabled : 108 | "a" === nodeName ? 109 | element.href || isTabIndexNotNaN : 110 | isTabIndexNotNaN) && 111 | // the element and all of its ancestors must be visible 112 | visible( element ); 113 | } 114 | 115 | function visible( element ) { 116 | return $.expr.filters.visible( element ) && 117 | !$( element ).parents().addBack().filter(function() { 118 | return $.css( this, "visibility" ) === "hidden"; 119 | }).length; 120 | } 121 | 122 | $.extend( $.expr[ ":" ], { 123 | data: $.expr.createPseudo ? 124 | $.expr.createPseudo(function( dataName ) { 125 | return function( elem ) { 126 | return !!$.data( elem, dataName ); 127 | }; 128 | }) : 129 | // support: jQuery <1.8 130 | function( elem, i, match ) { 131 | return !!$.data( elem, match[ 3 ] ); 132 | }, 133 | 134 | focusable: function( element ) { 135 | return focusable( element, !isNaN( $.attr( element, "tabindex" ) ) ); 136 | }, 137 | 138 | tabbable: function( element ) { 139 | var tabIndex = $.attr( element, "tabindex" ), 140 | isTabIndexNaN = isNaN( tabIndex ); 141 | return ( isTabIndexNaN || tabIndex >= 0 ) && focusable( element, !isTabIndexNaN ); 142 | } 143 | }); 144 | 145 | // support: jQuery <1.8 146 | if ( !$( "" ).outerWidth( 1 ).jquery ) { 147 | $.each( [ "Width", "Height" ], function( i, name ) { 148 | var side = name === "Width" ? [ "Left", "Right" ] : [ "Top", "Bottom" ], 149 | type = name.toLowerCase(), 150 | orig = { 151 | innerWidth: $.fn.innerWidth, 152 | innerHeight: $.fn.innerHeight, 153 | outerWidth: $.fn.outerWidth, 154 | outerHeight: $.fn.outerHeight 155 | }; 156 | 157 | function reduce( elem, size, border, margin ) { 158 | $.each( side, function() { 159 | size -= parseFloat( $.css( elem, "padding" + this ) ) || 0; 160 | if ( border ) { 161 | size -= parseFloat( $.css( elem, "border" + this + "Width" ) ) || 0; 162 | } 163 | if ( margin ) { 164 | size -= parseFloat( $.css( elem, "margin" + this ) ) || 0; 165 | } 166 | }); 167 | return size; 168 | } 169 | 170 | $.fn[ "inner" + name ] = function( size ) { 171 | if ( size === undefined ) { 172 | return orig[ "inner" + name ].call( this ); 173 | } 174 | 175 | return this.each(function() { 176 | $( this ).css( type, reduce( this, size ) + "px" ); 177 | }); 178 | }; 179 | 180 | $.fn[ "outer" + name] = function( size, margin ) { 181 | if ( typeof size !== "number" ) { 182 | return orig[ "outer" + name ].call( this, size ); 183 | } 184 | 185 | return this.each(function() { 186 | $( this).css( type, reduce( this, size, true, margin ) + "px" ); 187 | }); 188 | }; 189 | }); 190 | } 191 | 192 | // support: jQuery <1.8 193 | if ( !$.fn.addBack ) { 194 | $.fn.addBack = function( selector ) { 195 | return this.add( selector == null ? 196 | this.prevObject : this.prevObject.filter( selector ) 197 | ); 198 | }; 199 | } 200 | 201 | // support: jQuery 1.6.1, 1.6.2 (http://bugs.jquery.com/ticket/9413) 202 | if ( $( "" ).data( "a-b", "a" ).removeData( "a-b" ).data( "a-b" ) ) { 203 | $.fn.removeData = (function( removeData ) { 204 | return function( key ) { 205 | if ( arguments.length ) { 206 | return removeData.call( this, $.camelCase( key ) ); 207 | } else { 208 | return removeData.call( this ); 209 | } 210 | }; 211 | })( $.fn.removeData ); 212 | } 213 | 214 | // deprecated 215 | $.ui.ie = !!/msie [\w.]+/.exec( navigator.userAgent.toLowerCase() ); 216 | 217 | $.fn.extend({ 218 | focus: (function( orig ) { 219 | return function( delay, fn ) { 220 | return typeof delay === "number" ? 221 | this.each(function() { 222 | var elem = this; 223 | setTimeout(function() { 224 | $( elem ).focus(); 225 | if ( fn ) { 226 | fn.call( elem ); 227 | } 228 | }, delay ); 229 | }) : 230 | orig.apply( this, arguments ); 231 | }; 232 | })( $.fn.focus ), 233 | 234 | disableSelection: (function() { 235 | var eventType = "onselectstart" in document.createElement( "div" ) ? 236 | "selectstart" : 237 | "mousedown"; 238 | 239 | return function() { 240 | return this.bind( eventType + ".ui-disableSelection", function( event ) { 241 | event.preventDefault(); 242 | }); 243 | }; 244 | })(), 245 | 246 | enableSelection: function() { 247 | return this.unbind( ".ui-disableSelection" ); 248 | }, 249 | 250 | zIndex: function( zIndex ) { 251 | if ( zIndex !== undefined ) { 252 | return this.css( "zIndex", zIndex ); 253 | } 254 | 255 | if ( this.length ) { 256 | var elem = $( this[ 0 ] ), position, value; 257 | while ( elem.length && elem[ 0 ] !== document ) { 258 | // Ignore z-index if position is set to a value where z-index is ignored by the browser 259 | // This makes behavior of this function consistent across browsers 260 | // WebKit always returns auto if the element is positioned 261 | position = elem.css( "position" ); 262 | if ( position === "absolute" || position === "relative" || position === "fixed" ) { 263 | // IE returns 0 when zIndex is not specified 264 | // other browsers return a string 265 | // we ignore the case of nested elements with an explicit value of 0 266 | //
267 | value = parseInt( elem.css( "zIndex" ), 10 ); 268 | if ( !isNaN( value ) && value !== 0 ) { 269 | return value; 270 | } 271 | } 272 | elem = elem.parent(); 273 | } 274 | } 275 | 276 | return 0; 277 | } 278 | }); 279 | 280 | // $.ui.plugin is deprecated. Use $.widget() extensions instead. 281 | $.ui.plugin = { 282 | add: function( module, option, set ) { 283 | var i, 284 | proto = $.ui[ module ].prototype; 285 | for ( i in set ) { 286 | proto.plugins[ i ] = proto.plugins[ i ] || []; 287 | proto.plugins[ i ].push( [ option, set[ i ] ] ); 288 | } 289 | }, 290 | call: function( instance, name, args, allowDisconnected ) { 291 | var i, 292 | set = instance.plugins[ name ]; 293 | 294 | if ( !set ) { 295 | return; 296 | } 297 | 298 | if ( !allowDisconnected && ( !instance.element[ 0 ].parentNode || instance.element[ 0 ].parentNode.nodeType === 11 ) ) { 299 | return; 300 | } 301 | 302 | for ( i = 0; i < set.length; i++ ) { 303 | if ( instance.options[ set[ i ][ 0 ] ] ) { 304 | set[ i ][ 1 ].apply( instance.element, args ); 305 | } 306 | } 307 | } 308 | }; 309 | 310 | 311 | /*! 312 | * jQuery UI Widget 1.11.2 313 | * http://jqueryui.com 314 | * 315 | * Copyright 2014 jQuery Foundation and other contributors 316 | * Released under the MIT license. 317 | * http://jquery.org/license 318 | * 319 | * http://api.jqueryui.com/jQuery.widget/ 320 | */ 321 | 322 | 323 | var widget_uuid = 0, 324 | widget_slice = Array.prototype.slice; 325 | 326 | $.cleanData = (function( orig ) { 327 | return function( elems ) { 328 | var events, elem, i; 329 | for ( i = 0; (elem = elems[i]) != null; i++ ) { 330 | try { 331 | 332 | // Only trigger remove when necessary to save time 333 | events = $._data( elem, "events" ); 334 | if ( events && events.remove ) { 335 | $( elem ).triggerHandler( "remove" ); 336 | } 337 | 338 | // http://bugs.jquery.com/ticket/8235 339 | } catch ( e ) {} 340 | } 341 | orig( elems ); 342 | }; 343 | })( $.cleanData ); 344 | 345 | $.widget = function( name, base, prototype ) { 346 | var fullName, existingConstructor, constructor, basePrototype, 347 | // proxiedPrototype allows the provided prototype to remain unmodified 348 | // so that it can be used as a mixin for multiple widgets (#8876) 349 | proxiedPrototype = {}, 350 | namespace = name.split( "." )[ 0 ]; 351 | 352 | name = name.split( "." )[ 1 ]; 353 | fullName = namespace + "-" + name; 354 | 355 | if ( !prototype ) { 356 | prototype = base; 357 | base = $.Widget; 358 | } 359 | 360 | // create selector for plugin 361 | $.expr[ ":" ][ fullName.toLowerCase() ] = function( elem ) { 362 | return !!$.data( elem, fullName ); 363 | }; 364 | 365 | $[ namespace ] = $[ namespace ] || {}; 366 | existingConstructor = $[ namespace ][ name ]; 367 | constructor = $[ namespace ][ name ] = function( options, element ) { 368 | // allow instantiation without "new" keyword 369 | if ( !this._createWidget ) { 370 | return new constructor( options, element ); 371 | } 372 | 373 | // allow instantiation without initializing for simple inheritance 374 | // must use "new" keyword (the code above always passes args) 375 | if ( arguments.length ) { 376 | this._createWidget( options, element ); 377 | } 378 | }; 379 | // extend with the existing constructor to carry over any static properties 380 | $.extend( constructor, existingConstructor, { 381 | version: prototype.version, 382 | // copy the object used to create the prototype in case we need to 383 | // redefine the widget later 384 | _proto: $.extend( {}, prototype ), 385 | // track widgets that inherit from this widget in case this widget is 386 | // redefined after a widget inherits from it 387 | _childConstructors: [] 388 | }); 389 | 390 | basePrototype = new base(); 391 | // we need to make the options hash a property directly on the new instance 392 | // otherwise we'll modify the options hash on the prototype that we're 393 | // inheriting from 394 | basePrototype.options = $.widget.extend( {}, basePrototype.options ); 395 | $.each( prototype, function( prop, value ) { 396 | if ( !$.isFunction( value ) ) { 397 | proxiedPrototype[ prop ] = value; 398 | return; 399 | } 400 | proxiedPrototype[ prop ] = (function() { 401 | var _super = function() { 402 | return base.prototype[ prop ].apply( this, arguments ); 403 | }, 404 | _superApply = function( args ) { 405 | return base.prototype[ prop ].apply( this, args ); 406 | }; 407 | return function() { 408 | var __super = this._super, 409 | __superApply = this._superApply, 410 | returnValue; 411 | 412 | this._super = _super; 413 | this._superApply = _superApply; 414 | 415 | returnValue = value.apply( this, arguments ); 416 | 417 | this._super = __super; 418 | this._superApply = __superApply; 419 | 420 | return returnValue; 421 | }; 422 | })(); 423 | }); 424 | constructor.prototype = $.widget.extend( basePrototype, { 425 | // TODO: remove support for widgetEventPrefix 426 | // always use the name + a colon as the prefix, e.g., draggable:start 427 | // don't prefix for widgets that aren't DOM-based 428 | widgetEventPrefix: existingConstructor ? (basePrototype.widgetEventPrefix || name) : name 429 | }, proxiedPrototype, { 430 | constructor: constructor, 431 | namespace: namespace, 432 | widgetName: name, 433 | widgetFullName: fullName 434 | }); 435 | 436 | // If this widget is being redefined then we need to find all widgets that 437 | // are inheriting from it and redefine all of them so that they inherit from 438 | // the new version of this widget. We're essentially trying to replace one 439 | // level in the prototype chain. 440 | if ( existingConstructor ) { 441 | $.each( existingConstructor._childConstructors, function( i, child ) { 442 | var childPrototype = child.prototype; 443 | 444 | // redefine the child widget using the same prototype that was 445 | // originally used, but inherit from the new version of the base 446 | $.widget( childPrototype.namespace + "." + childPrototype.widgetName, constructor, child._proto ); 447 | }); 448 | // remove the list of existing child constructors from the old constructor 449 | // so the old child constructors can be garbage collected 450 | delete existingConstructor._childConstructors; 451 | } else { 452 | base._childConstructors.push( constructor ); 453 | } 454 | 455 | $.widget.bridge( name, constructor ); 456 | 457 | return constructor; 458 | }; 459 | 460 | $.widget.extend = function( target ) { 461 | var input = widget_slice.call( arguments, 1 ), 462 | inputIndex = 0, 463 | inputLength = input.length, 464 | key, 465 | value; 466 | for ( ; inputIndex < inputLength; inputIndex++ ) { 467 | for ( key in input[ inputIndex ] ) { 468 | value = input[ inputIndex ][ key ]; 469 | if ( input[ inputIndex ].hasOwnProperty( key ) && value !== undefined ) { 470 | // Clone objects 471 | if ( $.isPlainObject( value ) ) { 472 | target[ key ] = $.isPlainObject( target[ key ] ) ? 473 | $.widget.extend( {}, target[ key ], value ) : 474 | // Don't extend strings, arrays, etc. with objects 475 | $.widget.extend( {}, value ); 476 | // Copy everything else by reference 477 | } else { 478 | target[ key ] = value; 479 | } 480 | } 481 | } 482 | } 483 | return target; 484 | }; 485 | 486 | $.widget.bridge = function( name, object ) { 487 | var fullName = object.prototype.widgetFullName || name; 488 | $.fn[ name ] = function( options ) { 489 | var isMethodCall = typeof options === "string", 490 | args = widget_slice.call( arguments, 1 ), 491 | returnValue = this; 492 | 493 | // allow multiple hashes to be passed on init 494 | options = !isMethodCall && args.length ? 495 | $.widget.extend.apply( null, [ options ].concat(args) ) : 496 | options; 497 | 498 | if ( isMethodCall ) { 499 | this.each(function() { 500 | var methodValue, 501 | instance = $.data( this, fullName ); 502 | if ( options === "instance" ) { 503 | returnValue = instance; 504 | return false; 505 | } 506 | if ( !instance ) { 507 | return $.error( "cannot call methods on " + name + " prior to initialization; " + 508 | "attempted to call method '" + options + "'" ); 509 | } 510 | if ( !$.isFunction( instance[options] ) || options.charAt( 0 ) === "_" ) { 511 | return $.error( "no such method '" + options + "' for " + name + " widget instance" ); 512 | } 513 | methodValue = instance[ options ].apply( instance, args ); 514 | if ( methodValue !== instance && methodValue !== undefined ) { 515 | returnValue = methodValue && methodValue.jquery ? 516 | returnValue.pushStack( methodValue.get() ) : 517 | methodValue; 518 | return false; 519 | } 520 | }); 521 | } else { 522 | this.each(function() { 523 | var instance = $.data( this, fullName ); 524 | if ( instance ) { 525 | instance.option( options || {} ); 526 | if ( instance._init ) { 527 | instance._init(); 528 | } 529 | } else { 530 | $.data( this, fullName, new object( options, this ) ); 531 | } 532 | }); 533 | } 534 | 535 | return returnValue; 536 | }; 537 | }; 538 | 539 | $.Widget = function( /* options, element */ ) {}; 540 | $.Widget._childConstructors = []; 541 | 542 | $.Widget.prototype = { 543 | widgetName: "widget", 544 | widgetEventPrefix: "", 545 | defaultElement: "
", 546 | options: { 547 | disabled: false, 548 | 549 | // callbacks 550 | create: null 551 | }, 552 | _createWidget: function( options, element ) { 553 | element = $( element || this.defaultElement || this )[ 0 ]; 554 | this.element = $( element ); 555 | this.uuid = widget_uuid++; 556 | this.eventNamespace = "." + this.widgetName + this.uuid; 557 | 558 | this.bindings = $(); 559 | this.hoverable = $(); 560 | this.focusable = $(); 561 | 562 | if ( element !== this ) { 563 | $.data( element, this.widgetFullName, this ); 564 | this._on( true, this.element, { 565 | remove: function( event ) { 566 | if ( event.target === element ) { 567 | this.destroy(); 568 | } 569 | } 570 | }); 571 | this.document = $( element.style ? 572 | // element within the document 573 | element.ownerDocument : 574 | // element is window or document 575 | element.document || element ); 576 | this.window = $( this.document[0].defaultView || this.document[0].parentWindow ); 577 | } 578 | 579 | this.options = $.widget.extend( {}, 580 | this.options, 581 | this._getCreateOptions(), 582 | options ); 583 | 584 | this._create(); 585 | this._trigger( "create", null, this._getCreateEventData() ); 586 | this._init(); 587 | }, 588 | _getCreateOptions: $.noop, 589 | _getCreateEventData: $.noop, 590 | _create: $.noop, 591 | _init: $.noop, 592 | 593 | destroy: function() { 594 | this._destroy(); 595 | // we can probably remove the unbind calls in 2.0 596 | // all event bindings should go through this._on() 597 | this.element 598 | .unbind( this.eventNamespace ) 599 | .removeData( this.widgetFullName ) 600 | // support: jquery <1.6.3 601 | // http://bugs.jquery.com/ticket/9413 602 | .removeData( $.camelCase( this.widgetFullName ) ); 603 | this.widget() 604 | .unbind( this.eventNamespace ) 605 | .removeAttr( "aria-disabled" ) 606 | .removeClass( 607 | this.widgetFullName + "-disabled " + 608 | "ui-state-disabled" ); 609 | 610 | // clean up events and states 611 | this.bindings.unbind( this.eventNamespace ); 612 | this.hoverable.removeClass( "ui-state-hover" ); 613 | this.focusable.removeClass( "ui-state-focus" ); 614 | }, 615 | _destroy: $.noop, 616 | 617 | widget: function() { 618 | return this.element; 619 | }, 620 | 621 | option: function( key, value ) { 622 | var options = key, 623 | parts, 624 | curOption, 625 | i; 626 | 627 | if ( arguments.length === 0 ) { 628 | // don't return a reference to the internal hash 629 | return $.widget.extend( {}, this.options ); 630 | } 631 | 632 | if ( typeof key === "string" ) { 633 | // handle nested keys, e.g., "foo.bar" => { foo: { bar: ___ } } 634 | options = {}; 635 | parts = key.split( "." ); 636 | key = parts.shift(); 637 | if ( parts.length ) { 638 | curOption = options[ key ] = $.widget.extend( {}, this.options[ key ] ); 639 | for ( i = 0; i < parts.length - 1; i++ ) { 640 | curOption[ parts[ i ] ] = curOption[ parts[ i ] ] || {}; 641 | curOption = curOption[ parts[ i ] ]; 642 | } 643 | key = parts.pop(); 644 | if ( arguments.length === 1 ) { 645 | return curOption[ key ] === undefined ? null : curOption[ key ]; 646 | } 647 | curOption[ key ] = value; 648 | } else { 649 | if ( arguments.length === 1 ) { 650 | return this.options[ key ] === undefined ? null : this.options[ key ]; 651 | } 652 | options[ key ] = value; 653 | } 654 | } 655 | 656 | this._setOptions( options ); 657 | 658 | return this; 659 | }, 660 | _setOptions: function( options ) { 661 | var key; 662 | 663 | for ( key in options ) { 664 | this._setOption( key, options[ key ] ); 665 | } 666 | 667 | return this; 668 | }, 669 | _setOption: function( key, value ) { 670 | this.options[ key ] = value; 671 | 672 | if ( key === "disabled" ) { 673 | this.widget() 674 | .toggleClass( this.widgetFullName + "-disabled", !!value ); 675 | 676 | // If the widget is becoming disabled, then nothing is interactive 677 | if ( value ) { 678 | this.hoverable.removeClass( "ui-state-hover" ); 679 | this.focusable.removeClass( "ui-state-focus" ); 680 | } 681 | } 682 | 683 | return this; 684 | }, 685 | 686 | enable: function() { 687 | return this._setOptions({ disabled: false }); 688 | }, 689 | disable: function() { 690 | return this._setOptions({ disabled: true }); 691 | }, 692 | 693 | _on: function( suppressDisabledCheck, element, handlers ) { 694 | var delegateElement, 695 | instance = this; 696 | 697 | // no suppressDisabledCheck flag, shuffle arguments 698 | if ( typeof suppressDisabledCheck !== "boolean" ) { 699 | handlers = element; 700 | element = suppressDisabledCheck; 701 | suppressDisabledCheck = false; 702 | } 703 | 704 | // no element argument, shuffle and use this.element 705 | if ( !handlers ) { 706 | handlers = element; 707 | element = this.element; 708 | delegateElement = this.widget(); 709 | } else { 710 | element = delegateElement = $( element ); 711 | this.bindings = this.bindings.add( element ); 712 | } 713 | 714 | $.each( handlers, function( event, handler ) { 715 | function handlerProxy() { 716 | // allow widgets to customize the disabled handling 717 | // - disabled as an array instead of boolean 718 | // - disabled class as method for disabling individual parts 719 | if ( !suppressDisabledCheck && 720 | ( instance.options.disabled === true || 721 | $( this ).hasClass( "ui-state-disabled" ) ) ) { 722 | return; 723 | } 724 | return ( typeof handler === "string" ? instance[ handler ] : handler ) 725 | .apply( instance, arguments ); 726 | } 727 | 728 | // copy the guid so direct unbinding works 729 | if ( typeof handler !== "string" ) { 730 | handlerProxy.guid = handler.guid = 731 | handler.guid || handlerProxy.guid || $.guid++; 732 | } 733 | 734 | var match = event.match( /^([\w:-]*)\s*(.*)$/ ), 735 | eventName = match[1] + instance.eventNamespace, 736 | selector = match[2]; 737 | if ( selector ) { 738 | delegateElement.delegate( selector, eventName, handlerProxy ); 739 | } else { 740 | element.bind( eventName, handlerProxy ); 741 | } 742 | }); 743 | }, 744 | 745 | _off: function( element, eventName ) { 746 | eventName = (eventName || "").split( " " ).join( this.eventNamespace + " " ) + 747 | this.eventNamespace; 748 | element.unbind( eventName ).undelegate( eventName ); 749 | 750 | // Clear the stack to avoid memory leaks (#10056) 751 | this.bindings = $( this.bindings.not( element ).get() ); 752 | this.focusable = $( this.focusable.not( element ).get() ); 753 | this.hoverable = $( this.hoverable.not( element ).get() ); 754 | }, 755 | 756 | _delay: function( handler, delay ) { 757 | function handlerProxy() { 758 | return ( typeof handler === "string" ? instance[ handler ] : handler ) 759 | .apply( instance, arguments ); 760 | } 761 | var instance = this; 762 | return setTimeout( handlerProxy, delay || 0 ); 763 | }, 764 | 765 | _hoverable: function( element ) { 766 | this.hoverable = this.hoverable.add( element ); 767 | this._on( element, { 768 | mouseenter: function( event ) { 769 | $( event.currentTarget ).addClass( "ui-state-hover" ); 770 | }, 771 | mouseleave: function( event ) { 772 | $( event.currentTarget ).removeClass( "ui-state-hover" ); 773 | } 774 | }); 775 | }, 776 | 777 | _focusable: function( element ) { 778 | this.focusable = this.focusable.add( element ); 779 | this._on( element, { 780 | focusin: function( event ) { 781 | $( event.currentTarget ).addClass( "ui-state-focus" ); 782 | }, 783 | focusout: function( event ) { 784 | $( event.currentTarget ).removeClass( "ui-state-focus" ); 785 | } 786 | }); 787 | }, 788 | 789 | _trigger: function( type, event, data ) { 790 | var prop, orig, 791 | callback = this.options[ type ]; 792 | 793 | data = data || {}; 794 | event = $.Event( event ); 795 | event.type = ( type === this.widgetEventPrefix ? 796 | type : 797 | this.widgetEventPrefix + type ).toLowerCase(); 798 | // the original event may come from any element 799 | // so we need to reset the target on the new event 800 | event.target = this.element[ 0 ]; 801 | 802 | // copy original event properties over to the new event 803 | orig = event.originalEvent; 804 | if ( orig ) { 805 | for ( prop in orig ) { 806 | if ( !( prop in event ) ) { 807 | event[ prop ] = orig[ prop ]; 808 | } 809 | } 810 | } 811 | 812 | this.element.trigger( event, data ); 813 | return !( $.isFunction( callback ) && 814 | callback.apply( this.element[0], [ event ].concat( data ) ) === false || 815 | event.isDefaultPrevented() ); 816 | } 817 | }; 818 | 819 | $.each( { show: "fadeIn", hide: "fadeOut" }, function( method, defaultEffect ) { 820 | $.Widget.prototype[ "_" + method ] = function( element, options, callback ) { 821 | if ( typeof options === "string" ) { 822 | options = { effect: options }; 823 | } 824 | var hasOptions, 825 | effectName = !options ? 826 | method : 827 | options === true || typeof options === "number" ? 828 | defaultEffect : 829 | options.effect || defaultEffect; 830 | options = options || {}; 831 | if ( typeof options === "number" ) { 832 | options = { duration: options }; 833 | } 834 | hasOptions = !$.isEmptyObject( options ); 835 | options.complete = callback; 836 | if ( options.delay ) { 837 | element.delay( options.delay ); 838 | } 839 | if ( hasOptions && $.effects && $.effects.effect[ effectName ] ) { 840 | element[ method ]( options ); 841 | } else if ( effectName !== method && element[ effectName ] ) { 842 | element[ effectName ]( options.duration, options.easing, callback ); 843 | } else { 844 | element.queue(function( next ) { 845 | $( this )[ method ](); 846 | if ( callback ) { 847 | callback.call( element[ 0 ] ); 848 | } 849 | next(); 850 | }); 851 | } 852 | }; 853 | }); 854 | 855 | var widget = $.widget; 856 | 857 | 858 | /*! 859 | * jQuery UI Mouse 1.11.2 860 | * http://jqueryui.com 861 | * 862 | * Copyright 2014 jQuery Foundation and other contributors 863 | * Released under the MIT license. 864 | * http://jquery.org/license 865 | * 866 | * http://api.jqueryui.com/mouse/ 867 | */ 868 | 869 | 870 | var mouseHandled = false; 871 | $( document ).mouseup( function() { 872 | mouseHandled = false; 873 | }); 874 | 875 | var mouse = $.widget("ui.mouse", { 876 | version: "1.11.2", 877 | options: { 878 | cancel: "input,textarea,button,select,option", 879 | distance: 1, 880 | delay: 0 881 | }, 882 | _mouseInit: function() { 883 | var that = this; 884 | 885 | this.element 886 | .bind("mousedown." + this.widgetName, function(event) { 887 | return that._mouseDown(event); 888 | }) 889 | .bind("click." + this.widgetName, function(event) { 890 | if (true === $.data(event.target, that.widgetName + ".preventClickEvent")) { 891 | $.removeData(event.target, that.widgetName + ".preventClickEvent"); 892 | event.stopImmediatePropagation(); 893 | return false; 894 | } 895 | }); 896 | 897 | this.started = false; 898 | }, 899 | 900 | // TODO: make sure destroying one instance of mouse doesn't mess with 901 | // other instances of mouse 902 | _mouseDestroy: function() { 903 | this.element.unbind("." + this.widgetName); 904 | if ( this._mouseMoveDelegate ) { 905 | this.document 906 | .unbind("mousemove." + this.widgetName, this._mouseMoveDelegate) 907 | .unbind("mouseup." + this.widgetName, this._mouseUpDelegate); 908 | } 909 | }, 910 | 911 | _mouseDown: function(event) { 912 | // don't let more than one widget handle mouseStart 913 | if ( mouseHandled ) { 914 | return; 915 | } 916 | 917 | this._mouseMoved = false; 918 | 919 | // we may have missed mouseup (out of window) 920 | (this._mouseStarted && this._mouseUp(event)); 921 | 922 | this._mouseDownEvent = event; 923 | 924 | var that = this, 925 | btnIsLeft = (event.which === 1), 926 | // event.target.nodeName works around a bug in IE 8 with 927 | // disabled inputs (#7620) 928 | elIsCancel = (typeof this.options.cancel === "string" && event.target.nodeName ? $(event.target).closest(this.options.cancel).length : false); 929 | if (!btnIsLeft || elIsCancel || !this._mouseCapture(event)) { 930 | return true; 931 | } 932 | 933 | this.mouseDelayMet = !this.options.delay; 934 | if (!this.mouseDelayMet) { 935 | this._mouseDelayTimer = setTimeout(function() { 936 | that.mouseDelayMet = true; 937 | }, this.options.delay); 938 | } 939 | 940 | if (this._mouseDistanceMet(event) && this._mouseDelayMet(event)) { 941 | this._mouseStarted = (this._mouseStart(event) !== false); 942 | if (!this._mouseStarted) { 943 | event.preventDefault(); 944 | return true; 945 | } 946 | } 947 | 948 | // Click event may never have fired (Gecko & Opera) 949 | if (true === $.data(event.target, this.widgetName + ".preventClickEvent")) { 950 | $.removeData(event.target, this.widgetName + ".preventClickEvent"); 951 | } 952 | 953 | // these delegates are required to keep context 954 | this._mouseMoveDelegate = function(event) { 955 | return that._mouseMove(event); 956 | }; 957 | this._mouseUpDelegate = function(event) { 958 | return that._mouseUp(event); 959 | }; 960 | 961 | this.document 962 | .bind( "mousemove." + this.widgetName, this._mouseMoveDelegate ) 963 | .bind( "mouseup." + this.widgetName, this._mouseUpDelegate ); 964 | 965 | event.preventDefault(); 966 | 967 | mouseHandled = true; 968 | return true; 969 | }, 970 | 971 | _mouseMove: function(event) { 972 | // Only check for mouseups outside the document if you've moved inside the document 973 | // at least once. This prevents the firing of mouseup in the case of IE<9, which will 974 | // fire a mousemove event if content is placed under the cursor. See #7778 975 | // Support: IE <9 976 | if ( this._mouseMoved ) { 977 | // IE mouseup check - mouseup happened when mouse was out of window 978 | if ($.ui.ie && ( !document.documentMode || document.documentMode < 9 ) && !event.button) { 979 | return this._mouseUp(event); 980 | 981 | // Iframe mouseup check - mouseup occurred in another document 982 | } else if ( !event.which ) { 983 | return this._mouseUp( event ); 984 | } 985 | } 986 | 987 | if ( event.which || event.button ) { 988 | this._mouseMoved = true; 989 | } 990 | 991 | if (this._mouseStarted) { 992 | this._mouseDrag(event); 993 | return event.preventDefault(); 994 | } 995 | 996 | if (this._mouseDistanceMet(event) && this._mouseDelayMet(event)) { 997 | this._mouseStarted = 998 | (this._mouseStart(this._mouseDownEvent, event) !== false); 999 | (this._mouseStarted ? this._mouseDrag(event) : this._mouseUp(event)); 1000 | } 1001 | 1002 | return !this._mouseStarted; 1003 | }, 1004 | 1005 | _mouseUp: function(event) { 1006 | this.document 1007 | .unbind( "mousemove." + this.widgetName, this._mouseMoveDelegate ) 1008 | .unbind( "mouseup." + this.widgetName, this._mouseUpDelegate ); 1009 | 1010 | if (this._mouseStarted) { 1011 | this._mouseStarted = false; 1012 | 1013 | if (event.target === this._mouseDownEvent.target) { 1014 | $.data(event.target, this.widgetName + ".preventClickEvent", true); 1015 | } 1016 | 1017 | this._mouseStop(event); 1018 | } 1019 | 1020 | mouseHandled = false; 1021 | return false; 1022 | }, 1023 | 1024 | _mouseDistanceMet: function(event) { 1025 | return (Math.max( 1026 | Math.abs(this._mouseDownEvent.pageX - event.pageX), 1027 | Math.abs(this._mouseDownEvent.pageY - event.pageY) 1028 | ) >= this.options.distance 1029 | ); 1030 | }, 1031 | 1032 | _mouseDelayMet: function(/* event */) { 1033 | return this.mouseDelayMet; 1034 | }, 1035 | 1036 | // These are placeholder methods, to be overriden by extending plugin 1037 | _mouseStart: function(/* event */) {}, 1038 | _mouseDrag: function(/* event */) {}, 1039 | _mouseStop: function(/* event */) {}, 1040 | _mouseCapture: function(/* event */) { return true; } 1041 | }); 1042 | 1043 | 1044 | /*! 1045 | * jQuery UI Button 1.11.2 1046 | * http://jqueryui.com 1047 | * 1048 | * Copyright 2014 jQuery Foundation and other contributors 1049 | * Released under the MIT license. 1050 | * http://jquery.org/license 1051 | * 1052 | * http://api.jqueryui.com/button/ 1053 | */ 1054 | 1055 | 1056 | var lastActive, 1057 | baseClasses = "ui-button ui-widget ui-state-default ui-corner-all", 1058 | typeClasses = "ui-button-icons-only ui-button-icon-only ui-button-text-icons ui-button-text-icon-primary ui-button-text-icon-secondary ui-button-text-only", 1059 | formResetHandler = function() { 1060 | var form = $( this ); 1061 | setTimeout(function() { 1062 | form.find( ":ui-button" ).button( "refresh" ); 1063 | }, 1 ); 1064 | }, 1065 | radioGroup = function( radio ) { 1066 | var name = radio.name, 1067 | form = radio.form, 1068 | radios = $( [] ); 1069 | if ( name ) { 1070 | name = name.replace( /'/g, "\\'" ); 1071 | if ( form ) { 1072 | radios = $( form ).find( "[name='" + name + "'][type=radio]" ); 1073 | } else { 1074 | radios = $( "[name='" + name + "'][type=radio]", radio.ownerDocument ) 1075 | .filter(function() { 1076 | return !this.form; 1077 | }); 1078 | } 1079 | } 1080 | return radios; 1081 | }; 1082 | 1083 | $.widget( "ui.button", { 1084 | version: "1.11.2", 1085 | defaultElement: "