Clean up. Removed grey background from forms appearing inside accordions. Fixed border on tables innside accordions. Made action labels disappear on small and xtra small viewports. Updated javascript packages. Created a bower.json file. Installed anuglar-scheduler. Added schedule icon to job templates page. Shrunk pagination font.

This commit is contained in:
Chris Houseknecht 2014-03-07 18:31:15 -05:00
parent 129fed0c64
commit 15a08232e0
290 changed files with 68906 additions and 2679 deletions

View File

@ -203,6 +203,7 @@ function JobsEdit($scope, $rootScope, $compile, $location, $log, $routeParams, J
$scope.job_id = id;
$scope.parseType = 'yaml';
$scope.statusSearchSpin = false;
$scope.disableParseSelection = true;
function getPlaybooks(project, playbook) {
if (!Empty(project)) {

View File

@ -240,6 +240,7 @@ angular.module('JobTemplateFormDefinition', [])
addRequired: false,
editRequired: false,
readonly: true,
ngShow: "allow_callbacks",
column: 2,
required: false,
'class': 'span12',

View File

@ -27,7 +27,8 @@ angular.module('JobTemplatesListDefinition', [])
label: 'Name'
},
description: {
label: 'Description'
label: 'Description',
columnClass: 'hidden-sm hidden-xs'
}
},
@ -50,24 +51,26 @@ angular.module('JobTemplatesListDefinition', [])
edit: {
label: 'Edit',
ngClick: "editJobTemplate(job_template.id)",
icon: 'icon-edit',
awToolTip: 'Edit template',
"class": 'btn-default btn-xs',
dataPlacement: 'top'
},
submit: {
label: 'Launch',
icon: 'icon-rocket',
mode: 'all',
"class": 'btn-xs btn-success',
ngClick: 'submitJob(job_template.id)',
awToolTip: 'Start a job using this template',
dataPlacement: 'top'
},
schedule: {
label: 'Schedule',
mode: 'all',
awToolTip: 'Schedule a future job using this template',
dataPlacement: 'top'
},
"delete": {
label: 'Delete',
ngClick: "deleteJobTemplate(job_template.id, job_template.name)",
icon: 'icon-trash',
"class": 'btn-danger btn-xs',
awToolTip: 'Delete template',
dataPlacement: 'top'

View File

@ -25,7 +25,8 @@ angular.module('OrganizationListDefinition', [])
label: 'Name'
},
description: {
label: 'Description'
label: 'Description',
columnClass: 'hidden-sm hidden-xs'
}
},

View File

@ -28,14 +28,15 @@ angular.module('ProjectsListDefinition', [])
},
description: {
label: 'Description',
columnClass: 'hidden-sm hidden-xs',
excludeModal: true
excludeModal: true,
columnClass: 'hidden-sm hidden-xs'
},
scm_type: {
label: 'Type',
searchType: 'select',
searchOptions: [], // will be set by Options call to projects resource
excludeModal: true,
columnClass: 'hidden-sm hidden-xs',
nosort: true
},
status: {

View File

@ -584,6 +584,7 @@ legend {
/* Pagination */
.page-label {
font-size: 12px;
margin-top: 0;
text-align: right;
}
@ -816,23 +817,19 @@ input[type="checkbox"].checkbox-no-label {
.list-actions {
margin: 0;
}
table {
.list-wrapper {
background-color: @well;
padding: 10px;
border-radius: 4px;
border: 1px solid @well-border;
}
form {
background-color: @well;
padding: 10px;
border-radius: 4px;
border: 1px solid @well-border;
margin: 10px 0;
}
.ui-accordion-content {
padding-left: 15px;
padding-right: 15px;
}
.page-label {
margin-top: 5px;
}
}
#home-list-actions {
@ -1586,6 +1583,10 @@ tr td button i {
text-align: left;
}
.list-action-label {
display: none;
}
.site-footer {
height: 145px;
padding-top: 5px;

View File

@ -1,19 +1,18 @@
{
"name": "angular-cookies",
"version": "1.2.12",
"version": "1.2.15-build.2398+sha.4bab3d8",
"main": "./angular-cookies.js",
"dependencies": {
"angular": "1.2.12"
"angular": "1.2.15-build.2398+sha.4bab3d8"
},
"homepage": "https://github.com/angular/bower-angular-cookies",
"_release": "1.2.12",
"_release": "1.2.15-build.2398+sha.4bab3d8",
"_resolution": {
"type": "version",
"tag": "v1.2.12",
"commit": "5975d048a8b651b5db467f560c54bde3bfdbd1a2"
"tag": "v1.2.15-build.2398+sha.4bab3d8",
"commit": "8ca6ddfbd86f4bda921fa3d22e8d2e7d7e9b04d4"
},
"_source": "git://github.com/angular/bower-angular-cookies.git",
"_target": "~1.2.12",
"_originalSource": "angular-cookies",
"_direct": true
"_originalSource": "angular-cookies"
}

View File

@ -1,20 +1,19 @@
/**
* @license AngularJS v1.2.12
* @license AngularJS v1.2.15-build.2398+sha.4bab3d8
* (c) 2010-2014 Google, Inc. http://angularjs.org
* License: MIT
*/
(function(window, angular, undefined) {'use strict';
/**
* @ngdoc overview
* @ngdoc module
* @name ngCookies
* @description
*
* # ngCookies
*
* The `ngCookies` module provides a convenient wrapper for reading and writing browser cookies.
* The `ngCookies` module provides a convenient wrapper for reading and writing browser cookies.
*
* {@installModule cookies}
*
* <div doc-module-components="ngCookies"></div>
*
@ -25,9 +24,8 @@
angular.module('ngCookies', ['ng']).
/**
* @ngdoc object
* @name ngCookies.$cookies
* @requires $browser
* @ngdoc service
* @name $cookies
*
* @description
* Provides read/write access to browser's cookies.
@ -38,8 +36,8 @@ angular.module('ngCookies', ['ng']).
* Requires the {@link ngCookies `ngCookies`} module to be installed.
*
* @example
<doc:example>
<doc:source>
<example>
<file name="index.html">
<script>
function ExampleController($cookies) {
// Retrieving a cookie
@ -48,8 +46,8 @@ angular.module('ngCookies', ['ng']).
$cookies.myFavorite = 'oatmeal';
}
</script>
</doc:source>
</doc:example>
</file>
</example>
*/
factory('$cookies', ['$rootScope', '$browser', function ($rootScope, $browser) {
var cookies = {},
@ -134,8 +132,8 @@ angular.module('ngCookies', ['ng']).
/**
* @ngdoc object
* @name ngCookies.$cookieStore
* @ngdoc service
* @name $cookieStore
* @requires $cookies
*
* @description
@ -152,8 +150,7 @@ angular.module('ngCookies', ['ng']).
return {
/**
* @ngdoc method
* @name ngCookies.$cookieStore#get
* @methodOf ngCookies.$cookieStore
* @name $cookieStore#get
*
* @description
* Returns the value of given cookie key
@ -168,8 +165,7 @@ angular.module('ngCookies', ['ng']).
/**
* @ngdoc method
* @name ngCookies.$cookieStore#put
* @methodOf ngCookies.$cookieStore
* @name $cookieStore#put
*
* @description
* Sets a value for given cookie key
@ -183,8 +179,7 @@ angular.module('ngCookies', ['ng']).
/**
* @ngdoc method
* @name ngCookies.$cookieStore#remove
* @methodOf ngCookies.$cookieStore
* @name $cookieStore#remove
*
* @description
* Remove given cookie

View File

@ -1,5 +1,5 @@
/*
AngularJS v1.2.12
AngularJS v1.2.15-build.2398+sha.4bab3d8
(c) 2010-2014 Google, Inc. http://angularjs.org
License: MIT
*/

View File

@ -2,7 +2,7 @@
"version":3,
"file":"angular-cookies.min.js",
"lineCount":7,
"mappings":"A;;;;;aAKC,SAAQ,CAACA,CAAD,CAASC,CAAT,CAAkBC,CAAlB,CAA6B,CAoBtCD,CAAAE,OAAA,CAAe,WAAf,CAA4B,CAAC,IAAD,CAA5B,CAAAC,QAAA,CA4BW,UA5BX,CA4BuB,CAAC,YAAD,CAAe,UAAf,CAA2B,QAAS,CAACC,CAAD,CAAaC,CAAb,CAAuB,CAAA,IACxEC,EAAU,EAD8D,CAExEC,EAAc,EAF0D,CAGxEC,CAHwE,CAIxEC,EAAU,CAAA,CAJ8D,CAKxEC,EAAOV,CAAAU,KALiE,CAMxEC,EAAcX,CAAAW,YAGlBN,EAAAO,UAAA,CAAmB,QAAQ,EAAG,CAC5B,IAAIC,EAAiBR,CAAAC,QAAA,EACjBE,EAAJ,EAA0BK,CAA1B,GACEL,CAGA,CAHqBK,CAGrB,CAFAH,CAAA,CAAKG,CAAL,CAAqBN,CAArB,CAEA,CADAG,CAAA,CAAKG,CAAL,CAAqBP,CAArB,CACA,CAAIG,CAAJ,EAAaL,CAAAU,OAAA,EAJf,CAF4B,CAA9B,CAAA,EAUAL,EAAA,CAAU,CAAA,CAKVL,EAAAW,OAAA,CASAC,QAAa,EAAG,CAAA,IACVC,CADU,CAEVC,CAFU,CAIVC,CAGJ,KAAKF,CAAL,GAAaV,EAAb,CACMI,CAAA,CAAYL,CAAA,CAAQW,CAAR,CAAZ,CAAJ,EACEZ,CAAAC,QAAA,CAAiBW,CAAjB,CAAuBhB,CAAvB,CAKJ,KAAIgB,CAAJ,GAAYX,EAAZ,CAEE,CADAY,CACK,CADGZ,CAAA,CAAQW,CAAR,CACH,CAAAjB,CAAAoB,SAAA,CAAiBF,CAAjB,CAAL,EAMWA,CANX,GAMqBX,CAAA,CAAYU,CAAZ,CANrB,GAOEZ,CAAAC,QAAA,CAAiBW,CAAjB,CAAuBC,CAAvB,CACA,CAAAC,CAAA,CAAU,CAAA,CARZ,EACMnB,CAAAqB,UAAA,CAAkBd,CAAA,CAAYU,CAAZ,CAAlB,CAAJ,CACEX,CAAA,CAAQW,CAAR,CADF,CACkBV,CAAA,CAAYU,CAAZ,CADlB,CAGE,OAAOX,CAAA,CAAQW,CAAR,CASb,IAAIE,CAAJ,CAIE,IAAKF,CAAL,GAFAK,EAEahB,CAFID,CAAAC,QAAA,EAEJA,CAAAA,CAAb,CACMA,CAAA,CAAQW,CAAR,CAAJ,GAAsBK,CAAA,CAAeL,CAAf,CAAtB,GAEMN,CAAA,CAAYW,CAAA,CAAeL,CAAf,CAAZ,CAAJ,CACE,OAAOX,CAAA,CAAQW,CAAR,CADT,CAGEX,CAAA,CAAQW,CAAR,CAHF,CAGkBK,CAAA,CAAeL,CAAf,CALpB,CAlCU,CAThB,CAEA;MAAOX,EA1BqE,CAA3D,CA5BvB,CAAAH,QAAA,CA4HW,cA5HX,CA4H2B,CAAC,UAAD,CAAa,QAAQ,CAACoB,CAAD,CAAW,CAErD,MAAO,KAYAC,QAAQ,CAACC,CAAD,CAAM,CAEjB,MAAO,CADHP,CACG,CADKK,CAAA,CAASE,CAAT,CACL,EAAQzB,CAAA0B,SAAA,CAAiBR,CAAjB,CAAR,CAAkCA,CAFxB,CAZd,KA4BAS,QAAQ,CAACF,CAAD,CAAMP,CAAN,CAAa,CACxBK,CAAA,CAASE,CAAT,CAAA,CAAgBzB,CAAA4B,OAAA,CAAeV,CAAf,CADQ,CA5BrB,QA0CGW,QAAQ,CAACJ,CAAD,CAAM,CACpB,OAAOF,CAAA,CAASE,CAAT,CADa,CA1CjB,CAF8C,CAAhC,CA5H3B,CApBsC,CAArC,CAAA,CAoME1B,MApMF,CAoMUA,MAAAC,QApMV;",
"mappings":"A;;;;;aAKC,SAAQ,CAACA,CAAD,CAASC,CAAT,CAAkBC,CAAlB,CAA6B,CAmBtCD,CAAAE,OAAA,CAAe,WAAf,CAA4B,CAAC,IAAD,CAA5B,CAAAC,QAAA,CA2BW,UA3BX,CA2BuB,CAAC,YAAD,CAAe,UAAf,CAA2B,QAAS,CAACC,CAAD,CAAaC,CAAb,CAAuB,CAAA,IACxEC,EAAU,EAD8D,CAExEC,EAAc,EAF0D,CAGxEC,CAHwE,CAIxEC,EAAU,CAAA,CAJ8D,CAKxEC,EAAOV,CAAAU,KALiE,CAMxEC,EAAcX,CAAAW,YAGlBN,EAAAO,UAAA,CAAmB,QAAQ,EAAG,CAC5B,IAAIC,EAAiBR,CAAAC,QAAA,EACjBE,EAAJ,EAA0BK,CAA1B,GACEL,CAGA,CAHqBK,CAGrB,CAFAH,CAAA,CAAKG,CAAL,CAAqBN,CAArB,CAEA,CADAG,CAAA,CAAKG,CAAL,CAAqBP,CAArB,CACA,CAAIG,CAAJ,EAAaL,CAAAU,OAAA,EAJf,CAF4B,CAA9B,CAAA,EAUAL,EAAA,CAAU,CAAA,CAKVL,EAAAW,OAAA,CASAC,QAAa,EAAG,CAAA,IACVC,CADU,CAEVC,CAFU,CAIVC,CAGJ,KAAKF,CAAL,GAAaV,EAAb,CACMI,CAAA,CAAYL,CAAA,CAAQW,CAAR,CAAZ,CAAJ,EACEZ,CAAAC,QAAA,CAAiBW,CAAjB,CAAuBhB,CAAvB,CAKJ,KAAIgB,CAAJ,GAAYX,EAAZ,CAEE,CADAY,CACK,CADGZ,CAAA,CAAQW,CAAR,CACH,CAAAjB,CAAAoB,SAAA,CAAiBF,CAAjB,CAAL,EAMWA,CANX,GAMqBX,CAAA,CAAYU,CAAZ,CANrB,GAOEZ,CAAAC,QAAA,CAAiBW,CAAjB,CAAuBC,CAAvB,CACA,CAAAC,CAAA,CAAU,CAAA,CARZ,EACMnB,CAAAqB,UAAA,CAAkBd,CAAA,CAAYU,CAAZ,CAAlB,CAAJ,CACEX,CAAA,CAAQW,CAAR,CADF,CACkBV,CAAA,CAAYU,CAAZ,CADlB,CAGE,OAAOX,CAAA,CAAQW,CAAR,CASb,IAAIE,CAAJ,CAIE,IAAKF,CAAL,GAFAK,EAEahB,CAFID,CAAAC,QAAA,EAEJA,CAAAA,CAAb,CACMA,CAAA,CAAQW,CAAR,CAAJ,GAAsBK,CAAA,CAAeL,CAAf,CAAtB,GAEMN,CAAA,CAAYW,CAAA,CAAeL,CAAf,CAAZ,CAAJ,CACE,OAAOX,CAAA,CAAQW,CAAR,CADT,CAGEX,CAAA,CAAQW,CAAR,CAHF,CAGkBK,CAAA,CAAeL,CAAf,CALpB,CAlCU,CAThB,CAEA;MAAOX,EA1BqE,CAA3D,CA3BvB,CAAAH,QAAA,CA2HW,cA3HX,CA2H2B,CAAC,UAAD,CAAa,QAAQ,CAACoB,CAAD,CAAW,CAErD,MAAO,KAWAC,QAAQ,CAACC,CAAD,CAAM,CAEjB,MAAO,CADHP,CACG,CADKK,CAAA,CAASE,CAAT,CACL,EAAQzB,CAAA0B,SAAA,CAAiBR,CAAjB,CAAR,CAAkCA,CAFxB,CAXd,KA0BAS,QAAQ,CAACF,CAAD,CAAMP,CAAN,CAAa,CACxBK,CAAA,CAASE,CAAT,CAAA,CAAgBzB,CAAA4B,OAAA,CAAeV,CAAf,CADQ,CA1BrB,QAuCGW,QAAQ,CAACJ,CAAD,CAAM,CACpB,OAAOF,CAAA,CAASE,CAAT,CADa,CAvCjB,CAF8C,CAAhC,CA3H3B,CAnBsC,CAArC,CAAA,CA+LE1B,MA/LF,CA+LUA,MAAAC,QA/LV;",
"sources":["angular-cookies.js"],
"names":["window","angular","undefined","module","factory","$rootScope","$browser","cookies","lastCookies","lastBrowserCookies","runEval","copy","isUndefined","addPollFn","currentCookies","$apply","$watch","push","name","value","updated","isString","isDefined","browserCookies","$cookies","get","key","fromJson","put","toJson","remove"]
}

View File

@ -1,8 +1,8 @@
{
"name": "angular-cookies",
"version": "1.2.12",
"version": "1.2.15-build.2398+sha.4bab3d8",
"main": "./angular-cookies.js",
"dependencies": {
"angular": "1.2.12"
"angular": "1.2.15-build.2398+sha.4bab3d8"
}
}

View File

@ -0,0 +1,30 @@
{
"author": "Francesco Pontillo",
"name": "angular-filters",
"description": "A collection of filters for AngularJS.",
"version": "1.1.0",
"homepage": "https://github.com/frapontillo/angular-filters",
"repository": {
"type": "git",
"url": "git://github.com/frapontillo/angular-filters.git"
},
"main": "./dist/angular-filters.min.js",
"dependencies": {
"angular": "1.2.10"
},
"devDependencies": {
"angular-mocks": "1.2.10"
},
"resolutions": {
"angular": "1.2.10"
},
"_release": "1.1.0",
"_resolution": {
"type": "version",
"tag": "1.1.0",
"commit": "ecd468b898f99786e90c4631835e83ef93a2d743"
},
"_source": "git://github.com/frapontillo/angular-filters.git",
"_target": "*",
"_originalSource": "angular-filters"
}

View File

@ -0,0 +1,3 @@
.idea
node_modules
bower_components

View File

@ -0,0 +1,26 @@
{
"node": true,
"browser": true,
"esnext": true,
"bitwise": true,
"camelcase": true,
"curly": true,
"eqeqeq": true,
"immed": true,
"indent": 2,
"latedef": true,
"newcap": true,
"noarg": true,
"quotmark": "single",
"regexp": true,
"undef": true,
"unused": true,
"strict": true,
"trailing": true,
"smarttabs": true,
"globals": {
"angular": false,
"$": false,
"jQuery": false
}
}

View File

@ -0,0 +1,12 @@
language: node_js
node_js:
- "0.10.15"
before_install:
- export DISPLAY=:99.0
- sh -e /etc/init.d/xvfb start
- npm install -g grunt-cli karma bower
- npm install
- bower install
script: "grunt"

View File

@ -0,0 +1,32 @@
CHANGELOG
=========
#### v1.1.0
- Added `property` filter
- Added `join` filter
- Updated to angular 1.2.10
#### v1.0.1
- Improved tests and stability
- Rewritten build process
#### v1.0.0
- Main module renamed to `frapontillo.ex.filters` in order to adhere with the [Angular Component Specification draft](https://github.com/PascalPrecht/angular-component-spec).
- Added `bool` filter.
- Upgraded bower information, node packages and Karma test runner.
#### v0.0.2
- Added `firstNotNull`, `lastNotNull`, `max`, `min`.
- Test set complete.
- TravisCI is working.
#### v0.0.1
- First release.
- `default` filter is the only filter at the moment.
- Unit testing with grunt, testacular and gruntacular configured.
- `defaultSpec` test written.

View File

@ -0,0 +1,109 @@
'use strict';
module.exports = function(grunt) {
require('matchdep').filterDev('grunt-*').forEach(grunt.loadNpmTasks);
// configurable paths
var yeomanConfig = {
src: 'src',
dist: 'dist',
test: 'test'
};
grunt.initConfig({
yeoman: yeomanConfig,
pkg: grunt.file.readJSON('package.json'),
dev: {
reporters: 'dots'
},
karma : {
options: {
configFile: 'karma.conf.js',
singleRun: true
},
travis: {
browsers: ['PhantomJS']
},
local: {
browsers: ['Chrome']
},
dev: {
singleRun: false
}
},
jshint: {
options: {
jshintrc: '.jshintrc'
},
src: [
'Gruntfile.js',
'<%= yeoman.src %>/**/*.js'
],
test: {
src: ['<%= yeoman.test %>/**/*.js'],
options: {
jshintrc: 'test/.jshintrc'
}
}
},
meta: {
banner: '/**\n' + ' * <%= pkg.description %>\n' +
' * @version v<%= pkg.version %> - <%= grunt.template.today("yyyy-mm-dd") %>\n' +
' * @author <%= pkg.author.name %>\n' +
' * @link <%= pkg.homepage %>\n' +
' * @license <%= _.pluck(pkg.licenses, "type").join(", ") %>\n**/\n\n'
},
clean: {
dist: {
files: [{
dot: true,
src: [
'<%= yeoman.dist %>/*',
'!<%= yeoman.dist %>/.git*'
]
}]
},
temp: {
src: ['<%= yeoman.dist %>/.temp']
}
},
ngmin: {
dist: {
expand: true,
cwd: '<%= yeoman.src %>',
src: ['**/*.js'],
dest: '<%= yeoman.dist %>/.temp'
}
},
concat: {
options: {
banner: '<%= meta.banner %>\'use strict\';\n',
process: function(src, filepath) {
return '// Source: ' + filepath + '\n' +
src.replace(/(^|\n)[ \t]*('use strict'|"use strict");?\s*/g, '$1');
}
},
build: {
src: ['common/*.js', '<%= yeoman.dist %>/.temp/**/*.js'],
dest: '<%= yeoman.dist %>/<%= pkg.name %>.js'
}
},
uglify: {
options: {
banner: '<%= meta.banner %>'
},
build: {
src: ['<%= yeoman.dist %>/<%= pkg.name %>.js'],
dest: '<%= yeoman.dist %>/<%= pkg.name %>.min.js'
}
}
});
grunt.registerTask('test', ['jshint', 'karma:unit']);
grunt.registerTask('test-travis', ['jshint', 'karma:travis']);
grunt.registerTask('build', ['clean', 'ngmin', 'concat', 'uglify', 'clean:temp']);
grunt.registerTask('travis', ['test-travis', 'build']);
grunt.registerTask('default', ['test-travis', 'build']);
};

View File

@ -0,0 +1,202 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View File

@ -0,0 +1,200 @@
angular-filters [![Build Status](https://travis-ci.org/frapontillo/angular-filters.png?branch=master)](https://travis-ci.org/frapontillo/angular-filters)
===============
A collection of useful filters for [AngularJS](http://angularjs.org/).
You can install the latest version of `angular-filters` with `bower`:
```bash
$ bower install angular-filters
```
Then, simply include `./dist/angular-filters.js` or `./dist/angular-filters.min.js` in your Web app and inject the module `frapontillo.ex.filters` in your application.
## Filters specs
The included filters are:
- [`bool:trueValue:falseValue`](#bool)
- [`default:defaultValue`](#default)
- [`firstNotNull`](#firstnotnull)
- [`lastNotNull`](#lastnotnull)
- [`max`](#max)
- [`min`](#min)
- [`property`](#property)
- [`join`](#join)
### bool
The `bool` filter allows to **specify true and false values** to show depending on the input value. The second parameter will be returned if and only if the first parameter is `true`; the third parameter will be returned if and only if the first parameter is `false`.
This filter can be used to print a specific message depending on a boolean value.
Use it as follows:
```html
<p>{{ someBoolValue | bool:'Candies!':'No candies :(' }}</p>
```
```javascript
$scope.returnValue = $filter('bool')($scope.someBoolValue, 'Candies!', 'No candies :(');
```
### default
The `default` filter allows to **specify a default fallback value** if an object is one of the following:
- `null`
- `undefined`
- empty string, `''`
Please notice that if a value equals to `0`, the default value won't be returned, as `0` is accepted.
This filter is useful when another filter return is not safe and when you want to display a fallback value.
Use it as follows:
```html
<p>{{ someValue | number:2 | default:'No value is available.' }}</p>
```
```javascript
$scope.returnValue = $filter('default')
($filter('number')($scope.someValue, 2), 'No value is available.');
```
### firstNotNull
The `firstNotNull` filter returns the **first element from an array** that is neither `null` or `undefined`. This means it returns all numbers and strings, even if empty. It returns `undefined` if all values aren't set or if the array is empty.
Use it as follows:
```html
<p>{{ myValues | firstNotNull }}</p>
```
```javascript
$scope.firstValue = $filter('firstNotNull')($scope.myValues);
```
### lastNotNull
The `lastNotNull` filter returns the **last element from an array** that is neither `null` or `undefined`. This means it returns all numbers and strings, even if empty. It returns `undefined` if all values aren't set or if the array is empty.
Use it as follows:
```html
<p>{{ myValues | lastNotNull }}</p>
```
```javascript
$scope.firstValue = $filter('lastNotNull')($scope.myValues);
```
### max
The `max` filter returns the **maximum value from an array** that is neither `null` or `undefined`. It returns `undefined` if all values aren't set or if the array is empty.
Use it as follows:
```html
<p>{{ myValues | max }}</p>
```
```javascript
$scope.maxValue = $filter('max')($scope.myValues);
```
### min
The `min` filter returns the **minimum value from an array** that is neither `null` or `undefined`. It returns `undefined` if all values aren't set or if the array is empty.
Use it as follows:
```html
<p>{{ myValues | min }}</p>
```
```javascript
$scope.minValue = $filter('min')($scope.myValues);
```
### property
The `property` filter returns an **array with only the specified property from the original objects**, not altering the `null` or `undefined` values.
Use it as follows:
```html
<p>{{ myObjects | property:'myText' }}</p>
```
```javascript
$scope.allTheTexts = $filter('property')($scope.myObjects, 'myText');
```
### join
The `join` filter returns **the original array as a string, with its elements joined with the specified separator**, if any, otherwise defaulting to the comma `,`.
Use it as follows:
```html
<p>{{ myValues | join:', ' }}</p>
```
```javascript
$scope.joinedValues = $filter('join')($scope.myValues, ', ');
```
## Development
### Test and build
To test and build the distribution files yourself, do the following:
```shell
npm install -g grunt-cli karma bower
npm install
bower install
grunt
```
These are the available grunt task:
* `karma:travis`, run karma tests once, on PhantomJS
* `karma:local`, run karma tests once, on Chrome
* `karma:dev`, run karma tests indefinitely after every file change, on Chrome
* `jshint:src`, run jshint on every source file
* `jshint:test`, run jshint on every test file
* `clean:dist`, clean the distribution directory
* `clean:temp`, clean the temporary directory
* `ngmin`, prepares every angular file into the `dist/.temp` directory
* `concat`, concatenates the module declaration and the `ngmin`-ified file from the `dist/.temp` into the `dist` directory, adding the banner
* `uglify`, minifies the output file in the `dist` directory, adding the banner
* `build`, builds the regular and minified file
* `test-travis`, runs `jshint` and `karma:travis`
Use the default task by calling `grunt` to run tests on PhantomJS and builds the regular and minified file.
### Contribute
To contribute, please follow the generic [AngularJS Contributing Guidelines](https://github.com/angular/angular.js/blob/master/CONTRIBUTING.md), with the only exception to send the PR to the `develop` branch instead of `master`.
## License
```
Copyright 2014 Francesco Pontillo
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
```

View File

@ -0,0 +1,21 @@
{
"author": "Francesco Pontillo",
"name": "angular-filters",
"description": "A collection of filters for AngularJS.",
"version": "1.1.0",
"homepage": "https://github.com/frapontillo/angular-filters",
"repository": {
"type": "git",
"url": "git://github.com/frapontillo/angular-filters.git"
},
"main": "./dist/angular-filters.min.js",
"dependencies": {
"angular": "1.2.10"
},
"devDependencies": {
"angular-mocks": "1.2.10"
},
"resolutions": {
"angular": "1.2.10"
}
}

View File

@ -0,0 +1,2 @@
angular.module('frapontillo.ex.filters', []);
angular.module('frapontillo', ['ex.filters']);

View File

@ -0,0 +1,101 @@
/**
* A collection of filters for AngularJS.
* @version v1.1.0 - 2014-01-30
* @author Francesco Pontillo
* @link https://github.com/frapontillo/angular-filters
* @license Apache License 2.0
**/
'use strict';
// Source: common/module.js
angular.module('frapontillo.ex.filters', []);
angular.module('frapontillo', ['ex.filters']);
// Source: dist/.temp/filters/bool/bool.js
angular.module('frapontillo.ex.filters').filter('bool', function () {
return function (input, valueTrue, valueFalse) {
return input !== true ? valueFalse : valueTrue;
};
});
// Source: dist/.temp/filters/default/default.js
angular.module('frapontillo.ex.filters').filter('default', function () {
return function (input, value) {
if (input !== null && input !== undefined && (input !== '' || angular.isNumber(input))) {
return input;
}
return value || '';
};
});
// Source: dist/.temp/filters/firstNotNull/firstNotNull.js
angular.module('frapontillo.ex.filters').filter('firstNotNull', function () {
return function (input) {
if (input) {
var l = input.length - 1;
for (var i = 0; i <= l; i++) {
if (input[i] !== undefined && input[i] !== null) {
return input[i];
}
}
}
};
});
// Source: dist/.temp/filters/join/join.js
angular.module('frapontillo.ex.filters').filter('join', function () {
return function (array, separator) {
if (!array) {
return '';
}
return array.join(separator);
};
});
// Source: dist/.temp/filters/lastNotNull/lastNotNull.js
angular.module('frapontillo.ex.filters').filter('lastNotNull', function () {
return function (input) {
if (input) {
var l = input.length - 1;
for (var i = l; i >= 0; i--) {
if (input[i] !== undefined) {
return input[i];
}
}
}
};
});
// Source: dist/.temp/filters/max/max.js
angular.module('frapontillo.ex.filters').filter('max', function () {
return function (input) {
var out;
if (input) {
for (var i in input) {
if (input[i] > out || out === undefined || out === null) {
out = input[i];
}
}
}
return out;
};
});
// Source: dist/.temp/filters/min/min.js
angular.module('frapontillo.ex.filters').filter('min', function () {
return function (input) {
var out;
if (input) {
for (var i in input) {
if (input[i] < out || out === undefined || out === null) {
out = input[i];
}
}
}
return out;
};
});
// Source: dist/.temp/filters/property/property.js
angular.module('frapontillo.ex.filters').filter('property', function () {
return function (array, property) {
var newArray = [];
angular.forEach(array, function (element) {
var evalProperty = element[property];
newArray.push(evalProperty);
});
return newArray;
};
});

View File

@ -0,0 +1,9 @@
/**
* A collection of filters for AngularJS.
* @version v1.1.0 - 2014-01-30
* @author Francesco Pontillo
* @link https://github.com/frapontillo/angular-filters
* @license Apache License 2.0
**/
"use strict";angular.module("frapontillo.ex.filters",[]),angular.module("frapontillo",["ex.filters"]),angular.module("frapontillo.ex.filters").filter("bool",function(){return function(a,b,c){return a!==!0?c:b}}),angular.module("frapontillo.ex.filters").filter("default",function(){return function(a,b){return null===a||void 0===a||""===a&&!angular.isNumber(a)?b||"":a}}),angular.module("frapontillo.ex.filters").filter("firstNotNull",function(){return function(a){if(a)for(var b=a.length-1,c=0;b>=c;c++)if(void 0!==a[c]&&null!==a[c])return a[c]}}),angular.module("frapontillo.ex.filters").filter("join",function(){return function(a,b){return a?a.join(b):""}}),angular.module("frapontillo.ex.filters").filter("lastNotNull",function(){return function(a){if(a)for(var b=a.length-1,c=b;c>=0;c--)if(void 0!==a[c])return a[c]}}),angular.module("frapontillo.ex.filters").filter("max",function(){return function(a){var b;if(a)for(var c in a)(a[c]>b||void 0===b||null===b)&&(b=a[c]);return b}}),angular.module("frapontillo.ex.filters").filter("min",function(){return function(a){var b;if(a)for(var c in a)(a[c]<b||void 0===b||null===b)&&(b=a[c]);return b}}),angular.module("frapontillo.ex.filters").filter("property",function(){return function(a,b){var c=[];return angular.forEach(a,function(a){var d=a[b];c.push(d)}),c}});

View File

@ -0,0 +1,72 @@
// Karma configuration
// Generated on Tue Aug 27 2013 11:58:44 GMT+0200 (ora legale Europa occidentale)
module.exports = function(config) {
config.set({
// base path, that will be used to resolve files and exclude
basePath: '',
// frameworks to use
frameworks: ['jasmine'],
// list of files / patterns to load in the browser
files: [
'bower_components/angular/angular.js',
'bower_components/angular-mocks/angular-mocks.js',
'common/module.js',
'src/**/*.js',
'test/*/*Spec.js'
],
// list of files to exclude
exclude: [
],
// test results reporter to use
// possible values: 'dots', 'progress', 'junit', 'growl', 'coverage'
reporters: ['progress'],
// web server port
port: 9876,
// enable / disable colors in the output (reporters and logs)
colors: true,
// level of logging
// possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
logLevel: config.LOG_DEBUG,
// enable / disable watching file and executing tests whenever any file changes
autoWatch: true,
// Start these browsers, currently available:
// - Chrome
// - ChromeCanary
// - Firefox
// - Opera
// - Safari (only Mac)
// - PhantomJS
// - IE (only Windows)
browsers: ['Chrome'],
// If browser does not capture in given timeout [ms], kill it
captureTimeout: 60000,
// Continuous Integration mode
// if true, it capture browsers, run tests and exit
singleRun: false
});
};

View File

@ -0,0 +1,9 @@
'use strict';
angular.module('frapontillo.ex.filters')
.filter('bool', function() {
return function(input, valueTrue, valueFalse) {
return input !== true ? valueFalse : valueTrue;
};
}
);

View File

@ -0,0 +1,12 @@
'use strict';
angular.module('frapontillo.ex.filters')
.filter('default', function() {
return function(input, value) {
if (input !== null && input !== undefined && (input !== '' || angular.isNumber(input))) {
return input;
}
return value || '';
};
}
);

View File

@ -0,0 +1,16 @@
'use strict';
angular.module('frapontillo.ex.filters')
.filter('firstNotNull', function() {
return function(input) {
if (input) {
var l = input.length - 1;
for (var i = 0; i <= l; i++) {
if (input[i] !== undefined && input[i] !== null) {
return input[i];
}
}
}
};
}
);

View File

@ -0,0 +1,12 @@
'use strict';
angular.module('frapontillo.ex.filters')
.filter('join', function() {
return function(array, separator) {
if (!array) {
return '';
}
return array.join(separator);
};
}
);

View File

@ -0,0 +1,16 @@
'use strict';
angular.module('frapontillo.ex.filters')
.filter('lastNotNull', function() {
return function(input) {
if (input) {
var l = input.length - 1;
for (var i = l; i >= 0; i--) {
if (input[i] !== undefined) {
return input[i];
}
}
}
};
}
);

View File

@ -0,0 +1,17 @@
'use strict';
angular.module('frapontillo.ex.filters')
.filter('max', function() {
return function(input) {
var out;
if (input) {
for (var i in input) {
if (input[i] > out || out === undefined || out === null) {
out = input[i];
}
}
}
return out;
};
}
);

View File

@ -0,0 +1,17 @@
'use strict';
angular.module('frapontillo.ex.filters')
.filter('min', function() {
return function(input) {
var out;
if (input) {
for (var i in input) {
if (input[i] < out || out === undefined || out === null) {
out = input[i];
}
}
}
return out;
};
}
);

View File

@ -0,0 +1,15 @@
'use strict';
angular.module('frapontillo.ex.filters')
.filter('property', function() {
return function(array, property) {
var newArray = [];
// for each object in the array
angular.forEach(array, function(element) {
var evalProperty = element[property];
newArray.push(evalProperty);
});
return newArray;
};
}
);

View File

@ -0,0 +1,35 @@
{
"node": true,
"browser": true,
"esnext": true,
"bitwise": true,
"camelcase": true,
"curly": true,
"eqeqeq": true,
"immed": true,
"indent": 2,
"latedef": true,
"newcap": true,
"noarg": true,
"quotmark": "single",
"regexp": true,
"undef": true,
"unused": true,
"strict": true,
"trailing": true,
"smarttabs": true,
"globals": {
"after": false,
"afterEach": false,
"angular": false,
"before": false,
"beforeEach": false,
"browser": false,
"describe": false,
"expect": false,
"inject": false,
"it": false,
"spyOn": false
}
}

View File

@ -0,0 +1,34 @@
'use strict';
describe('bool', function () {
var boolFilter;
beforeEach(module('frapontillo.ex.filters'));
beforeEach(inject(function ($filter) {
boolFilter = $filter('bool');
}));
it('should match the true value', function () {
expect(boolFilter(true, 1, 0)).toEqual(1);
});
it('should match the false value', function () {
expect(boolFilter(false, 1, 0)).toEqual(0);
});
it('should match a string to the false value', function () {
expect(boolFilter('true', 1, 0)).toEqual(0);
});
it('should match the empty string to the false value', function () {
expect(boolFilter('', 1, 0)).toEqual(0);
});
it('should match undefined to the false value', function () {
expect(boolFilter(undefined, 1, 0)).toEqual(0);
});
it('should match null to the false value', function () {
expect(boolFilter(null, 1, 0)).toEqual(0);
});
});

View File

@ -0,0 +1,47 @@
'use strict';
describe('default', function () {
var defaultFilter;
var numberFilter;
beforeEach(module('frapontillo.ex.filters'));
beforeEach(inject(function ($filter) {
defaultFilter = $filter('default');
numberFilter = $filter('number');
}));
it('should return the number 1337', function () {
var inputVal = 1337;
expect(defaultFilter(inputVal)).toEqual(1337);
});
it('should return a "default" string', function () {
var inputVal;
var defaultVal = 'default';
expect(defaultFilter(inputVal, defaultVal)).toEqual('default');
});
it('should return a "default" string', function () {
var inputVal = null;
var defaultVal = 'default';
expect(defaultFilter(inputVal, defaultVal)).toEqual('default');
});
it('should return the number 0', function () {
var inputVal = 0;
var defaultVal = 'default';
expect(defaultFilter(inputVal, defaultVal)).toEqual(0);
});
it('should return the string "13.37"', function () {
var inputVal = '13.3678787';
var defaultVal = 'N.A.';
expect(defaultFilter(numberFilter(inputVal, 2), defaultVal)).toEqual('13.37');
});
it('should return a "N.A." string', function () {
var inputVal;
var defaultVal = 'N.A.';
expect(defaultFilter(numberFilter(inputVal, 2), defaultVal)).toEqual(defaultVal);
});
});

View File

@ -0,0 +1,28 @@
'use strict';
describe('firstNotNull', function () {
var firstNotNullFilter;
beforeEach(module('frapontillo.ex.filters'));
beforeEach(inject(function ($filter) {
firstNotNullFilter = $filter('firstNotNull');
}));
it('should return the number 1337', function () {
expect(firstNotNullFilter([
null, undefined, 1337, 0
])).toEqual(1337);
});
it('should return the number 0', function () {
expect(firstNotNullFilter([
null, 0, undefined, 3
])).toEqual(0);
});
it('should return undefined', function () {
expect(firstNotNullFilter([
null, undefined
])).toEqual(undefined);
});
});

View File

@ -0,0 +1,30 @@
'use strict';
describe('join', function () {
var joinFilter;
beforeEach(module('frapontillo.ex.filters'));
beforeEach(inject(function ($filter) {
joinFilter = $filter('join');
}));
it('should return \'this is a simple test\'', function () {
expect(joinFilter(['this', 'is', 'a', 'simple', 'test'], ' ')).toEqual('this is a simple test');
});
it('should return the empty string for an undefined array', function () {
expect(joinFilter(undefined, '')).toEqual('');
});
it('should return the empty string for an empty array', function () {
expect(joinFilter([], '')).toEqual('');
});
it('should return \'0123456789\'', function () {
expect(joinFilter([0,1,2,3,4,5,6,7,8,9], '')).toEqual('0123456789');
});
it('should default to the comma separator and return \'0,1,2,3,4,5,6,7,8,9\'', function () {
expect(joinFilter([0,1,2,3,4,5,6,7,8,9])).toEqual('0,1,2,3,4,5,6,7,8,9');
});
});

View File

@ -0,0 +1,28 @@
'use strict';
describe('lastNotNull', function () {
var lastNotNullFilter;
beforeEach(module('frapontillo.ex.filters'));
beforeEach(inject(function ($filter) {
lastNotNullFilter = $filter('lastNotNull');
}));
it('should return the number 0', function () {
expect(lastNotNullFilter([
null, undefined, 1337, 0
])).toEqual(0);
});
it('should return the number 1337', function () {
expect(lastNotNullFilter([
null, 0, 1337, undefined
])).toEqual(1337);
});
it('should return undefined', function () {
expect(lastNotNullFilter([
null, undefined
])).toEqual(undefined);
});
});

View File

@ -0,0 +1,28 @@
'use strict';
describe('max', function () {
var maxFilter;
beforeEach(module('frapontillo.ex.filters'));
beforeEach(inject(function ($filter) {
maxFilter = $filter('max');
}));
it('should return the number 1337', function () {
expect(maxFilter([
null, undefined, 1337, 0
])).toEqual(1337);
});
it('should return the number 1337', function () {
expect(maxFilter([
null, 0, 1337, undefined
])).toEqual(1337);
});
it('should return undefined', function () {
expect(maxFilter([
null, undefined
])).toEqual(undefined);
});
});

View File

@ -0,0 +1,28 @@
'use strict';
describe('min', function () {
var minFilter;
beforeEach(module('frapontillo.ex.filters'));
beforeEach(inject(function ($filter) {
minFilter = $filter('min');
}));
it('should return the number 0', function () {
expect(minFilter([
null, undefined, 1337, 0
])).toEqual(0);
});
it('should return the number 1', function () {
expect(minFilter([
null, 1, 1337, undefined
])).toEqual(1);
});
it('should return undefined', function () {
expect(minFilter([
null, undefined
])).toEqual(undefined);
});
});

View File

@ -0,0 +1,42 @@
'use strict';
describe('property', function () {
var propertyFilter;
var testArray = [
{id:0, text:'zero'},
{id:1, text:'one'},
{id:2, text:'two'},
{id:3, text:'three'},
{id:4, text:'four'},
{id:5, text:'five'},
{id:6, text:'six'}
];
var makeArray = function() {
return angular.copy(testArray);
};
beforeEach(module('frapontillo.ex.filters'));
beforeEach(inject(function ($filter) {
propertyFilter = $filter('property');
}));
it('should return the \'id\' properties', function () {
var filteredArray = propertyFilter(makeArray(), 'id');
expect(filteredArray[0]).toEqual(0);
expect(filteredArray.length).toEqual(7);
});
it('should return elements with only the \'text\' property', function () {
var filteredArray = propertyFilter(makeArray(), 'text');
expect(filteredArray[0]).toEqual('zero');
expect(filteredArray.length).toEqual(7);
});
it('should return empty elements', function () {
var filteredArray = propertyFilter(makeArray(), 'something else');
expect(filteredArray[0]).toEqual(undefined);
expect(filteredArray.length).toEqual(7);
});
});

View File

@ -1,19 +1,18 @@
{
"name": "angular-resource",
"version": "1.2.12",
"version": "1.2.15-build.2398+sha.4bab3d8",
"main": "./angular-resource.js",
"dependencies": {
"angular": "1.2.12"
"angular": "1.2.15-build.2398+sha.4bab3d8"
},
"homepage": "https://github.com/angular/bower-angular-resource",
"_release": "1.2.12",
"_release": "1.2.15-build.2398+sha.4bab3d8",
"_resolution": {
"type": "version",
"tag": "v1.2.12",
"commit": "4d368199bfbab887f1c1622bb47d4d0924d6a8ed"
"tag": "v1.2.15-build.2398+sha.4bab3d8",
"commit": "c20e7f87b0f788322c1c1920f1646a116389fa8d"
},
"_source": "git://github.com/angular/bower-angular-resource.git",
"_target": "~1.2.12",
"_originalSource": "angular-resource",
"_direct": true
"_originalSource": "angular-resource"
}

View File

@ -1,5 +1,5 @@
/**
* @license AngularJS v1.2.12
* @license AngularJS v1.2.15-build.2398+sha.4bab3d8
* (c) 2010-2014 Google, Inc. http://angularjs.org
* License: MIT
*/
@ -49,7 +49,7 @@ function shallowClearAndCopy(src, dst) {
}
/**
* @ngdoc overview
* @ngdoc module
* @name ngResource
* @description
*
@ -58,7 +58,6 @@ function shallowClearAndCopy(src, dst) {
* The `ngResource` module provides interaction support with RESTful services
* via the $resource service.
*
* {@installModule resource}
*
* <div doc-module-components="ngResource"></div>
*
@ -66,8 +65,8 @@ function shallowClearAndCopy(src, dst) {
*/
/**
* @ngdoc object
* @name ngResource.$resource
* @ngdoc service
* @name $resource
* @requires $http
*
* @description
@ -103,8 +102,8 @@ function shallowClearAndCopy(src, dst) {
* If the parameter value is prefixed with `@` then the value of that parameter is extracted from
* the data object (useful for non-GET operations).
*
* @param {Object.<Object>=} actions Hash with declaration of custom action that should extend the
* default set of resource actions. The declaration should be created in the format of {@link
* @param {Object.<Object>=} actions Hash with declaration of custom action that should extend
* the default set of resource actions. The declaration should be created in the format of {@link
* ng.$http#usage_parameters $http.config}:
*
* {action1: {method:?, params:?, isArray:?, headers:?, ...},
@ -139,35 +138,37 @@ function shallowClearAndCopy(src, dst) {
* - **`timeout`** `{number|Promise}` timeout in milliseconds, or {@link ng.$q promise} that
* should abort the request when resolved.
* - **`withCredentials`** - `{boolean}` - whether to set the `withCredentials` flag on the
* XHR object. See {@link https://developer.mozilla.org/en/http_access_control#section_5
* requests with credentials} for more information.
* - **`responseType`** - `{string}` - see {@link
* https://developer.mozilla.org/en-US/docs/DOM/XMLHttpRequest#responseType requestType}.
* XHR object. See
* [requests with credentials](https://developer.mozilla.org/en/http_access_control#section_5)
* for more information.
* - **`responseType`** - `{string}` - see
* [requestType](https://developer.mozilla.org/en-US/docs/DOM/XMLHttpRequest#responseType).
* - **`interceptor`** - `{Object=}` - The interceptor object has two optional methods -
* `response` and `responseError`. Both `response` and `responseError` interceptors get called
* with `http response` object. See {@link ng.$http $http interceptors}.
*
* @returns {Object} A resource "class" object with methods for the default set of resource actions
* optionally extended with custom `actions`. The default set contains these actions:
*
* { 'get': {method:'GET'},
* 'save': {method:'POST'},
* 'query': {method:'GET', isArray:true},
* 'remove': {method:'DELETE'},
* 'delete': {method:'DELETE'} };
* ```js
* { 'get': {method:'GET'},
* 'save': {method:'POST'},
* 'query': {method:'GET', isArray:true},
* 'remove': {method:'DELETE'},
* 'delete': {method:'DELETE'} };
* ```
*
* Calling these methods invoke an {@link ng.$http} with the specified http method,
* destination and parameters. When the data is returned from the server then the object is an
* instance of the resource class. The actions `save`, `remove` and `delete` are available on it
* as methods with the `$` prefix. This allows you to easily perform CRUD operations (create,
* read, update, delete) on server-side data like this:
* <pre>
var User = $resource('/user/:userId', {userId:'@id'});
var user = User.get({userId:123}, function() {
user.abc = true;
user.$save();
});
</pre>
* ```js
* var User = $resource('/user/:userId', {userId:'@id'});
* var user = User.get({userId:123}, function() {
* user.abc = true;
* user.$save();
* });
* ```
*
* It is important to realize that invoking a $resource object method immediately returns an
* empty reference (object or array depending on `isArray`). Once the data is returned from the
@ -211,7 +212,7 @@ function shallowClearAndCopy(src, dst) {
*
* # Credit card resource
*
* <pre>
* ```js
// Define CreditCard class
var CreditCard = $resource('/user/:userId/card/:cardId',
{userId:123, cardId:'@id'}, {
@ -244,7 +245,7 @@ function shallowClearAndCopy(src, dst) {
// POST: /user/123/card {number:'0123', name:'Mike Smith'}
// server returns: {id:789, number:'0123', name: 'Mike Smith'};
expect(newCard.id).toEqual(789);
* </pre>
* ```
*
* The object returned from this function execution is a resource "class" which has "static" method
* for each action in the definition.
@ -255,19 +256,19 @@ function shallowClearAndCopy(src, dst) {
* all of the non-GET methods are available with `$` prefix. This allows you to easily support CRUD
* operations (create, read, update, delete) on server-side data.
<pre>
```js
var User = $resource('/user/:userId', {userId:'@id'});
var user = User.get({userId:123}, function() {
user.abc = true;
user.$save();
});
</pre>
```
*
* It's worth noting that the success callback for `get`, `query` and other methods gets passed
* in the response that came from the server as well as $http header getter function, so one
* could rewrite the above example and get access to http headers as:
*
<pre>
```js
var User = $resource('/user/:userId', {userId:'@id'});
User.get({userId:123}, function(u, getResponseHeaders){
u.abc = true;
@ -276,15 +277,15 @@ function shallowClearAndCopy(src, dst) {
//putResponseHeaders => $http header getter
});
});
</pre>
```
* # Creating a custom 'PUT' request
* In this example we create a custom method on our resource to make a PUT request
* <pre>
* ```js
* var app = angular.module('app', ['ngResource', 'ngRoute']);
*
* // Some APIs expect a PUT request in the format URL/object/ID
* // Here we are creating an 'update' method
* // Here we are creating an 'update' method
* app.factory('Notes', ['$resource', function($resource) {
* return $resource('/notes/:id', null,
* {
@ -305,7 +306,7 @@ function shallowClearAndCopy(src, dst) {
*
* // This will PUT /notes/ID with the note object in the request payload
* }]);
* </pre>
* ```
*/
angular.module('ngResource', ['ng']).
factory('$resource', ['$http', '$q', function($http, $q) {

View File

@ -1,5 +1,5 @@
/*
AngularJS v1.2.12
AngularJS v1.2.15-build.2398+sha.4bab3d8
(c) 2010-2014 Google, Inc. http://angularjs.org
License: MIT
*/

File diff suppressed because one or more lines are too long

View File

@ -1,8 +1,8 @@
{
"name": "angular-resource",
"version": "1.2.12",
"version": "1.2.15-build.2398+sha.4bab3d8",
"main": "./angular-resource.js",
"dependencies": {
"angular": "1.2.12"
"angular": "1.2.15-build.2398+sha.4bab3d8"
}
}

View File

@ -1,16 +1,16 @@
{
"name": "angular-route",
"version": "1.2.13",
"version": "1.2.14",
"main": "./angular-route.js",
"dependencies": {
"angular": "1.2.13"
"angular": "1.2.14"
},
"homepage": "https://github.com/angular/bower-angular-route",
"_release": "1.2.13",
"_release": "1.2.14",
"_resolution": {
"type": "version",
"tag": "v1.2.13",
"commit": "fed5c1c5f17cb2f358f78371fd5b03232f398a91"
"tag": "v1.2.14",
"commit": "2a998317521fb468b8b69a4a29e6be2d19bc6a3e"
},
"_source": "git://github.com/angular/bower-angular-route.git",
"_target": "*",

View File

@ -1,12 +1,12 @@
/**
* @license AngularJS v1.2.13
* @license AngularJS v1.2.14
* (c) 2010-2014 Google, Inc. http://angularjs.org
* License: MIT
*/
(function(window, angular, undefined) {'use strict';
/**
* @ngdoc overview
* @ngdoc module
* @name ngRoute
* @description
*
@ -16,8 +16,7 @@
*
* ## Example
* See {@link ngRoute.$route#example $route} for an example of configuring and using `ngRoute`.
*
* {@installModule route}
*
*
* <div doc-module-components="ngRoute"></div>
*/
@ -26,14 +25,14 @@ var ngRouteModule = angular.module('ngRoute', ['ng']).
provider('$route', $RouteProvider);
/**
* @ngdoc object
* @name ngRoute.$routeProvider
* @ngdoc provider
* @name $routeProvider
* @function
*
* @description
*
* Used for configuring routes.
*
*
* ## Example
* See {@link ngRoute.$route#example $route} for an example of configuring and using `ngRoute`.
*
@ -49,27 +48,26 @@ function $RouteProvider(){
/**
* @ngdoc method
* @name ngRoute.$routeProvider#when
* @methodOf ngRoute.$routeProvider
* @name $routeProvider#when
*
* @param {string} path Route path (matched against `$location.path`). If `$location.path`
* contains redundant trailing slash or is missing one, the route will still match and the
* `$location.path` will be updated to add or drop the trailing slash to exactly match the
* route definition.
*
* * `path` can contain named groups starting with a colon: e.g. `:name`. All characters up
* * `path` can contain named groups starting with a colon: e.g. `:name`. All characters up
* to the next slash are matched and stored in `$routeParams` under the given `name`
* when the route matches.
* * `path` can contain named groups starting with a colon and ending with a star:
* * `path` can contain named groups starting with a colon and ending with a star:
* e.g.`:name*`. All characters are eagerly stored in `$routeParams` under the given `name`
* when the route matches.
* * `path` can contain optional named groups with a question mark: e.g.`:name?`.
* * `path` can contain optional named groups with a question mark: e.g.`:name?`.
*
* For example, routes like `/color/:color/largecode/:largecode*\/edit` will match
* `/color/brown/largecode/code/with/slashs/edit` and extract:
* `/color/brown/largecode/code/with/slashes/edit` and extract:
*
* * `color: brown`
* * `largecode: code/with/slashs`.
* * `color: brown`
* * `largecode: code/with/slashes`.
*
*
* @param {Object} route Mapping information to be assigned to `$route.current` on route
@ -89,7 +87,7 @@ function $RouteProvider(){
*
* If `template` is a function, it will be called with the following parameters:
*
* - `{Array.<Object>}` - route parameters extracted from the current
* - `{Array.&lt;Object&gt;}` - route parameters extracted from the current
* `$location.path()` by applying the current route
*
* - `templateUrl` `{string=|function()=}` path or function that returns a path to an html
@ -97,7 +95,7 @@ function $RouteProvider(){
*
* If `templateUrl` is a function, it will be called with the following parameters:
*
* - `{Array.<Object>}` - route parameters extracted from the current
* - `{Array.&lt;Object&gt;}` - route parameters extracted from the current
* `$location.path()` by applying the current route
*
* - `resolve` - `{Object.<string, function>=}` - An optional map of dependencies which should
@ -112,7 +110,7 @@ function $RouteProvider(){
*
* - `key` `{string}`: a name of a dependency to be injected into the controller.
* - `factory` - `{string|function}`: If `string` then it is an alias for a service.
* Otherwise if function, then it is {@link api/AUTO.$injector#invoke injected}
* Otherwise if function, then it is {@link auto.$injector#invoke injected}
* and the return value is treated as the dependency. If the result is a promise, it is
* resolved before its value is injected into the controller. Be aware that
* `ngRoute.$routeParams` will still refer to the previous route within these resolve
@ -212,8 +210,7 @@ function $RouteProvider(){
/**
* @ngdoc method
* @name ngRoute.$routeProvider#otherwise
* @methodOf ngRoute.$routeProvider
* @name $routeProvider#otherwise
*
* @description
* Sets route definition that will be used on route change when no other route definition
@ -239,8 +236,8 @@ function $RouteProvider(){
function($rootScope, $location, $routeParams, $q, $injector, $http, $templateCache, $sce) {
/**
* @ngdoc object
* @name ngRoute.$route
* @ngdoc service
* @name $route
* @requires $location
* @requires $routeParams
*
@ -255,7 +252,7 @@ function $RouteProvider(){
* - `$scope` - The current route scope.
* - `$template` - The current route template HTML.
*
* @property {Array.<Object>} routes Array of all configured routes.
* @property {Array.&lt;Object&gt;} routes Array of all configured routes.
*
* @description
* `$route` is used for deep-linking URLs to controllers and views (HTML partials).
@ -276,7 +273,7 @@ function $RouteProvider(){
Note that this example is using {@link ng.directive:script inlined templates}
to get it working on jsfiddle as well.
<example module="ngViewExample" deps="angular-route.js">
<example name="$route-service" module="ngRouteExample" deps="angular-route.js" fixBase="true">
<file name="index.html">
<div ng-controller="MainCntl">
Choose:
@ -309,7 +306,7 @@ function $RouteProvider(){
</file>
<file name="script.js">
angular.module('ngViewExample', ['ngRoute'])
angular.module('ngRouteExample', ['ngRoute'])
.config(function($routeProvider, $locationProvider) {
$routeProvider.when('/Book/:bookId', {
@ -350,17 +347,17 @@ function $RouteProvider(){
}
</file>
<file name="protractorTest.js">
<file name="protractor.js" type="protractor">
it('should load and compile correct template', function() {
element(by.linkText('Moby: Ch1')).click();
var content = element(by.css('.doc-example-live [ng-view]')).getText();
var content = element(by.css('[ng-view]')).getText();
expect(content).toMatch(/controller\: ChapterCntl/);
expect(content).toMatch(/Book Id\: Moby/);
expect(content).toMatch(/Chapter Id\: 1/);
element(by.partialLinkText('Scarlet')).click();
content = element(by.css('.doc-example-live [ng-view]')).getText();
content = element(by.css('[ng-view]')).getText();
expect(content).toMatch(/controller\: BookCntl/);
expect(content).toMatch(/Book Id\: Scarlet/);
});
@ -370,8 +367,7 @@ function $RouteProvider(){
/**
* @ngdoc event
* @name ngRoute.$route#$routeChangeStart
* @eventOf ngRoute.$route
* @name $route#$routeChangeStart
* @eventType broadcast on root scope
* @description
* Broadcasted before a route change. At this point the route services starts
@ -387,8 +383,7 @@ function $RouteProvider(){
/**
* @ngdoc event
* @name ngRoute.$route#$routeChangeSuccess
* @eventOf ngRoute.$route
* @name $route#$routeChangeSuccess
* @eventType broadcast on root scope
* @description
* Broadcasted after a route dependencies are resolved.
@ -403,8 +398,7 @@ function $RouteProvider(){
/**
* @ngdoc event
* @name ngRoute.$route#$routeChangeError
* @eventOf ngRoute.$route
* @name $route#$routeChangeError
* @eventType broadcast on root scope
* @description
* Broadcasted if any of the resolve promises are rejected.
@ -417,8 +411,7 @@ function $RouteProvider(){
/**
* @ngdoc event
* @name ngRoute.$route#$routeUpdate
* @eventOf ngRoute.$route
* @name $route#$routeUpdate
* @eventType broadcast on root scope
* @description
*
@ -432,8 +425,7 @@ function $RouteProvider(){
/**
* @ngdoc method
* @name ngRoute.$route#reload
* @methodOf ngRoute.$route
* @name $route#reload
*
* @description
* Causes `$route` service to reload the current route even if
@ -565,7 +557,7 @@ function $RouteProvider(){
/**
* @returns the current active route, by matching it against the URL
* @returns {Object} the current active route, by matching it against the URL
*/
function parseRoute() {
// Match a route
@ -583,7 +575,7 @@ function $RouteProvider(){
}
/**
* @returns interpolation of the redirect path with the parameters
* @returns {string} interpolation of the redirect path with the parameters
*/
function interpolate(string, params) {
var result = [];
@ -607,8 +599,8 @@ ngRouteModule.provider('$routeParams', $RouteParamsProvider);
/**
* @ngdoc object
* @name ngRoute.$routeParams
* @ngdoc service
* @name $routeParams
* @requires $route
*
* @description
@ -617,7 +609,7 @@ ngRouteModule.provider('$routeParams', $RouteParamsProvider);
* Requires the {@link ngRoute `ngRoute`} module to be installed.
*
* The route parameters are a combination of {@link ng.$location `$location`}'s
* {@link ng.$location#methods_search `search()`} and {@link ng.$location#methods_path `path()`}.
* {@link ng.$location#search `search()`} and {@link ng.$location#path `path()`}.
* The `path` parameters are extracted when the {@link ngRoute.$route `$route`} path is matched.
*
* In case of parameter name collision, `path` params take precedence over `search` params.
@ -630,14 +622,14 @@ ngRouteModule.provider('$routeParams', $RouteParamsProvider);
* Instead you can use `$route.current.params` to access the new route's parameters.
*
* @example
* <pre>
* ```js
* // Given:
* // URL: http://server.com/index.html#/Chapter/1/Section/2?search=moby
* // Route: /Chapter/:chapterId/Section/:sectionId
* //
* // Then
* $routeParams ==> {chapterId:1, sectionId:2, search:'moby'}
* </pre>
* ```
*/
function $RouteParamsProvider() {
this.$get = function() { return {}; };
@ -649,7 +641,7 @@ ngRouteModule.directive('ngView', ngViewFillContentFactory);
/**
* @ngdoc directive
* @name ngRoute.directive:ngView
* @name ngView
* @restrict ECA
*
* @description
@ -679,7 +671,9 @@ ngRouteModule.directive('ngView', ngViewFillContentFactory);
* - Otherwise enable scrolling only if the `autoscroll` attribute value evaluated
* as an expression yields a truthy value.
* @example
<example module="ngViewExample" deps="angular-route.js" animations="true">
<example name="ngView-directive" module="ngViewExample"
deps="angular-route.js;angular-animate.js"
animations="true" fixBase="true">
<file name="index.html">
<div ng-controller="MainCntl as main">
Choose:
@ -794,17 +788,17 @@ ngRouteModule.directive('ngView', ngViewFillContentFactory);
}
</file>
<file name="protractorTest.js">
<file name="protractor.js" type="protractor">
it('should load and compile correct template', function() {
element(by.linkText('Moby: Ch1')).click();
var content = element(by.css('.doc-example-live [ng-view]')).getText();
var content = element(by.css('[ng-view]')).getText();
expect(content).toMatch(/controller\: ChapterCntl/);
expect(content).toMatch(/Book Id\: Moby/);
expect(content).toMatch(/Chapter Id\: 1/);
element(by.partialLinkText('Scarlet')).click();
content = element(by.css('.doc-example-live [ng-view]')).getText();
content = element(by.css('[ng-view]')).getText();
expect(content).toMatch(/controller\: BookCntl/);
expect(content).toMatch(/Book Id\: Scarlet/);
});
@ -815,8 +809,7 @@ ngRouteModule.directive('ngView', ngViewFillContentFactory);
/**
* @ngdoc event
* @name ngRoute.directive:ngView#$viewContentLoaded
* @eventOf ngRoute.directive:ngView
* @name ngView#$viewContentLoaded
* @eventType emit on the current ngView scope
* @description
* Emitted every time the ngView content is reloaded.
@ -831,6 +824,7 @@ function ngViewFactory( $route, $anchorScroll, $animate) {
link: function(scope, $element, attr, ctrl, $transclude) {
var currentScope,
currentElement,
previousElement,
autoScrollExp = attr.autoscroll,
onloadExp = attr.onload || '';
@ -838,12 +832,19 @@ function ngViewFactory( $route, $anchorScroll, $animate) {
update();
function cleanupLastView() {
if (currentScope) {
if(previousElement) {
previousElement.remove();
previousElement = null;
}
if(currentScope) {
currentScope.$destroy();
currentScope = null;
}
if(currentElement) {
$animate.leave(currentElement);
$animate.leave(currentElement, function() {
previousElement = null;
});
previousElement = currentElement;
currentElement = null;
}
}

View File

@ -1,14 +1,14 @@
/*
AngularJS v1.2.13
AngularJS v1.2.14
(c) 2010-2014 Google, Inc. http://angularjs.org
License: MIT
*/
(function(h,e,A){'use strict';function u(w,q,k){return{restrict:"ECA",terminal:!0,priority:400,transclude:"element",link:function(a,c,b,f,n){function y(){l&&(l.$destroy(),l=null);g&&(k.leave(g),g=null)}function v(){var b=w.current&&w.current.locals;if(e.isDefined(b&&b.$template)){var b=a.$new(),f=w.current;g=n(b,function(d){k.enter(d,null,g||c,function(){!e.isDefined(t)||t&&!a.$eval(t)||q()});y()});l=f.scope=b;l.$emit("$viewContentLoaded");l.$eval(h)}else y()}var l,g,t=b.autoscroll,h=b.onload||"";
a.$on("$routeChangeSuccess",v);v()}}}function z(e,h,k){return{restrict:"ECA",priority:-400,link:function(a,c){var b=k.current,f=b.locals;c.html(f.$template);var n=e(c.contents());b.controller&&(f.$scope=a,f=h(b.controller,f),b.controllerAs&&(a[b.controllerAs]=f),c.data("$ngControllerController",f),c.children().data("$ngControllerController",f));n(a)}}}h=e.module("ngRoute",["ng"]).provider("$route",function(){function h(a,c){return e.extend(new (e.extend(function(){},{prototype:a})),c)}function q(a,
e){var b=e.caseInsensitiveMatch,f={originalPath:a,regexp:a},h=f.keys=[];a=a.replace(/([().])/g,"\\$1").replace(/(\/)?:(\w+)([\?\*])?/g,function(a,e,b,c){a="?"===c?c:null;c="*"===c?c:null;h.push({name:b,optional:!!a});e=e||"";return""+(a?"":e)+"(?:"+(a?e:"")+(c&&"(.+?)"||"([^/]+)")+(a||"")+")"+(a||"")}).replace(/([\/$\*])/g,"\\$1");f.regexp=RegExp("^"+a+"$",b?"i":"");return f}var k={};this.when=function(a,c){k[a]=e.extend({reloadOnSearch:!0},c,a&&q(a,c));if(a){var b="/"==a[a.length-1]?a.substr(0,a.length-
1):a+"/";k[b]=e.extend({redirectTo:a},q(b,c))}return this};this.otherwise=function(a){this.when(null,a);return this};this.$get=["$rootScope","$location","$routeParams","$q","$injector","$http","$templateCache","$sce",function(a,c,b,f,n,q,v,l){function g(){var d=t(),m=r.current;if(d&&m&&d.$$route===m.$$route&&e.equals(d.pathParams,m.pathParams)&&!d.reloadOnSearch&&!x)m.params=d.params,e.copy(m.params,b),a.$broadcast("$routeUpdate",m);else if(d||m)x=!1,a.$broadcast("$routeChangeStart",d,m),(r.current=
d)&&d.redirectTo&&(e.isString(d.redirectTo)?c.path(u(d.redirectTo,d.params)).search(d.params).replace():c.url(d.redirectTo(d.pathParams,c.path(),c.search())).replace()),f.when(d).then(function(){if(d){var a=e.extend({},d.resolve),c,b;e.forEach(a,function(d,c){a[c]=e.isString(d)?n.get(d):n.invoke(d)});e.isDefined(c=d.template)?e.isFunction(c)&&(c=c(d.params)):e.isDefined(b=d.templateUrl)&&(e.isFunction(b)&&(b=b(d.params)),b=l.getTrustedResourceUrl(b),e.isDefined(b)&&(d.loadedTemplateUrl=b,c=q.get(b,
{cache:v}).then(function(a){return a.data})));e.isDefined(c)&&(a.$template=c);return f.all(a)}}).then(function(c){d==r.current&&(d&&(d.locals=c,e.copy(d.params,b)),a.$broadcast("$routeChangeSuccess",d,m))},function(c){d==r.current&&a.$broadcast("$routeChangeError",d,m,c)})}function t(){var a,b;e.forEach(k,function(f,k){var p;if(p=!b){var s=c.path();p=f.keys;var l={};if(f.regexp)if(s=f.regexp.exec(s)){for(var g=1,q=s.length;g<q;++g){var n=p[g-1],r="string"==typeof s[g]?decodeURIComponent(s[g]):s[g];
n&&r&&(l[n.name]=r)}p=l}else p=null;else p=null;p=a=p}p&&(b=h(f,{params:e.extend({},c.search(),a),pathParams:a}),b.$$route=f)});return b||k[null]&&h(k[null],{params:{},pathParams:{}})}function u(a,c){var b=[];e.forEach((a||"").split(":"),function(a,d){if(0===d)b.push(a);else{var e=a.match(/(\w+)(.*)/),f=e[1];b.push(c[f]);b.push(e[2]||"");delete c[f]}});return b.join("")}var x=!1,r={routes:k,reload:function(){x=!0;a.$evalAsync(g)}};a.$on("$locationChangeSuccess",g);return r}]});h.provider("$routeParams",
function(){this.$get=function(){return{}}});h.directive("ngView",u);h.directive("ngView",z);u.$inject=["$route","$anchorScroll","$animate"];z.$inject=["$compile","$controller","$route"]})(window,window.angular);
(function(n,e,A){'use strict';function x(s,g,k){return{restrict:"ECA",terminal:!0,priority:400,transclude:"element",link:function(a,c,b,f,w){function y(){p&&(p.remove(),p=null);h&&(h.$destroy(),h=null);l&&(k.leave(l,function(){p=null}),p=l,l=null)}function v(){var b=s.current&&s.current.locals;if(e.isDefined(b&&b.$template)){var b=a.$new(),d=s.current;l=w(b,function(d){k.enter(d,null,l||c,function(){!e.isDefined(t)||t&&!a.$eval(t)||g()});y()});h=d.scope=b;h.$emit("$viewContentLoaded");h.$eval(u)}else y()}
var h,l,p,t=b.autoscroll,u=b.onload||"";a.$on("$routeChangeSuccess",v);v()}}}function z(e,g,k){return{restrict:"ECA",priority:-400,link:function(a,c){var b=k.current,f=b.locals;c.html(f.$template);var w=e(c.contents());b.controller&&(f.$scope=a,f=g(b.controller,f),b.controllerAs&&(a[b.controllerAs]=f),c.data("$ngControllerController",f),c.children().data("$ngControllerController",f));w(a)}}}n=e.module("ngRoute",["ng"]).provider("$route",function(){function s(a,c){return e.extend(new (e.extend(function(){},
{prototype:a})),c)}function g(a,e){var b=e.caseInsensitiveMatch,f={originalPath:a,regexp:a},k=f.keys=[];a=a.replace(/([().])/g,"\\$1").replace(/(\/)?:(\w+)([\?\*])?/g,function(a,e,b,c){a="?"===c?c:null;c="*"===c?c:null;k.push({name:b,optional:!!a});e=e||"";return""+(a?"":e)+"(?:"+(a?e:"")+(c&&"(.+?)"||"([^/]+)")+(a||"")+")"+(a||"")}).replace(/([\/$\*])/g,"\\$1");f.regexp=RegExp("^"+a+"$",b?"i":"");return f}var k={};this.when=function(a,c){k[a]=e.extend({reloadOnSearch:!0},c,a&&g(a,c));if(a){var b=
"/"==a[a.length-1]?a.substr(0,a.length-1):a+"/";k[b]=e.extend({redirectTo:a},g(b,c))}return this};this.otherwise=function(a){this.when(null,a);return this};this.$get=["$rootScope","$location","$routeParams","$q","$injector","$http","$templateCache","$sce",function(a,c,b,f,g,n,v,h){function l(){var d=p(),m=r.current;if(d&&m&&d.$$route===m.$$route&&e.equals(d.pathParams,m.pathParams)&&!d.reloadOnSearch&&!u)m.params=d.params,e.copy(m.params,b),a.$broadcast("$routeUpdate",m);else if(d||m)u=!1,a.$broadcast("$routeChangeStart",
d,m),(r.current=d)&&d.redirectTo&&(e.isString(d.redirectTo)?c.path(t(d.redirectTo,d.params)).search(d.params).replace():c.url(d.redirectTo(d.pathParams,c.path(),c.search())).replace()),f.when(d).then(function(){if(d){var a=e.extend({},d.resolve),c,b;e.forEach(a,function(d,c){a[c]=e.isString(d)?g.get(d):g.invoke(d)});e.isDefined(c=d.template)?e.isFunction(c)&&(c=c(d.params)):e.isDefined(b=d.templateUrl)&&(e.isFunction(b)&&(b=b(d.params)),b=h.getTrustedResourceUrl(b),e.isDefined(b)&&(d.loadedTemplateUrl=
b,c=n.get(b,{cache:v}).then(function(a){return a.data})));e.isDefined(c)&&(a.$template=c);return f.all(a)}}).then(function(c){d==r.current&&(d&&(d.locals=c,e.copy(d.params,b)),a.$broadcast("$routeChangeSuccess",d,m))},function(c){d==r.current&&a.$broadcast("$routeChangeError",d,m,c)})}function p(){var a,b;e.forEach(k,function(f,k){var q;if(q=!b){var g=c.path();q=f.keys;var l={};if(f.regexp)if(g=f.regexp.exec(g)){for(var h=1,p=g.length;h<p;++h){var n=q[h-1],r="string"==typeof g[h]?decodeURIComponent(g[h]):
g[h];n&&r&&(l[n.name]=r)}q=l}else q=null;else q=null;q=a=q}q&&(b=s(f,{params:e.extend({},c.search(),a),pathParams:a}),b.$$route=f)});return b||k[null]&&s(k[null],{params:{},pathParams:{}})}function t(a,c){var b=[];e.forEach((a||"").split(":"),function(a,d){if(0===d)b.push(a);else{var e=a.match(/(\w+)(.*)/),f=e[1];b.push(c[f]);b.push(e[2]||"");delete c[f]}});return b.join("")}var u=!1,r={routes:k,reload:function(){u=!0;a.$evalAsync(l)}};a.$on("$locationChangeSuccess",l);return r}]});n.provider("$routeParams",
function(){this.$get=function(){return{}}});n.directive("ngView",x);n.directive("ngView",z);x.$inject=["$route","$anchorScroll","$animate"];z.$inject=["$compile","$controller","$route"]})(window,window.angular);
//# sourceMappingURL=angular-route.min.js.map

File diff suppressed because one or more lines are too long

View File

@ -1,8 +1,8 @@
{
"name": "angular-route",
"version": "1.2.13",
"version": "1.2.14",
"main": "./angular-route.js",
"dependencies": {
"angular": "1.2.13"
"angular": "1.2.14"
}
}

View File

@ -1,19 +1,18 @@
{
"name": "angular-sanitize",
"version": "1.2.12",
"version": "1.2.15-build.2398+sha.4bab3d8",
"main": "./angular-sanitize.js",
"dependencies": {
"angular": "1.2.12"
"angular": "1.2.15-build.2398+sha.4bab3d8"
},
"homepage": "https://github.com/angular/bower-angular-sanitize",
"_release": "1.2.12",
"_release": "1.2.15-build.2398+sha.4bab3d8",
"_resolution": {
"type": "version",
"tag": "v1.2.12",
"commit": "5d289f45d4ebda51e2b9b53093aabd881f82120a"
"tag": "v1.2.15-build.2398+sha.4bab3d8",
"commit": "1721d2dac3d78bd743a7bc9f05f1c94bc701691e"
},
"_source": "git://github.com/angular/bower-angular-sanitize.git",
"_target": "~1.2.12",
"_originalSource": "angular-sanitize",
"_direct": true
"_originalSource": "angular-sanitize"
}

View File

@ -1,5 +1,5 @@
/**
* @license AngularJS v1.2.12
* @license AngularJS v1.2.15-build.2398+sha.4bab3d8
* (c) 2010-2014 Google, Inc. http://angularjs.org
* License: MIT
*/
@ -8,7 +8,7 @@
var $sanitizeMinErr = angular.$$minErr('$sanitize');
/**
* @ngdoc overview
* @ngdoc module
* @name ngSanitize
* @description
*
@ -16,7 +16,6 @@ var $sanitizeMinErr = angular.$$minErr('$sanitize');
*
* The `ngSanitize` module provides functionality to sanitize HTML.
*
* {@installModule sanitize}
*
* <div doc-module-components="ngSanitize"></div>
*
@ -42,7 +41,7 @@ var $sanitizeMinErr = angular.$$minErr('$sanitize');
/**
* @ngdoc service
* @name ngSanitize.$sanitize
* @name $sanitize
* @function
*
* @description
@ -58,8 +57,8 @@ var $sanitizeMinErr = angular.$$minErr('$sanitize');
* @returns {string} Sanitized html.
*
* @example
<doc:example module="ngSanitize">
<doc:source>
<example module="ngSanitize" deps="angular-sanitize.js">
<file name="index.html">
<script>
function Ctrl($scope, $sce) {
$scope.snippet =
@ -103,8 +102,8 @@ var $sanitizeMinErr = angular.$$minErr('$sanitize');
</tr>
</table>
</div>
</doc:source>
<doc:protractor>
</file>
<file name="protractor.js" type="protractor">
it('should sanitize the html snippet by default', function() {
expect(element(by.css('#bind-html-with-sanitize div')).getInnerHtml()).
toBe('<p>an html\n<em>click here</em>\nsnippet</p>');
@ -134,8 +133,8 @@ var $sanitizeMinErr = angular.$$minErr('$sanitize');
expect(element(by.css('#bind-default div')).getInnerHtml()).toBe(
"new &lt;b onclick=\"alert(1)\"&gt;text&lt;/b&gt;");
});
</doc:protractor>
</doc:example>
</file>
</example>
*/
function $SanitizeProvider() {
this.$get = ['$$sanitizeUri', function($$sanitizeUri) {
@ -260,7 +259,7 @@ function htmlParser( html, handler ) {
match = html.match( DOCTYPE_REGEXP );
if ( match ) {
html = html.replace( match[0] , '');
html = html.replace( match[0], '');
chars = false;
}
// end tag
@ -400,7 +399,7 @@ function decodeEntities(value) {
* resulting string can be safely inserted into attribute or
* element text.
* @param value
* @returns escaped text
* @returns {string} escaped text
*/
function encodeEntities(value) {
return value.
@ -476,7 +475,7 @@ angular.module('ngSanitize', []).provider('$sanitize', $SanitizeProvider);
/**
* @ngdoc filter
* @name ngSanitize.filter:linky
* @name linky
* @function
*
* @description
@ -493,8 +492,8 @@ angular.module('ngSanitize', []).provider('$sanitize', $SanitizeProvider);
<span ng-bind-html="linky_expression | linky"></span>
*
* @example
<doc:example module="ngSanitize">
<doc:source>
<example module="ngSanitize" deps="angular-sanitize.js">
<file name="index.html">
<script>
function Ctrl($scope) {
$scope.snippet =
@ -538,8 +537,8 @@ angular.module('ngSanitize', []).provider('$sanitize', $SanitizeProvider);
<td><div ng-bind="snippet"></div></td>
</tr>
</table>
</doc:source>
<doc:protractor>
</file>
<file name="protractor.js" type="protractor">
it('should linkify the snippet with urls', function() {
expect(element(by.id('linky-filter')).element(by.binding('snippet | linky')).getText()).
toBe('Pretty text with some links: http://angularjs.org/, us@somewhere.org, ' +
@ -570,8 +569,8 @@ angular.module('ngSanitize', []).provider('$sanitize', $SanitizeProvider);
toBe('http://angularjs.org/');
expect(element(by.css('#linky-target a')).getAttribute('target')).toEqual('_blank');
});
</doc:protractor>
</doc:example>
</file>
</example>
*/
angular.module('ngSanitize').filter('linky', ['$sanitize', function($sanitize) {
var LINKY_URL_REGEXP =

View File

@ -1,5 +1,5 @@
/*
AngularJS v1.2.12
AngularJS v1.2.15-build.2398+sha.4bab3d8
(c) 2010-2014 Google, Inc. http://angularjs.org
License: MIT
*/

File diff suppressed because one or more lines are too long

View File

@ -1,8 +1,8 @@
{
"name": "angular-sanitize",
"version": "1.2.12",
"version": "1.2.15-build.2398+sha.4bab3d8",
"main": "./angular-sanitize.js",
"dependencies": {
"angular": "1.2.12"
"angular": "1.2.15-build.2398+sha.4bab3d8"
}
}

View File

@ -0,0 +1,48 @@
{
"name": "angular-scheduler",
"authors": [
"Chris Houseknecht <chouse@ansible.com>"
],
"description": "Angular based UI widget for creating repeat schedule entries.",
"license": "MIT",
"ignore": [
"**/.*",
"node_modules",
"bower_components",
"test",
"tests"
],
"devDependencies": {
"angular-route": "*",
"components-font-awesome": "*",
"jquery": "*",
"jqueryui": "*",
"less": "*",
"angular-mocks": "~1.2.13"
},
"dependencies": {
"angular": "~1.2.14",
"underscore": "*",
"twitter": "*",
"rrule": "*",
"timezone-js": "*",
"angular-tz-extensions": "*"
},
"homepage": "https://github.com/chouseknecht/angular-scheduler",
"main": "git@github.com:chouseknecht/angular-scheduler.git",
"keywords": [
"schedule",
"rrule",
"calendar"
],
"_release": "f2488ff1ec",
"_resolution": {
"type": "branch",
"branch": "master",
"commit": "f2488ff1ec1b2aa48206fa97111b6f8d5e88de89"
},
"_source": "git://github.com/chouseknecht/angular-scheduler.git",
"_target": "*",
"_originalSource": "angular-scheduler",
"_direct": true
}

View File

@ -0,0 +1,43 @@
module.exports = function(grunt) {
grunt.initConfig({
pkg: grunt.file.readJSON('./package.json'),
jshint: {
options: {
jshintrc: '.jshintrc'
},
uses_defaults: ['lib/angular-scheduler.js', 'app/js/sampleApp.js']
},
uglify: {
options: {
banner: '/*! <%= pkg.name %> - v<%= pkg.version %> - ' +
'<%= grunt.template.today("yyyy-mm-dd") %> */'
},
my_target: {
files: {
'lib/angular-scheduler.min.js': ['lib/angular-scheduler.js']
}
}
},
less: {
production: {
options: {
cleancss: true
},
files: {
"lib/angular-scheduler.min.css": "lib/angular-scheduler.css"
}
}
}
});
grunt.loadNpmTasks('grunt-contrib-jshint');
grunt.loadNpmTasks('grunt-contrib-uglify');
grunt.loadNpmTasks('grunt-contrib-less');
grunt.registerTask('default', ['jshint', 'uglify', 'less']);
}

View File

@ -0,0 +1,20 @@
The MIT License (MIT)
Copyright (c) 2014 Ansible, Inc.
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@ -0,0 +1,27 @@
angular-scheduler
=================
A UI widget for creating or editing repeating calendar entries. Dynamically injects HTML anwhere in an Angular app. Provides methods for converting schedule entry to and from RRule format, based on the [iCalendar RFC](http://www.ietf.org/rfc/rfc2445.txt).
Intalling
---------
bower install angular-scheduler
Using
-----
Coming soon...
Examples
--------
An example site is included along with a simple node based web server. With [node](http://nodejs.org) installed, run the following:
node scripts/web-server.js
Once that's running, point you browser to http://localhost:8000/app/index.html

View File

@ -0,0 +1,185 @@
/*********************************************
* Copyright (c) 2013-2014 Chris Houseknecht
*
* SampleForm.js
*
* Demonstrate some of the things you can do with angular-forms.js to
* generate clean, consistent forms in your app.
*
*/
@black: #171717;
@white: #FFF;
@red: #da4f49;
@red-hover: #AE3F3A;
@green: #5bb75b;
@blue: #1778c3; /* logo blue */
@blue-link: #1778c3;
@blue-dark: #2a6496; /* link hover */
@grey: #A9A9A9;
@grey-txt: #707070;
@well: #f5f5f5; /* well background color */
@well-border: #e3e3e3;
@info: #d9edf7; /* alert info background color */
@info-border: #bce8f1; /* alert info border color */
@info-color: #3a87ad;
body {
padding-bottom: 80px;
}
.navbar {
margin-bottom: 40px;
}
#parse-type-group {
margin: 15px 0;
}
#parse-type-label {
display: inline-block;
padding-right: 10px;
font-weight: bold;
}
hr {
margin-top: 40px;
margin-bottom: 40px;
}
.table {
margin-bottom: 0;
}
.well {
margin-bottom: 0;
}
textarea {
resize: none;
}
a,
a:active,
a:link,
a:hover {
text-decoration: none;
}
.external-editor-link {
display: inline-block;
margin-left: 20px;
}
#message {
display: none;
}
.navbar-default .navbar-brand {
font-size: 24px;
color: #000;
}
.navigation {
margin-top: 20px;
padding-right: 15px;
}
.footer {
margin-top: 30px;
}
.footer .navbar-brand {
margin-bottom: 0;
padding-right: 0;
}
#form-container {
margin-top: 0;
}
#set-value-test {
margin-top: 40px;
}
/* Modal dialog */
.ui-dialog-title {
font-size: 22px;
color: @blue;
font-weight: bold;
line-height: normal;
}
.ui-dialog {
.close {
font-size: 18px;
font-weight: bold;
font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
line-height: 1;
opacity: .7;
text-shadow: 0 1px 0 @white;
}
.ui-widget-header {
border-radius: 0;
border: none;
border-bottom: 1px solid #A9A9A9;
height: 55px;
}
.ui-dialog-titlebar {
padding-bottom: 0;
padding-top: 12px;
}
.ui-dialog-titlebar.ui-widget-header {
background-image: none;
background-color: @white;
}
.ui-dialog-titlebar .ui-state-default {
background-image: none;
background-color: @white;
border-color: @white;
color: #A9A9A9;
}
.mono-space {
font-family: Fixed, monospace;
}
textarea.resizable {
resize: vertical;
}
.ui-resizable-se {
right: 5px;
bottom: 5px;
background-position: -80px -224px;
color: @black;
}
}
.ui-dialog-buttonset {
button.btn.btn-default.ui-state-hover,
button.btn.btn-default.ui-state-active,
button.btn.btn-default.ui-state-focus {
font-weight: normal;
}
button.btn.btn-primary.ui-state-hover,
button.btn.btn-primary.ui-state-active,
button.btn.btn-primary.ui-state-focus {
background-image: none;
color: @white;
background-color: @blue-dark;
border-color: #285e8e;
text-decoration: none;
font-weight: normal;
}
}
.ui-widget-overlay.ui-front {
background-image: none;
background-color: #000;
opacity: .6;
z-index: 1040;
}
.ui-front {
z-index: 1050;
}

View File

@ -0,0 +1,62 @@
<!doctype html>
<html lang="en" ng-app="sampleApp">
<head>
<meta charset="utf-8">
<title>Angular Scheduler | Sample application</title>
<link rel="stylesheet" href="/bower_components/twitter/dist/css/bootstrap.min.css" >
<link rel="stylesheet" href="/bower_components/components-font-awesome/css/font-awesome.min.css" >
<link rel="stylesheet" href="/bower_components/jqueryui/themes/redmond/jquery-ui.min.css" >
<link rel="stylesheet" href="/lib/angular-scheduler.css" >
<link rel="stylesheet/less" type="text/css" href="/app/css/sampleApp.less" >
</head>
<body>
<nav class="navbar navbar-default" role="navigation">
<div class="container">
<!-- Brand and toggle get grouped for better mobile display -->
<div class="navbar-header">
<button type="button" class="navbar-toggle" data-toggle="collapse" data-target="#nb-collapse-1">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="/">Angular Scheduler</a>
</div>
</div>
</nav>
<div class="container">
<div id="main-view" ng-view></div>
</div>
<div class="footer navbar-default navbar-fixed-bottom">
<div class="container">
<div class="row">
<div class="col-lg-12">
<a class="navbar-brand pull-right" href="/">Angular Scheduler</a>
</div>
</div>
</div>
</div>
<script src="/bower_components/less/dist/less-1.6.2.min.js"></script>
<script src="/bower_components/jquery/dist/jquery.min.js"></script>
<script src="/bower_components/jqueryui/ui/minified/jquery-ui.min.js"></script>
<script src="/bower_components/twitter/dist/js/bootstrap.min.js"></script>
<!-- timezone -->
<script src="/bower_components/timezone-js/src/date.js"></script>
<script src="/bower_components/angular-tz-extensions/packages/jstimezonedetect/jstz.min.js"></script>
<!-- rrule -->
<script src="/bower_components/underscore/underscore.js"></script>
<script src="/bower_components/rrule/lib/rrule.js"></script>
<script src="/bower_components/angular/angular.min.js"></script>
<script src="/bower_components/angular-route/angular-route.min.js"></script>
<script src="/bower_components/angular-tz-extensions/lib/angular-tz-extensions.js"></script>
<script src="/lib/angular-scheduler.js"></script>
<script src="/app/js/sampleApp.js"></script>
</body>
</html>

View File

@ -0,0 +1,81 @@
/**********************************************
* sampleApp.js
*
* Copyright (c) 2013-2014 Ansible, Inc.
*
*/
'use strict';
angular.module('sampleApp', ['ngRoute', 'AngularScheduler', 'Timezones'])
.config(['$routeProvider', function($routeProvider) {
$routeProvider
.when('/', {
templateUrl: 'partials/main.html',
controller: 'sampleController'
})
.otherwise({
redirectTo: '/'
});
}])
.constant('AngularScheduler.partial', '/lib/angular-scheduler.html')
.constant('AngularScheduler.useTimezone', false)
.constant('AngularScheduler.showUTCField', false)
.constant('$timezones.definitions.location', '/bower_components/angular-tz-extensions/tz/data')
.controller('sampleController', ['$scope', '$filter', 'SchedulerInit', function($scope, $filter, SchedulerInit) {
var scheduler = SchedulerInit({ scope: $scope });
scheduler.inject('form-container', true);
console.log('User timezone: ');
console.log(scheduler.getUserTimezone());
$scope.setRRule = function() {
$scope.inputRRuleMsg = '';
$scope.inputRRuleMsg = scheduler.setRRule($scope.inputRRule);
};
$scope.resetForm = function() { scheduler.clear(); };
$scope.saveForm = function() {
if (scheduler.isValid()) {
var schedule = scheduler.getValue(),
html =
"<form>\n" +
"<div class=\"form-group\">\n" +
"<label>RRule</label>\n" +
"<textarea id=\"rrule-result\" readonly class=\"form-control\" rows=\"8\">" + schedule.rrule + "</textarea>\n" +
"</div>\n" +
"</form>\n",
wheight = $(window).height(),
wwidth = $(window).width(),
w, h;
w = (600 > wwidth) ? wwidth : 600;
h = (400 > wheight) ? wheight : 400;
$('#message').html(html)
.dialog({
title: schedule.name,
modal: true,
width: w,
height: h,
position: 'center',
buttons: { OK: function() { $(this).dialog('close');} },
open: function () {
// fix the close button
$('.ui-dialog[aria-describedby="message"]').find('.ui-dialog-titlebar button')
.empty().attr({ 'class': 'close' }).text('x');
// fix the OK button
$('.ui-dialog[aria-describedby="message"]').find('.ui-dialog-buttonset button:first')
.attr({ 'class': 'btn btn-primary', 'id': 'modal-ok-button' });
}
});
}
};
}]);

View File

@ -0,0 +1,68 @@
<div class="row">
<div class="col-md-8">
<div class="well">
<div id="form-container"></div>
</div>
</div>
<div class="col-md-4">
<hr class="visible-sm visible-xs"/>
<table class="table table-condensed">
<theader>
<tr>
<th>Scope Variable</th>
<th>Value</th>
</tr>
<theader>
<tbody>
<tr><td>schedulerName</td><td>{{ schedulerName }}</td></tr>
<tr><td>schedulerStartDt</td><td>{{ schedulerStartDt }}</td></tr>
<tr><td>schedulerStartHour</td><td>{{ schedulerStartHour }}</td></tr>
<tr><td>schedulerStartMinute</td><td>{{ schedulerStartMinute }}</td></tr>
<tr><td>schedulerStartSecond</td><td>{{ schedulerStartSecond }}</td></tr>
<tr ng-show="schedulerShowTimeZone"><td>schedulerTimeZone.name</td><td>{{ schedulerTimeZone.name }}</td></tr>
<tr><td>schedulerUTCTime</td><td>{{ schedulerUTCTime }}</td></tr>
<tr><td>schedulerFrequency.value</td><td>{{ schedulerFrequency.value }}</td></tr>
<tr><td>schedulerInterval</td><td>{{ schedulerInterval }}</td></tr>
<tr ng-show="schedulerFrequency.value == 'monthly'"><td>monthlyRepeatOption</td><td>{{ monthlyRepeatOption }}</td></tr>
<tr ng-show="schedulerFrequency.value == 'monthly' && monthlyRepeatOption == 'day'"><td>monthDay</td><td>{{ monthDay }}</td></tr>
<tr ng-show="schedulerFrequency.value == 'monthly' && monthlyRepeatOption == 'other'"><td>monthlyOccurrence.value</td><td>{{ monthlyOccurrence.value }}</td></tr>
<tr ng-show="schedulerFrequency.value == 'monthly' && monthlyRepeatOption == 'other'"><td>monthlyWeekDay.value</td><td>{{ monthlyWeekDay.value }}</td></tr>
<tr ng-show="schedulerFrequency.value == 'yearly'"><td>yearlyRepeatOption</td><td>{{ yearlyRepeatOption }}</td></tr>
<tr ng-show="schedulerFrequency.value == 'yearly' && yearlyRepeatOption == 'month'"><td>yearlyMonth.value</td><td>{{ yearlyMonth.value }}</td></tr>
<tr ng-show="schedulerFrequency.value == 'yearly' && yearlyRepeatOption == 'month'"><td>yearlyMonthDay</td><td>{{ yearlyMonthDay }}</td></tr>
<tr ng-show="schedulerFrequency.value == 'yearly' && yearlyRepeatOption == 'other'"><td>yearlyOccurrence.value</td><td>{{ yearlyOccurrence.value }}</td></tr>
<tr ng-show="schedulerFrequency.value == 'yearly' && yearlyRepeatOption == 'other'"><td>yearlyWeekDay.value</td><td>{{ yearlyWeekDay.value }}</td></tr>
<tr ng-show="schedulerFrequency.value == 'yearly' && yearlyRepeatOption == 'other'"><td>yearlyOtherMonth.value</td><td>{{ yearlyOtherMonth.value }}</td></tr>
<tr ng-show="schedulerFrequency.value == 'weekly'"><td>weekDays.value</td><td>{{ weekDays }}</td></tr>
<tr ng-show="schedulerFrequency && schedulerFrequency.value != 'none'"><td>schedulerEnd.value</td><td>{{ schedulerEnd.value }}</td></tr>
<tr ng-show="schedulerFrequency && schedulerFrequency.value != 'none' && schedulerEnd.value == 'after'"><td>schedulerOccurrenceCount</td><td>{{ schedulerOccurrenceCount }}</td></tr>
<tr ng-show="schedulerFrequency && schedulerFrequency.value != 'none' && schedulerEnd.value == 'on'"><td>schedulerEndDt</td><td>{{ schedulerEndDt }}</td></tr>
</tbody>
</table>
</div>
</div>
<div class="row">
<div class="col-md-8">
<hr />
<div id="set-value-test">
<h4>RRule Demo</h4>
<p>Enter an RRule to display in the form above. For RRule syntax refer to examples at <a href="https://github.com/jkbr/rrule">https://github.com/jkbr/rrule</a>. Click Show Me to see the form change.</p>
<form class="form" role="form">
<div class="form-group">
<label>RRule</label>
<input type="text" ng-model="inputRRule" class="form-control" ng-keypress="inputRRuleMsg='';" placeholder="RRule key=value pairs">
<div class="error" ng-bind="inputRRuleMsg" style="height: 20px;"></div>
</div>
</form>
<div class="button-group">
<button type="button" class="btn btn-primary btn-sm" id="show-me-button" ng-click="setRRule()">Show Me</button>
</div>
</div>
</div>
</div>
<div id="message"><div>

View File

@ -0,0 +1,39 @@
{
"name": "angular-scheduler",
"version": "0.0.1",
"authors": [
"Chris Houseknecht <chouse@ansible.com>"
],
"description": "Angular based UI widget for creating repeat schedule entries.",
"license": "MIT",
"ignore": [
"**/.*",
"node_modules",
"bower_components",
"test",
"tests"
],
"devDependencies": {
"angular-route": "*",
"components-font-awesome": "*",
"jquery": "*",
"jqueryui": "*",
"less": "*",
"angular-mocks": "~1.2.13"
},
"dependencies": {
"angular": "~1.2.14",
"underscore": "*",
"twitter": "*",
"rrule": "*",
"timezone-js": "*",
"angular-tz-extensions":"*"
},
"homepage": "https://github.com/chouseknecht/angular-scheduler",
"main": "git@github.com:chouseknecht/angular-scheduler.git",
"keywords": [
"schedule",
"rrule",
"calendar"
]
}

View File

@ -0,0 +1,93 @@
/***************************************************************************
* angular-scheruler.css
*
* Copyright (c) 2014 Ansible, Inc.
*
* Maintainers:
*
* Chris Houseknecht
* @chouseknecht
* chouse@ansible.com
*
*/
.ui-widget input {
font-size: 12px;
font-weight: normal;
text-align: center;
}
.ui-spinner.ui-widget-content {
border-bottom-color: #ccc;
border-top-color: #ccc;
border-left-color: #ccc;
border-right-color: #ccc;
}
.ui-spinner-button {
border-left-color: #ccc;
border-left-style: solid;
border-left-width: 1px;
}
.scheduler-time-spinner {
width: 40px;
height: 24px;
}
.scheduler-spinner {
width: 50px;
height: 24px;
}
.fmt-help {
font-size: 12px;
font-weight: normal;
color: #999;
padding-left: 10px;
}
.error {
color: #dd1b16;
font-size: 12px;
margin-bottom: 0;
margin-top: 0;
padding-top: 3px;
}
.pull-up {
margin-top: -15px;
margin-bottom: 10px;
}
.red-text {
color: #dd1b16;
}
input.ng-dirty.ng-invalid, select.ng-dirty.ng-invalid, textarea.ng-dirty.ng-invalid {
border: 1px solid red;
outline: none;
}
.help-text {
font-size: 12px;
font-weight: normal;
color: #999;
margin-top: 5px;
}
.inline-label {
margin-left: 10px;
}
#scheduler-buttons {
margin-top: 20px;
}
.no-label {
padding-top: 25px;
}
.padding-top-slim {
padding-top: 5px;
}
.option-pad-left {
padding-left: 15px;
}
.option-pad-top {
padding-top: 15px;
}
.option-pad-bottom {
padding-bottom: 15px;
}
#monthlyOccurrence, #monthlyWeekDay {
margin-top: 5px;
}
select {
width: 100%;
}

View File

@ -0,0 +1,229 @@
<!--
angular-scheruler.html
Partial to be injected on inect() method call, providing the form for our widget.
Copyright (c) 2014 Ansible, Inc.
Maintainers:
Chris Houseknecht
@chouseknecht
chouse@ansible.com
-->
<div class="row">
<div class="col-md-12">
<form class="form" role="form" name="scheduler_form" novalidate>
<div class="row">
<div class="col-md-5">
<div class="form-group">
<label><span class="red-text">*</span> Name</label>
<input type="text" class="form-control input-sm" name="schedulerName" id="schedulerName" ng-model="schedulerName" required placeholder="Schedule name">
<div class="error" ng-show="scheduler_form.schedulerName.$dirty && scheduler_form.schedulerName.$error.required">Schedule name is required</div>
</div>
</div>
</div>
<div class="row">
<div class="col-md-5">
<div class="form-group">
<label>Start Date <span class="fmt-help"> yyyy-mm-dd</span></label>
<div class="input-group">
<input type="text" class="form-control input-sm" name="schedulerStartDt" id="schedulerStartDt" ng-model="schedulerStartDt"
sch-date-picker data-min-today="true" placeholder="YYYY-MM-DD" required ng-change="scheduleTimeChange()" >
<span class="input-group-btn">
<button class="btn btn-default btn-sm" type="button" ng-click="showCalendar('schedulerStartDt')">
<i class="fa fa-calendar"></i></button>
</span>
</div>
</div>
</div>
<div class="col-md-7">
<div class="form-group">
<label>Start Time <span class="fmt-help">HH24:MM:SS</span><span class="fmt-help" ng-show="!schedulerShowTimeZone">UTC</span></label>
<div class="input-group">
<input name="schedulerStartHour" id="schedulerStartHour" sch-spinner="scheduler_form" class="scheduler-time-spinner"
ng-model="schedulerStartHour" data-zero-pad="2" placeholder="HH24" min="0" max="23" required ng-change="scheduleTimeChange" >
<span>:</span><input name="schedulerStartMinute" id="schedulerStartMinute" sch-spinner="scheduler_form" class="scheduler-time-spinner"
data-zero-pad="2" ng-model="schedulerStartMinute" placeholder="MM" min="0" max="59" required ng-change="scheduleTimeChange" >
<span>:</span><input name="schedulerStartSecond" id="schedulerStartSecond" sch-spinner="scheduler_form" class="scheduler-time-spinner"
data-zero-pad="2" ng-model="schedulerStartSecond" placeholder="SS" min="0" max="23" required ng-change="scheduleTimeChange" >
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-md-12">
<div class="error pull-up" ng-show="scheduler_form.schedulerStartDt.$dirty && scheduler_form_schedulerStartDt_error" ng-bind="scheduler_form_schedulerStartDt_error"></div>
</div>
</div>
<div class="row">
<div class="col-md-4" ng-show="schedulerShowTimeZone">
<div class="form-group">
<label>Time Zone</label>
<select name="schedulerTimeZone" id="schedulerTimeZone" ng-model="schedulerTimeZone" ng-options="z.name for z in timeZones"
required class="form-control input-sm" ng-change="scheduleTimeChange()" ></select>
</div>
</div>
<div class="col-md-4" ng-show="schedulerShowUTCStartTime">
<div class="form-group">
<label>UTC Start Time</label>
<input type="text" name="schedulerUTCTime" ng-model="schedulerUTCTime" id="schedulerUTCTime" class="form-control input-sm" readonly>
</div>
</div>
</div>
<div class="row">
<div class="col-md-4">
<div class="form-group">
<label>Repeat</label>
<select name="schedulerFrequency" id="schedulerFrequency" ng-model="schedulerFrequency"
ng-options="f.name for f in frequencyOptions" required class="form-control input-sm"
ng-change="scheduleRepeatChange()"></select>
</div>
</div>
<div class="col-md-4">
<div class="form-group no-label" ng-show="schedulerShowInterval">
<label>Every</label>
<input name="schedulerInterval" id="schedulerInterval" sch-spinner="scheduler_form" class="scheduler-spinner"
ng-model="schedulerInterval" min="1" max="999" >
<label class="inline-label" ng-bind="schedulerIntervalLabel"></label>
</div>
</div>
</div>
<div class="row" ng-show="schedulerFrequency && schedulerFrequency.value == 'monthly'">
<div class="col-md-12">
<div class="form-group option-pad-left">
<div class="radio col-md-2">
<label><input type="radio" value="day" ng-model="monthlyRepeatOption" ng-change="monthlyRepeatChange()" name="monthlyRepeatOption"
id="monthlyRepeatOption"> on day</label>
</div>
<div class="col-md-3" style="padding-top:5px">
<input name="monthDay" id="monthDay" sch-spinner="scheduler_form" class="scheduler-spinner"
ng-model="monthDay" min="1" max="31" >
</div>
</div>
</div>
</div>
<div class="row option-pad-bottom" ng-show="schedulerFrequency && schedulerFrequency.value == 'monthly'">
<div class="col-md-12">
<div class="form-group option-pad-left">
<div class="radio col-md-2">
<label><input type="radio" value="other" ng-model="monthlyRepeatOption" ng-change="monthlyRepeatChange()" name="monthlyRepeatOption" id="monthlyRepeatOption"> on the</label>
</div>
<div class="col-md-3">
<select name="monthlyOccurrence" id="monthlyOccurrence" ng-model="monthlyOccurrence" ng-options="o.name for o in occurrences"
ng-disabled="monthlyRepeatOption != 'other'" class="form-control input-sm" ></select>
</div>
<div class="col-md-3">
<select name="monthlyWeekDay" id="monthlyWeekDay" ng-model="monthlyWeekDay" ng-options="w.name for w in weekdays"
ng-disabled="monthlyRepeatOption != 'other'" class="form-control input-sm" ></select>
</div>
</div>
</div>
</div>
<div class="row" ng-show="schedulerFrequency && schedulerFrequency.value == 'yearly'">
<div class="col-md-12">
<div class="form-group option-pad-left">
<div class="radio col-md-2">
<label><input type="radio" value="month" ng-model="yearlyRepeatOption" ng-change="yearlyRepeatChange()" name="yearlyRepeatOption" id="yearlyRepeatOption"> on</label>
</div>
<div class="col-md-3 padding-top-slim">
<select name="yearlyMonth" id="yearlyMonth" ng-model="yearlyMonth" ng-options="m.name for m in months"
ng-disabled="yearlyRepeatOption != 'month'" class="form-control input-sm" ></select>
</div>
<div class="col-md-3 padding-top-slim">
<input name="yearlyMonthDay" id="yearlyMonthDay" sch-spinner="scheduler_form" class="scheduler-spinner"
ng-model="yearlyMonthDay" min="1" max="31" />
</div>
</div>
</div>
</div>
<div class="row option-pad-bottom" ng-show="schedulerFrequency && schedulerFrequency.value == 'yearly'">
<div class="col-md-12">
<div class="form-group option-pad-left">
<div class="radio col-md-2">
<label><input type="radio" value="other" ng-model="yearlyRepeatOption" ng-change="yearlyRepeatChange()" name="yearlyRepeatOption"
id="yearlyRepeatOption"> on the</label>
</div>
<div class="col-md-2 padding-top-slim">
<select name="yearlyOccurrence" id="yearlyOccurrence" ng-model="yearlyOccurrence" ng-options="o.name for o in occurrences"
ng-disabled="yearlyRepeatOption != 'other'" class="form-control input-sm" ></select>
</div>
<div class="col-md-2 padding-top-slim">
<select name="yearlyWeekDay" id="yearlyWeekDay" ng-model="yearlyWeekDay" ng-options="w.name for w in weekdays"
ng-disabled="yearlyRepeatOption != 'other'" class="form-control input-sm" ></select>
</div>
<div class="col-md-2 padding-top-slim">
<select name="yearlyOtherMonth" id="yearlyOtherMonth" ng-model="yearlyOtherMonth" ng-options="m.name for m in months"
ng-disabled="yearlyRepeatOption != 'other'" class="form-control input-sm" ></select>
</div>
</div>
</div>
</div>
<div class="form-group option-pad-left option-pad-bottom" ng-show="schedulerFrequency && schedulerFrequency.value == 'weekly'">
<label><span class="red-text">*</span> On Days</label>
<div class="input-group">
<div class="btn-group" data-toggle="buttons-checkbox" id="weekdaySelect">
<button type="button" ng-class="weekDaySUClass" class="btn btn-default" data-value="SU" ng-click="setWeekday('su')">Sun</button>
<button type="button" ng-class="weekDayMOClass" class="btn btn-default" data-value="MO" ng-click="setWeekday('mo')">Mon</button>
<button type="button" ng-class="weekDayTUClass" class="btn btn-default" data-value="TU" ng-click="setWeekday('tu')">Tue</button>
<button type="button" ng-class="weekDayWEClass" class="btn btn-default" data-value="WE" ng-click="setWeekday('we')">Wed</button>
<button type="button" ng-class="weekDayTHClass" class="btn btn-default" data-value="TH" ng-click="setWeekday('th')">Thu</button>
<button type="button" ng-class="weekDayFRClass" class="btn btn-default" data-value="FR" ng-click="setWeekday('fr')">Fri</button>
<button type="button" ng-class="weekDaySAClass" class="btn btn-default" data-value="SA" ng-click="setWeekday('sa')">Sat</button>
</div>
</div>
<div class="error" ng-show="scheduler_weekDays_error">Select one or more days</div>
</div>
<div class="row">
<div class="col-md-4">
<div class="form-group" ng-show="schedulerShowInterval">
<label>End</label>
<div>
<select id="schedulerEnd" name="schedulerEnd" ng-model="schedulerEnd" ng-options="e.name for e in endOptions" required
class="form-control input-sm" ng-change="scheduleRepeatChange()"></select>
</div>
</div>
</div>
<div class="col-md-4" ng-show="schedulerEnd && schedulerEnd.value == 'after'">
<div class="form-group no-label">
<div class="input-group">
<input ng-name="schedulerOccurrenceCount" ng-id="schedulerOccurrenceCount" sch-spinner="scheduler_form" class="scheduler-spinner"
ng-model="schedulerOccurrenceCount" min="1" max="999" >
<label class="inline-label">Occurrence(s)</label>
</div>
</div>
</div>
<div class="col-md-4" ng-show="schedulerEnd && schedulerEnd.value == 'on'">
<div class="form-group no-label">
<div class="input-group">
<input type="text" name="schedulerEndDt" id="schedulerEndDt" class="form-control input-sm" ng-model="schedulerEndDt" sch-date-picker
data-min-today="true" placeholder="YYYY-MM-DD" >
<span class="input-group-btn">
<button class="btn btn-default btn-sm" type="button" ng-click="showCalendar('schedulerEndDt')">
<i class="fa fa-calendar"></i></button>
</span>
</div>
<div class="error" ng-show="scheduler_endDt_error">Provide a valid date</div>
</div>
</div>
</div>
</form>
<div id="scheduler-buttons" style="display:none;">
<button type="button" class="btn btn-default btn-sm" id="reset-button" ng-click="resetForm()"><i class="fa fa-undo"></i> Reset</button>
<button type="button" class="btn btn-primary btn-sm" id="save-button" ng-click="saveForm()"><i class="fa fa-check"></i> Save</button>
</div>
</div><!-- col-md-12 -->
</div><!-- row -->

View File

@ -0,0 +1,870 @@
/***************************************************************************
* angular-scheruler.js
*
* Copyright (c) 2014 Ansible, Inc.
*
* Maintainers:
*
* Chris Houseknecht
* @chouseknecht
* chouse@ansible.com
*
*/
/* global RRule */
'use strict';
angular.module('underscore',[])
.factory('_', [ function() {
return window._;
}]);
angular.module('AngularScheduler', ['underscore'])
.constant('AngularScheduler.partial', '/lib/angular-scheduler.html')
.constant('AngularScheduler.useTimezone', false)
.constant('AngularScheduler.showUTCField', false)
// Initialize supporting scope variables and functions. Returns a scheduler object with getString(),
// setString() and inject() methods.
.factory('SchedulerInit', ['$log', '$filter', '$timezones', 'LoadLookupValues', 'SetDefaults', 'CreateObject', '_',
'AngularScheduler.useTimezone', 'AngularScheduler.showUTCField',
function($log, $filter, $timezones, LoadLookupValues, SetDefaults, CreateObject, _, useTimezone, showUTCField) {
return function(params) {
var scope = params.scope;
scope.schedulerShowTimeZone = useTimezone;
scope.schedulerShowUTCStartTime = showUTCField;
scope.setDefaults = function() {
if (useTimezone) {
scope.current_timezone = $timezones.getLocal();
if ($.isEmptyObject(scope.current_timezone) || !scope.current_timezone.name) {
$log.error('Failed to find local timezone. Defaulting to America/New_York.');
scope.current_timezone = { name: 'America/New_York' };
}
// Set the <select> to the browser's local timezone
scope.schedulerTimeZone = _.find(scope.timeZones, function(x) {
return x.name === scope.current_timezone.name;
});
}
LoadLookupValues(scope);
SetDefaults(scope);
scope.scheduleTimeChange();
scope.scheduleRepeatChange();
};
scope.scheduleTimeChange = function() {
if (scope.schedulerStartDt) {
if (useTimezone) {
scope.resetStartDate();
try {
var dateStr = scope.schedulerStartDt + 'T' + scope.schedulerStartHour + ':' + scope.schedulerStartMinute +
':' + scope.schedulerStartSecond + '.000Z';
scope.schedulerUTCTime = $timezones.toUTC(dateStr, scope.schedulerTimeZone.name).toISOString();
}
catch(e) {
scope.startDateError("Provide a valid start date and time");
}
}
else {
scope.schedulerUTCTime = scope.schedulerStartDt + 'T' + scope.schedulerStartHour + ':' + scope.schedulerStartMinute +
':' + scope.schedulerStartSecond + '.000Z';
}
// Push possible end date values to start date + 1
}
else {
scope.schedulerUTCTime = '';
}
};
scope.scheduleRepeatChange = function() {
if (scope.schedulerFrequency && scope.schedulerFrequency.value !== '' && scope.schedulerFrequency.value !== 'none') {
scope.schedulerInterval = 1;
scope.schedulerShowInterval = true;
scope.schedulerIntervalLabel = scope.schedulerFrequency.intervalLabel;
}
else {
scope.schedulerShowInterval = false;
scope.schedulerEnd = scope.endOptions[0];
}
};
scope.showCalendar = function(fld) {
$('#' + fld).focus();
};
scope.monthlyRepeatChange = function() {
if (scope.monthlyRepeatOption !== 'day') {
$('#monthDay').spinner('disable');
}
else {
$('#monthDay').spinner('enable');
}
};
scope.yearlyRepeatChange = function() {
if (scope.yearlyRepeatOption !== 'month') {
$('#yearlyRepeatDay').spinner('disable');
}
else {
$('#yearlyRepeatDay').spinner('enable');
}
};
scope.setWeekday = function(day) {
// Add or remove day when user clicks checkbox button
var i = scope.weekDays.indexOf(day);
if (i >= 0) {
scope.weekDays.splice(day,1);
}
else {
scope.weekDays.push(day);
}
};
scope.startDateError = function(msg) {
if (scope.scheduler_form) {
scope.scheduler_form_schedulerStartDt_error = msg;
scope.scheduler_form.schedulerStartDt.$pristine = false;
scope.scheduler_form.schedulerStartDt.$dirty = true;
$('#schedulerStartDt').removeClass('ng-pristine').removeClass('ng-valid').removeClass('ng-valid-custom-error')
.addClass('ng-dirty').addClass('ng-invalid').addClass('ng-invalid-custom-error');
}
};
scope.resetStartDate = function() {
if (scope.scheduler_form) {
scope.scheduler_form_schedulerStartDt_error = '';
scope.scheduler_form.schedulerStartDt.$setValidity('custom-error', true);
scope.scheduler_form.schedulerStartDt.$setPristine();
}
};
// When timezones become available, use to set defaults
if (scope.removeZonesReady) {
scope.removeZonesReady();
}
scope.removeZonesReady = scope.$on('zonesReady', function() {
scope.timeZones = JSON.parse(localStorage.zones);
scope.setDefaults();
});
if (useTimezone) {
// Build list of timezone <select> element options
$timezones.getZoneList(scope);
}
else {
scope.setDefaults();
}
return CreateObject(scope);
};
}])
/**
Return an AngularScheduler object we can use to get the RRule result from user input, check if
user input is valid, reset the form, etc. All the things we need to access and manipulate the
scheduler widget
*/
.factory('CreateObject', ['AngularScheduler.useTimezone', '$filter', 'GetRule', 'Inject', 'SetDefaults', '$timezones', 'SetRule',
function(useTimezone, $filter, GetRule, Inject, SetDefaults, $timezones, SetRule) {
return function(scope) {
var fn = function() {
this.scope = scope;
this.useTimezone = useTimezone;
// Evaluate user intput and build options for passing to rrule
this.getOptions = function() {
var options = {};
options.startDate = this.scope.schedulerUTCTime;
options.frequency = this.scope.schedulerFrequency.value;
options.interval = this.scope.schedulerInterval;
if (this.scope.schedulerEnd.value === 'after') {
options.occurrenceCount = this.scope.schedulerOccurrenceCount;
}
if (this.scope.schedulerEnd.value === 'on') {
options.endDate = scope.schedulerEndDt + this.scope.schedulerUTCTime.replace(/^\d{4}-\d{2}-\d{2}/,'');
}
if (this.scope.schedulerFrequency.value === 'weekly') {
options.weekDays = this.scope.weekDays;
}
else if (this.scope.schedulerFrequency.value === 'yearly') {
if (this.scope.yearlyRepeatOption === 'month') {
options.month = this.scope.yearlyMonth.value;
options.monthDay = this.scope.yearlyMonthDay;
}
else {
options.setOccurrence = this.scope.yearlyOccurrence.value;
options.weekDays = this.scope.yearlyWeekDay.value;
options.month = this.scope.yearlyOtherMonth.value;
}
}
else if (this.scope.schedulerFrequency.value === 'monthly') {
if (this.scope.monthlyRepeatOption === 'day') {
options.monthDay = this.scope.monthDay;
}
else {
options.setOccurrence = this.scope.monthlyOccurrence.value;
options.weekDays = this.scope.monthlyWeekDay.value;
}
}
return options;
};
// Clear custom field errors
this.clearErrors = function() {
this.scope.scheduler_weekDays_error = false;
this.scope.scheduler_endDt_error = false;
this.scope.resetStartDate();
this.scope.scheduler_endDt_error = false;
this.scope.scheduler_form.schedulerEndDt.$setValidity('custom-error', true);
this.scope.scheduler_form.schedulerEndDt.$setPristine();
this.scope.scheduler_form.$setPristine();
};
// Check the input form for errors
this.isValid = function() {
var startDt, now, dateStr, adjNow, timeNow, timeFuture, validity = true;
this.clearErrors();
if (this.scope.schedulerFrequency.value === 'weekly' && scope.weekDays.length === 0) {
this.scope.scheduler_weekDays_error = true;
validity = false;
}
if (!this.scope.scheduler_form.schedulerName.$valid) {
// Make sure schedulerName requird error shows up
this.scope.scheduler_form.schedulerName.$dirty = true;
$('#schedulerName').addClass('ng-dirty');
validity = false;
}
if (this.scope.schedulerEnd.value === 'on') {
if (!/^\d{4}-\d{2}-\d{2}$/.test(this.scope.schedulerEndDt)) {
this.scope.scheduler_form.schedulerEndDt.$pristine = false;
this.scope.scheduler_form.schedulerEndDt.$dirty = true;
$('#schedulerEndDt').removeClass('ng-pristine').removeClass('ng-valid').removeClass('ng-valid-custom-error')
.addClass('ng-dirty').addClass('ng-invalid').addClass('ng-invalid-custom-error');
this.scope.scheduler_endDt_error = true;
validity = false;
}
}
if (this.scope.schedulerUTCTime) {
try {
startDt = new Date(this.scope.schedulerUTCTime);
if (!isNaN(startDt)) {
timeFuture = startDt.getTime();
now = new Date();
if (this.useTimezone) {
dateStr = now.getFullYear() + '-' +
$filter('schZeroPad')(now.getMonth() + 1, 2)+ '-' +
$filter('schZeroPad')(now.getDate(),2) + 'T' +
$filter('schZeroPad')(now.getHours(),2) + ':' +
$filter('schZeroPad')(now.getMinutes(),2) + ':' +
$filter('schZeroPad')(now.getSeconds(),2) + '.000Z';
adjNow = $timezones.toUTC(dateStr, this.scope.schedulerTimeZone.name); //Adjust to the selected TZ
timeNow = adjNow.getTime();
}
else {
timeNow = now.getTime();
}
if (timeNow >= timeFuture) {
this.scope.startDateError("Start date and time must be in the future");
validity = false;
}
}
else {
this.scope.startDateError("Invalid start date and time");
validity = false;
}
}
catch(e) {
this.scope.startDateError("Invalid start date and time");
validity = false;
}
}
else {
scope.startDateError("Provide a valid start date and time");
validity = false;
}
return validity;
};
// Returns an rrule object
this.getRule = function() {
var options = this.getOptions();
return GetRule(options);
};
// Return object containing schedule name, string representation of rrule per iCalendar RFC,
// and options used to create rrule
this.getValue = function() {
var rule = this.getRule(),
options = this.getOptions();
return {
name: scope.schedulerName,
rrule: rule.toString(),
options: options
};
};
this.setRRule = function(rule) {
this.clear();
return SetRule(rule, this.scope);
};
// Read in the HTML partial, compile and inject it into the DOM.
// Pass in the target element's id attribute value or an angular.element()
// object.
this.inject = function(element, showButtons) {
return Inject({ scope: this.scope, target: element, buttons: showButtons });
};
// Clear the form, returning all elements to a default state
this.clear = function() {
this.clearErrors();
this.scope.scheduler_form.schedulerName.$setPristine();
this.scope.setDefaults();
};
// Get the user's local timezone
this.getUserTimezone = function() {
return $timezones.getLocal();
}
};
return new fn();
};
}])
.factory('Inject', ['AngularScheduler.partial', '$compile', '$http', '$log', function(scheduler_partial, $compile, $http, $log) {
return function(params) {
var scope = params.scope,
target = params.target,
buttons = params.buttons;
if (scope.removeHtmlReady) {
scope.removeHtmlReady();
}
scope.removeHtmlReady = scope.$on('htmlReady', function(e, data) {
var element = (angular.isObject(target)) ? target : angular.element(document.getElementById(target));
element.html(data);
$compile(element)(scope);
if (buttons) {
$('#scheduler-buttons').show();
}
});
$http({ method: 'GET', url: scheduler_partial })
.success( function(data) {
scope.$emit('htmlReady', data);
})
.error( function(data, status) {
throw('Error reading ' + scheduler_partial + '. ' + status);
//$log.error('Error calling ' + scheduler_partial + '. ' + status);
});
};
}])
.factory('GetRule', ['$log', function($log) {
return function(params) {
// Convert user inputs to an rrule. Returns rrule object using https://github.com/jkbr/rrule
// **list of 'valid values' found below in LoadLookupValues
var startDate = params.startDate, // date object or string in yyyy-MM-ddTHH:mm:ss.sssZ format
frequency = params.frequency, // string, optional, valid value from frequencyOptions
interval = params.interval, // integer, optional
occurrenceCount = params.occurrenceCount, //integer, optional
endDate = params.endDate, // date object or string in yyyy-MM-dd format, optional
// ignored if occurrenceCount provided
month = params.month, // integer, optional, valid value from months
monthDay = params.monthDay, // integer, optional, between 1 and 31
weekDays = params.weekDays, // integer, optional, valid value from weekdays
setOccurrence = params.setOccurrence, // integer, optional, valid value from occurrences
options = {}, i;
if (angular.isDate(startDate)) {
options.dtstart = startDate;
}
else {
try {
options.dtstart = new Date(startDate);
}
catch(e) {
$log.error('Date conversion failed. Attempted to convert ' + startDate + ' to Date. ' + e.message);
}
}
if (frequency && frequency !== 'none') {
options.freq = RRule[frequency.toUpperCase()];
options.interval = interval;
if (weekDays && typeof weekDays === 'string') {
options.byweekday = RRule[weekDays.toUpperCase()];
}
if (weekDays && angular.isArray(weekDays)) {
options.byweekday = [];
for (i=0; i < weekDays.length; i++) {
options.byweekday.push(RRule[weekDays[i].toUpperCase()]);
}
}
if (setOccurrence !== undefined && setOccurrence !== null) {
options.bysetpos = setOccurrence;
}
if (month) {
options.bymonth = month;
}
if (monthDay) {
options.bymonthday = monthDay;
}
if (occurrenceCount) {
options.count = occurrenceCount;
}
else if (endDate) {
if (angular.isDate(endDate)) {
options.until = endDate;
}
else {
try {
options.until = new Date(endDate);
}
catch(e) {
$log.error('Date conversion failed. Attempted to convert ' + endDate + ' to Date. ' + e.message);
}
}
}
}
else {
// We only want to run 1x
options.freq = RRule.DAILY;
options.interval = 1;
options.count = 1;
}
return new RRule(options);
};
}])
.factory('SetRule', ['AngularScheduler.useTimezone', '_', '$log', '$timezones', '$filter',
function(useTimezone, _, $log, $timezones, $filter) {
return function(rule, scope) {
var set, result = '', i,
setStartDate = false;
// Search the set of RRule keys for a particular key, returning its value
function getValue(set, key) {
var pair = _.find(set, function(x) {
var k = x.split(/=/)[0].toUpperCase();
return (k === key);
});
if (pair) {
return pair.split(/=/)[1].toUpperCase();
}
return null;
}
function toWeekDays(days) {
var darray = days.toLowerCase().split(/,/),
match = _.find(scope.weekdays, function(x) {
var warray = (angular.isArray(x.value)) ? x.value : [x.value],
diffA = _.difference(warray, darray),
diffB = _.difference(darray, warray);
return (diffA.length === 0 && diffB.length === 0);
});
return match;
}
function setValue(pair, set) {
var key = pair.split(/=/)[0].toUpperCase(),
value = pair.split(/=/)[1],
days, l, j, dt, month, day, timeString;
if (key === 'NAME') {
//name is not actually part of RRule, but we can handle it just the same
scope.schedulerName = value;
}
if (key === 'FREQ') {
l = value.toLowerCase();
scope.schedulerFrequency = _.find(scope.frequencyOptions, function(opt) {
return opt.value === l;
});
if (!scope.schedulerFrequency || !scope.schedulerFrequency.name) {
result = 'FREQ not found in list of valid options';
}
}
if (key === 'INTERVAL') {
if (parseInt(value,10)) {
scope.schedulerInterval = parseInt(value,10);
scope.schedulerShowInterval = true;
}
else {
result = 'INTERVAL must contain an integer > 0';
}
}
if (key === 'BYDAY') {
if (getValue(set, 'FREQ') === 'WEEKLY') {
days = value.split(/,/);
scope.weekDays = [];
for (j=0; j < days.length; j++) {
if (_.contains(['SU','MO','TU','WE','TH','FR','SA'], days[j])) {
scope.weekDays.push(days[j].toLowerCase());
scope['weekDay' + days[j].toUpperCase() + 'Class'] = 'active'; //activate related button
}
else {
result = 'BYDAY contains unrecognized day value(s)';
}
}
}
else if (getValue(set, 'FREQ') === 'MONTHLY') {
scope.monthlyRepeatOption = 'other';
scope.monthlyWeekDay = toWeekDays(value);
if (!scope.monthlyWeekDay) {
result = 'BYDAY contains unrecognized day value(s)';
}
}
else {
scope.yearlyRepeatOption = 'other';
scope.yearlyWeekDay = toWeekDays(value);
if (!scope.yearlyWeekDay) {
result = 'BYDAY contains unrecognized day value(s)';
}
}
}
if (key === 'BYMONTHDAY') {
if (parseInt(value,10) && parseInt(value,10) > 0 && parseInt(value,10) < 32) {
scope.monthDay = parseInt(value,10);
scope.monhthlyRepeatOption = 'day';
}
else {
result = 'BYMONTHDAY must contain an integer between 1 and 31';
}
}
if (key === 'DTSTART') {
// The form has been reset to the local zone
setStartDate = true;
if (/\d{8}T\d{6}Z/.test(value)) {
// date may come in without separators. add them so new Date constructor will work
value = value.replace(/(\d{4})(\d{2})(\d{2}T)(\d{2})(\d{2})(\d{2}Z)/,
function(match, p1, p2, p3, p4,p5,p6) {
return p1 + '-' + p2 + '-' + p3 + p4 + ':' + p5 + ':' + p6;
});
}
if (useTimezone) {
dt = new Date(value); // date adjusted to local zone automatically
month = $filter('schZeroPad')(dt.getMonth() + 1, 2);
day = $filter('schZeroPad')(dt.getDate(), 2);
scope.schedulerStartDt = dt.getFullYear() + '-' + month + '-' + day;
scope.schedulerStartHour = $filter('schZeroPad')(dt.getHours(),2);
scope.schedulerStartMinute = $filter('schZeroPad')(dt.getMinutes(),2);
scope.schedulerStartSecond = $filter('schZeroPad')(dt.getSeconds(),2);
scope.scheduleTimeChange(); // calc UTC
}
else {
timeString = value.replace(/^.*T/,'');
scope.schedulerStartDt = value.replace(/T.*$/,'');
scope.schedulerStartHour = timeString.substr(0,2);
scope.schedulerStartMinute = timeString.substr(3,2);
scope.schedulerStartMinute = timeString.substr(6,2);
}
scope.scheduleTimeChange();
}
if (key === 'BYSETPOS') {
if (getValue(set, 'FREQ') === 'YEARLY') {
scope.yearlRepeatOption = 'other';
scope.yearlyOccurrence = _.find(scope.occurrences, function(x) {
return (x.value === parseInt(value,10));
});
if (!scope.yearlyOccurrence || !scope.yearlyOccurrence.name) {
result = 'BYSETPOS was not in the set of 1,2,3,4,-1';
}
}
else {
scope.monthlyOccurrence = _.find(scope.occurrences, function(x) {
return (x.value === parseInt(value,10));
});
if (!scope.monthlyOccurrence || !scope.monthlyOccurrence.name) {
result = 'BYSETPOS was not in the set of 1,2,3,4,-1';
}
}
}
if (key === 'COUNT') {
if (parseInt(value,10)) {
scope.schedulerEnd = scope.endOptions[1];
scope.schedulerOccurrenceCount = parseInt(value,10);
}
else {
result = "COUNT must be a valid integer > 0";
}
}
if (key === 'UNTIL') {
if (/\d{8}T\d{6}Z/.test(value)) {
// date may come in without separators. add them so new Date constructor will work
value = value.replace(/(\d{4})(\d{2})(\d{2}T)(\d{2})(\d{2})(\d{2}Z)/,
function(match, p1, p2, p3, p4,p5,p6) {
return p1 + '-' + p2 + '-' + p3 + p4 + ':' + p5 + ':' + p6;
});
}
scope.schedulerEnd = scope.endOptions[2];
if (useTimezone) {
dt = new Date(value); // date adjusted to local zone automatically
month = $filter('schZeroPad')(dt.getMonth() + 1, 2);
day = $filter('schZeroPad')(dt.getDate(), 2);
scope.schedulerEndDt = dt.getFullYear() + '-' + month + '-' + day;
}
else {
scope.schedulerEndDt = value.replace(/T.*$/,'');
}
}
if (key === 'BYMONTH') {
if (getValue(set, 'FREQ') === 'YEARLY' && getValue(set, 'BYDAY')) {
scope.yearlRepeatOption = 'other';
scope.yearlyOtherMonth = _.find(scope.months, function(x) {
return x.value === parseInt(value,10);
});
if (!scope.yearlyOtherMonth || !scope.yearlyOtherMonth.name) {
result = 'BYMONTH must be an integer between 1 and 12';
}
}
else {
scope.yearlyOption = 'month';
scope.yearlyMonth = _.find(scope.months, function(x) {
return x.value === parseInt(value,10);
});
if (!scope.yearlyMonth || !scope.yearlyMonth.name) {
result = 'BYMONTH must be an integer between 1 and 12';
}
}
}
if (key === 'BYMONTHDAY') {
if (parseInt(value,10)) {
scope.yearlyMonthDay = parseInt(value,10);
}
else {
result = 'BYMONTHDAY must be an integer between 1 and 31';
}
}
}
function isValid() {
// Check what was put into scope vars, and see if anything is
// missing or not quite right.
if (scope.schedulerFrequency.name === 'weekly' && scope.weekDays.length === 0) {
result = 'Frequency is weekly, but BYDAYS value is missing.';
}
if (!setStartDate) {
result = 'Warning: start date was not provided';
}
}
if (rule) {
set = rule.split(/;/);
if (angular.isArray(set)) {
for (i=0; i < set.length; i++) {
setValue(set[i], set);
if (result) {
break;
}
}
if (!result) {
isValid();
}
}
else {
result = 'No rule entered. Provide a valid RRule string.';
}
}
else {
result = 'No rule entered. Provide a valid RRule string.';
}
if (result) {
$log.error(result);
}
return result;
};
}])
.factory('SetDefaults', ['$filter', function($filter) {
return function(scope) {
// Set default values
var defaultDate = new Date(),
defaultMonth = $filter('schZeroPad')(defaultDate.getMonth() + 1, 2),
defaultDay = $filter('schZeroPad')(defaultDate.getDate(), 2),
defaultDateStr = defaultDate.getFullYear() + '-' + defaultMonth + '-' + defaultDay;
scope.schedulerName = '';
scope.weekDays = [];
scope.schedulerStartHour = '00';
scope.schedulerStartMinute = '00';
scope.schedulerStartSecond = '00';
scope.schedulerStartDt = defaultDateStr;
scope.schedulerFrequency = scope.frequencyOptions[0];
scope.schedulerShowEvery = false;
scope.schedulerEnd = scope.endOptions[0];
scope.schedulerInterval = 1;
scope.schedulerOccurrenceCount = 1;
scope.monthlyRepeatOption = 'day';
scope.monthDay = 1;
scope.monthlyOccurrence = scope.occurrences[0];
scope.monthlyWeekDay = scope.weekdays[0];
scope.yearlyRepeatOption = 'month';
scope.yearlyMonth = scope.months[0];
scope.yearlyMonthDay = 1;
scope.yearlyWeekDay = scope.weekdays[0];
scope.yearlyOtherMonth = scope.months[0];
scope.yearlyOccurrence = scope.occurrences[0];
scope.weekDayMOClass = '';
scope.weekDayTUClass = '';
scope.weekDayWEClass = '';
scope.weekDayTHClass = '';
scope.weekDayFRClass = '';
scope.weekDaySAClass = '';
scope.weekDaySUClass = '';
};
}])
.factory('LoadLookupValues', [ function() {
return function(scope) {
scope.frequencyOptions = [
{ name: 'None (run once)', value: 'none', intervalLabel: '' },
{ name: 'Minutely', value: 'minutely', intervalLabel: 'minutes' },
{ name: 'Hourly', value: 'hourly', intervalLabel: 'hours' },
{ name: 'Daily', value: 'daily', intervalLabel: 'days' },
{ name: 'Weekly', value: 'weekly', intervalLabel: 'weeks' },
{ name: 'Monthly', value: 'monthly', intervalLabel: 'months' },
{ name: 'Yearly', value: 'yearly', intervalLabel: 'years' }
];
scope.endOptions = [
{ name: 'Never', value: 'never' },
{ name: 'After', value: 'after' },
{ name: 'On Date', value: 'on' }
];
scope.occurrences = [
{ name: 'first', value: 1 },
{ name: 'second', value: 2 },
{ name: 'third', value: 3 },
{ name: 'fourth', value: 4 },
{ name: 'last', value: -1 }
];
scope.weekdays = [
{ name: 'Sunday', value: 'su' },
{ name: 'Monday', value: 'mo' },
{ name: 'Tueday', value: 'tu' },
{ name: 'Wednesday', value: 'we' },
{ name: 'Thursday', value: 'th' },
{ name: 'Friday', value: 'fr' },
{ name: 'Saturday', value: 'sa' },
{ name: 'Day', value: ['mo', 'tu', 'we', 'th', 'fr', 'sa', 'su'] },
{ name: 'Weekday', value: ['mo', 'tu', 'we', 'th', 'fr'] },
{ name: 'Weekend day', value: ['sa', 'su'] }
];
scope.months = [
{ name: 'January', value: 1 },
{ name: 'February', value: 2 },
{ name: 'March', value: 3 },
{ name: 'April', value: 4 },
{ name: 'May', value: 5 },
{ name: 'June', value: 6 },
{ name: 'July', value: 7 },
{ name: 'August', value: 8 },
{ name: 'September', value: 9 },
{ name: 'October', value: 10 },
{ name: 'November', value: 11 },
{ name: 'December', value: 12 }
];
};
}])
// $filter('afZeroPad')(n, pad) -- or -- {{ n | afZeroPad:pad }}
.filter('schZeroPad', [ function() {
return function (n, pad) {
var str = (Math.pow(10,pad) + '').replace(/^1/,'') + (n + '').trim();
return str.substr(str.length - pad);
};
}])
.directive('schTooltip', [ function() {
return {
link: function(scope, element, attrs) {
var placement = (attrs.placement) ? attrs.placement : 'top';
$(element).tooltip({
html: true,
placement: placement,
title: attrs.afTooltip,
trigger: 'hover',
container: 'body'
});
}
};
}])
.directive('schDatePicker', [ function() {
return {
require: 'ngModel',
link: function(scope, element, attrs) {
var options = {},
variable = attrs.ngModel,
defaultDate = new Date();
options.dateFormat = attrs.dateFormat || 'yy-mm-dd';
options.defaultDate = scope[variable];
options.minDate = (attrs.minToday) ? defaultDate : null;
options.maxDate = (attrs.maxDate) ? new Date(attrs('maxDate')) : null;
options.changeMonth = (attrs.changeMonth === "false") ? false : true;
options.changeYear = (attrs.changeYear === "false") ? false : true;
$(element).datepicker(options);
}
};
}])
// Custom directives
.directive('schSpinner', ['$filter', function($filter) {
return {
require: 'ngModel',
link: function(scope, element, attr, ctrl) {
// Add jquerui spinner to 'spinner' type input
var form = attr.schSpinner,
zeroPad = attr.zeroPad;
$(element).spinner({
stop: function() {
if (zeroPad) {
scope[attr.ngModel] = $filter('schZeroPad')($(this).val(),zeroPad);
$(this).val(scope[attr.ngModel]);
}
else {
scope[attr.ngModel] = $(this).spinner('value');
}
if (attr.ngChange) {
scope.$apply(scope[attr.ngChange]);
}
},
spin: function() {
scope[form].$setDirty();
ctrl.$dirty = true;
ctrl.$pristine = false;
if (!scope.$$phase) {
scope.$digest();
}
}
});
}
};
}]);

View File

@ -0,0 +1 @@
.ui-widget input{font-size:12px;font-weight:400;text-align:center}.ui-spinner.ui-widget-content{border-bottom-color:#ccc;border-top-color:#ccc;border-left-color:#ccc;border-right-color:#ccc}.ui-spinner-button{border-left-color:#ccc;border-left-style:solid;border-left-width:1px}.scheduler-time-spinner{width:40px;height:24px}.scheduler-spinner{width:50px;height:24px}.fmt-help{font-size:12px;font-weight:400;color:#999;padding-left:10px}.error{color:#dd1b16;font-size:12px;margin-bottom:0;margin-top:0;padding-top:3px}.pull-up{margin-top:-15px;margin-bottom:10px}.red-text{color:#dd1b16}input.ng-dirty.ng-invalid,select.ng-dirty.ng-invalid,textarea.ng-dirty.ng-invalid{border:1px solid red;outline:0}.help-text{font-size:12px;font-weight:400;color:#999;margin-top:5px}.inline-label{margin-left:10px}#scheduler-buttons{margin-top:20px}.no-label{padding-top:25px}.padding-top-slim{padding-top:5px}.option-pad-left{padding-left:15px}.option-pad-top{padding-top:15px}.option-pad-bottom{padding-bottom:15px}#monthlyOccurrence,#monthlyWeekDay{margin-top:5px}select{width:100%}

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,244 @@
#!/usr/bin/env node
var util = require('util'),
http = require('http'),
fs = require('fs'),
url = require('url'),
events = require('events');
var DEFAULT_PORT = 8000;
function main(argv) {
new HttpServer({
'GET': createServlet(StaticServlet),
'HEAD': createServlet(StaticServlet)
}).start(Number(argv[2]) || DEFAULT_PORT);
}
function escapeHtml(value) {
return value.toString().
replace('<', '&lt;').
replace('>', '&gt;').
replace('"', '&quot;');
}
function createServlet(Class) {
var servlet = new Class();
return servlet.handleRequest.bind(servlet);
}
/**
* An Http server implementation that uses a map of methods to decide
* action routing.
*
* @param {Object} Map of method => Handler function
*/
function HttpServer(handlers) {
this.handlers = handlers;
this.server = http.createServer(this.handleRequest_.bind(this));
}
HttpServer.prototype.start = function(port) {
this.port = port;
this.server.listen(port);
util.puts('Http Server running at http://localhost:' + port + '/');
};
HttpServer.prototype.parseUrl_ = function(urlString) {
var parsed = url.parse(urlString);
parsed.pathname = url.resolve('/', parsed.pathname);
return url.parse(url.format(parsed), true);
};
HttpServer.prototype.handleRequest_ = function(req, res) {
var logEntry = req.method + ' ' + req.url;
if (req.headers['user-agent']) {
logEntry += ' ' + req.headers['user-agent'];
}
util.puts(logEntry);
req.url = this.parseUrl_(req.url);
var handler = this.handlers[req.method];
if (!handler) {
res.writeHead(501);
res.end();
} else {
handler.call(this, req, res);
}
};
/**
* Handles static content.
*/
function StaticServlet() {}
StaticServlet.MimeMap = {
'txt': 'text/plain',
'html': 'text/html',
'css': 'text/css',
'xml': 'application/xml',
'json': 'application/json',
'js': 'application/javascript',
'jpg': 'image/jpeg',
'jpeg': 'image/jpeg',
'gif': 'image/gif',
'png': 'image/png',
  'svg': 'image/svg+xml'
};
StaticServlet.prototype.handleRequest = function(req, res) {
var self = this;
var path = ('./' + req.url.pathname).replace('//','/').replace(/%(..)/g, function(match, hex){
return String.fromCharCode(parseInt(hex, 16));
});
var parts = path.split('/');
if (parts[parts.length-1].charAt(0) === '.')
return self.sendForbidden_(req, res, path);
fs.stat(path, function(err, stat) {
if (err)
return self.sendMissing_(req, res, path);
if (stat.isDirectory())
return self.sendDirectory_(req, res, path);
return self.sendFile_(req, res, path);
});
}
StaticServlet.prototype.sendError_ = function(req, res, error) {
res.writeHead(500, {
'Content-Type': 'text/html'
});
res.write('<!doctype html>\n');
res.write('<title>Internal Server Error</title>\n');
res.write('<h1>Internal Server Error</h1>');
res.write('<pre>' + escapeHtml(util.inspect(error)) + '</pre>');
util.puts('500 Internal Server Error');
util.puts(util.inspect(error));
};
StaticServlet.prototype.sendMissing_ = function(req, res, path) {
path = path.substring(1);
res.writeHead(404, {
'Content-Type': 'text/html'
});
res.write('<!doctype html>\n');
res.write('<title>404 Not Found</title>\n');
res.write('<h1>Not Found</h1>');
res.write(
'<p>The requested URL ' +
escapeHtml(path) +
' was not found on this server.</p>'
);
res.end();
util.puts('404 Not Found: ' + path);
};
StaticServlet.prototype.sendForbidden_ = function(req, res, path) {
path = path.substring(1);
res.writeHead(403, {
'Content-Type': 'text/html'
});
res.write('<!doctype html>\n');
res.write('<title>403 Forbidden</title>\n');
res.write('<h1>Forbidden</h1>');
res.write(
'<p>You do not have permission to access ' +
escapeHtml(path) + ' on this server.</p>'
);
res.end();
util.puts('403 Forbidden: ' + path);
};
StaticServlet.prototype.sendRedirect_ = function(req, res, redirectUrl) {
res.writeHead(301, {
'Content-Type': 'text/html',
'Location': redirectUrl
});
res.write('<!doctype html>\n');
res.write('<title>301 Moved Permanently</title>\n');
res.write('<h1>Moved Permanently</h1>');
res.write(
'<p>The document has moved <a href="' +
redirectUrl +
'">here</a>.</p>'
);
res.end();
util.puts('301 Moved Permanently: ' + redirectUrl);
};
StaticServlet.prototype.sendFile_ = function(req, res, path) {
var self = this;
var file = fs.createReadStream(path);
res.writeHead(200, {
'Content-Type': StaticServlet.
MimeMap[path.split('.').pop()] || 'text/plain'
});
if (req.method === 'HEAD') {
res.end();
} else {
file.on('data', res.write.bind(res));
file.on('close', function() {
res.end();
});
file.on('error', function(error) {
self.sendError_(req, res, error);
});
}
};
StaticServlet.prototype.sendDirectory_ = function(req, res, path) {
var self = this;
if (path.match(/[^\/]$/)) {
req.url.pathname += '/';
var redirectUrl = url.format(url.parse(url.format(req.url)));
return self.sendRedirect_(req, res, redirectUrl);
}
fs.readdir(path, function(err, files) {
if (err)
return self.sendError_(req, res, error);
if (!files.length)
return self.writeDirectoryIndex_(req, res, path, []);
var remaining = files.length;
files.forEach(function(fileName, index) {
fs.stat(path + '/' + fileName, function(err, stat) {
if (err)
return self.sendError_(req, res, err);
if (stat.isDirectory()) {
files[index] = fileName + '/';
}
if (!(--remaining))
return self.writeDirectoryIndex_(req, res, path, files);
});
});
});
};
StaticServlet.prototype.writeDirectoryIndex_ = function(req, res, path, files) {
path = path.substring(1);
res.writeHead(200, {
'Content-Type': 'text/html'
});
if (req.method === 'HEAD') {
res.end();
return;
}
res.write('<!doctype html>\n');
res.write('<title>' + escapeHtml(path) + '</title>\n');
res.write('<style>\n');
res.write(' ol { list-style-type: none; font-size: 1.2em; }\n');
res.write('</style>\n');
res.write('<h1>Directory: ' + escapeHtml(path) + '</h1>');
res.write('<ol>');
files.forEach(function(fileName) {
if (fileName.charAt(0) !== '.') {
res.write('<li><a href="' +
escapeHtml(fileName) + '">' +
escapeHtml(fileName) + '</a></li>');
}
});
res.write('</ol>');
res.end();
};
// Must be last,
main(process.argv);

View File

@ -0,0 +1,46 @@
{
"name": "angular-tz-extensions",
"version": "0.3.9",
"main": "js/angular-timezones.js",
"ignore": [
".bowerrc",
".idea",
".gitignore",
".travis.yml",
"index.html",
"js/demo.js",
"package.json",
"test",
"**/.*",
"node_modules",
"bower_components",
"tests"
],
"dependencies": {
"angular": "*",
"jquery": "*",
"angular-filters": "*",
"timezone-js": "git@github.com:mde/timezone-js.git#v0.4.4"
},
"homepage": "https://github.com/chouseknecht/angular-tz-extensions",
"description": "Add timezone information to a js date object, get local browser timezone info, and get a list of available timezones from the iana tz database.",
"keywords": [
"timezone",
"date",
"iana",
"tzdata"
],
"authors": [
"Chris Houseknecht"
],
"license": "MIT",
"_release": "0.3.9",
"_resolution": {
"type": "version",
"tag": "0.3.9",
"commit": "dded062e72274e2fc5379bb251c476d152ecc1a1"
},
"_source": "git://github.com/chouseknecht/angular-tz-extensions.git",
"_target": "*",
"_originalSource": "angular-tz-extensions"
}

View File

@ -0,0 +1,32 @@
module.exports = function(grunt) {
grunt.initConfig({
pkg: grunt.file.readJSON('./package.json'),
jshint: {
options: {
jshintrc: '.jshintrc'
},
uses_defaults: ['lib/angular-tz-extensions.js']
},
uglify: {
options: {
banner: '/*! <%= pkg.name %> - v<%= pkg.version %> - ' +
'<%= grunt.template.today("yyyy-mm-dd") %> */'
},
my_target: {
files: {
'lib/angular-tz-extensions.min.js': ['lib/angular-tz-extensions.js']
}
}
}
});
grunt.loadNpmTasks('grunt-contrib-jshint');
grunt.loadNpmTasks('grunt-contrib-uglify');
grunt.registerTask('default', ['jshint', 'uglify']);
}

View File

@ -0,0 +1,20 @@
The MIT License (MIT)
Copyright (c) 2013 Michael Ahlers
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@ -0,0 +1,137 @@
# Angular TZ Extensions
Javascript is good at creating dates in the local timezone, and it provides a reasonable set of methods for returning date and time information within the local timezone and UTC. But what if you want a date object aligned to a timezone other than the local one? What if you need to present the user with a list of timezone choices? What if you need to know the offset or abbreviation for a particular timezone? Angular TZ Extensions provides the solution.
Originally forked from https://github.com/michaelahlers/angular-timezones.
Depends on https://github.com/mde/timezone-js, http://pellepim.bitbucket.org/jstz/ and [AngularJS](http://angularjs.org).
## Install
Install using [Bower](https://github.com/bower/bower):
bower install angular-tz-extensions
Once installed include the follwing scripts in your app:
<script type="text/javascript" src="/bower_components/timezone-js/src/date.js"></script>
<script type="text/javascript" src="/bower_components/angular-tz-extensions/lib/angular-tz-extensions.js"></script>
If you want to detect the local timezone, include the jstimezonedetect. You can use the package found in this repo:
<script type="text/javascript" src="/packages/jstimezonedetect/jstz.min.js"></script>
Or, pull it from a CDN:
<script type="text/javascript" src="http://cdnjs.cloudflare.com/ajax/libs/jstimezonedetect/1.0.4/jstz.js"></script>
## Usage
After including `angular-timezones.js`, add this package to your application.
var yourApplication = angular.module('your-application', ['Timezones'])
### Configuration
This package provides the [IANA timezone data](http://iana.org/time-zones), but you may have this resource served from a different location or you may wish to provide your own data. To change that location, set the `$timezones.definitions.location` property to the appropriate path or URL.
yourApplication.constant('$timezones.definitions.location', '/custom/path/to/tz/data')
This is done by the unit tests and illustrated in the included sample app (see Examples below).
### Align date to a given timezone
Use `$timezones.align(timezone, date)` to align a date object to a timezone represeneted as an Olsan timezone string value. The getFullYear, getMonth, getDate, getHours, getMinutes, getSeconds and getTimezone methods of the returned date object will present values in the requested timezone.
Below is an example comparing a date object aligned to the local timezone (America/New_York) with a date object created using the align method a timezone of 'America/Los_Angeles':
var rightNow = new Date();
console.log(rightNow.getTimezoneOffset());
console.log(rightNow.getHours());
console.log($filter('date')(rightNow,"yyyy-MM-dd HH:mm:ss Z"));
var test = $timezones.align(rightNow, 'America/Los_Angeles');
console.log(test.timezone);
console.log(test.getTimezoneOffset());
console.log(test.getHours());
console.log($filter('date')(test,"yyyy-MM-dd HH:mm:ss Z"));
Results in:
300
14
2014-03-03 14:40:33 -0500
America/Los_Angeles
480
11
2014-03-03 11:40:33 -0800
Note that TimezoneJS (timezone-js/src/date.js) adds additional properties and methods to the date object. Here we're accessing the timezone property. There is also a getTimezoneInfo() method. See TimezoneJS documentation for more details.
Alignment can also be accomplished at the view level using the provided tzAlign filter:
{{ someDate|tzAlign:'America/Los_Angeles'|date:"yyyy-MM-dd HH:mm:ss Z" }}
### Resolve a timezone
The `$timezones.resolve(timezone, reference)` function will, using the reference `Date` provided, look up complete details about the timezone&mdash;including the abbreviation, offset, and decomposed region and locality. This is useful for avoiding clever tricks to extract abbreviations from `Date.toString` (which is not particularly portable or robust). Additionally, resulting values are derived from the authoritative IANA timezone data.
var scope.timezone = $timezone.resolve('America/New_York', new Date());
console.log(scope.timezone);
Returns:
{
abbreviation: 'EST',
locality: 'New York',
name: 'America/New_York',
offset: 300,
region: 'America'
}
### Detect local timezone
If [jsTimezoneDetect](https://bitbucket.org/pellepim/jstimezonedetect) is included, the `$timezones.getLocal()` function will detect the browser's local timezone and provide a complete definition that's resolved against the IANA database. For convenience, jsTimezoneDetect is included in packages/jstimezonedetect. You may want to pull the latest version in from bitbucket.
### Get a list of available timezones
You can retrieve an array of all available timezones- perfect for populating a select element. Use the `$timezone.getZoneList($scope)` method, passing in a scope instance. The method reads the zone.tab tab file, which is part of tzdata. When the data is ready, the method emits 'zonesReady'. Retrieve the data inside `$scope.$on('zonesReady', callback)`. The data will be available in local storage and can be accessed using: `JSON.parse(localStorage.zones)`. Here's an example taken from the included sample app:
if ($scope.removeZonesReady) {
$scope.removeZonesReady();
}
$scope.removeZonesReady = $scope.$on('zonesReady', function() {
var i;
$scope.zones = JSON.parse(localStorage.zones);
$scope.current_timezone = $timezones.getLocal();
for (i=0; i < $scope.zones.length; i++) {
if ($scope.zones[i].name === $scope.current_timezone.name) {
$scope.selectedZone = $scope.zones[i];
break;
}
}
});
$timezones.getZoneList($scope);
## Examples
A sample application is included. Run it locally using [Node](http://nodejs.org):
node ./scripts/web-server.js
Once running, point your browser to http://localhost:8000/app/filters.html
## Developers
_Timezones for Angular_ is tested with [Karma](http://karma-runner.github.io/) and [PhantomJS](http://phantomjs.org/)
With [NPM](http://npmjs.com/) installed, you can test your modifications with the following.
npm install
npm test
To run the tests you will need to have phantomjs in your PATH. Install it globally using `npm install -g phantomjs` or manually add it to your path using something like `export PATH=$PATH:./node_modules/phantomjs/bin/phantomjs`

View File

@ -0,0 +1,92 @@
<!DOCTYPE html>
<html ng-app="application">
<head>
<meta charset="utf-8">
<title>Filters - Timezones for AngularJS</title>
<script type="text/javascript" src="/bower_components/jquery/dist/jquery.min.js"></script>
<script type="text/javascript" src="/bower_components/angular/angular.min.js"></script>
<script type="text/javascript" src="/bower_components/angular-filters/dist/angular-filters.min.js"></script>
<script type="text/javascript" src="/bower_components/timezone-js/src/date.js"></script>
<script type="text/javascript" src="/packages/jstimezonedetect/jstz.min.js"></script>
<script type="text/javascript" src="/lib/angular-tz-extensions.js"></script>
<script type="text/javascript" src="/app/js/filters.js"></script>
<style>
.demo {
margin-top: 30px;
}
.zone-table {
margin-top: 20px;
}
.zone-table tr td {
padding-left: 10px;
padding-right: 10px;
}
</style>
</head>
<body>
<div ng-controller="World">
<h1>Available Time Zones</h1>
<select ng-model="selectedZone" ng-change="zoneChange()" ng-options="z.name for z in zones">
<option value="">--Choose your timezone --</options>
<select>
<table class="zone-table">
<thead>
<tr>
<th>Abbreviation</th>
<th>Locality</th>
<th>Name</th>
<th>Offset</th>
<th>Region</th>
</tr>
</thead>
<tbody>
<tr>
<td>{{ current_timezone.abbreviation }}</td>
<td>{{ current_timezone.locality }}</td>
<td>{{ current_timezone.name }}</td>
<td>{{ current_timezone.offset }}</td>
<td>{{ current_timezone.region }}</td>
</td>
</tbody>
</table>
<div class="demo">
<h1>Filters</h1>
<h2><code>tzDate</code></h2>
<p>
The local time, {{now|date:"yyyy-MM-dd HH:mm:ss Z"}}, shown around the world.
</p>
<table>
<thead>
<tr>
<th>Timezone</th>
<th>Time</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="example in examples">
<td>{{example.timezone}}</td>
<td>{{example.reference|tzAlign:example.timezone|date:"yyyy-MM-dd HH:mm:ss Z"}}</td>
</tr>
</tbody>
</table>
</div>
</div>
</body>
</html>

View File

@ -0,0 +1,63 @@
(function (angular) {
var application = angular.module('application', ['Timezones']);
application.constant('$timezones.definitions.location', '/tz/data');
application.controller('World', ['$scope', '$timezones', '$filter',
function ($scope, $timezones, $filter) {
var now = $scope.now = Date.now();
if ($scope.removeZonesReady) {
$scope.removeZonesReady();
}
$scope.removeZonesReady = $scope.$on('zonesReady', function() {
var i;
$scope.zones = JSON.parse(localStorage.zones);
$scope.current_timezone = $timezones.getLocal();
for (i=0; i < $scope.zones.length; i++) {
if ($scope.zones[i].name === $scope.current_timezone.name) {
$scope.selectedZone = $scope.zones[i];
break;
}
}
});
$scope.zoneChange = function() {
var date = new Date();
$scope.current_timezone = $timezones.resolve($scope.selectedZone.name, date);
};
$timezones.getZoneList($scope);
$scope.examples = [{
timezone: 'Pacific/Honolulu',
reference: now
}, {
timezone: 'America/Los_Angeles',
reference: now
}, {
timezone: 'America/Chicago',
reference: now
}, {
timezone: 'America/New_York',
reference: now
}, {
timezone: 'Europe/Berlin',
reference: now
}, {
timezone: 'Asia/Tokyo',
reference: now
}, {
timezone: 'Australia/Sydney',
reference: now
}, {
timezone: 'Etc/GMT+12',
reference: now
}];
}
]);
})(angular);

View File

@ -0,0 +1,37 @@
{
"name": "angular-tz-extensions",
"version": "0.3.9",
"main": "js/angular-timezones.js",
"ignore": [
".bowerrc",
".idea",
".gitignore",
".travis.yml",
"index.html",
"js/demo.js",
"package.json",
"test",
"**/.*",
"node_modules",
"bower_components",
"tests"
],
"dependencies": {
"angular": "*",
"jquery": "*",
"angular-filters": "*",
"timezone-js": "git@github.com:mde/timezone-js.git#v0.4.4"
},
"homepage": "https://github.com/chouseknecht/angular-tz-extensions",
"description": "Add timezone information to a js date object, get local browser timezone info, and get a list of available timezones from the iana tz database.",
"keywords": [
"timezone",
"date",
"iana",
"tzdata"
],
"authors": [
"Chris Houseknecht"
],
"license": "MIT"
}

View File

@ -0,0 +1,283 @@
/**
* angular-tz-extensions.js
*
* Copyright (c) 2014 Ansible, Inc.
*
* Distributed under The MIT License (MIT).
*
*/
(function (root) {
var angular = root.angular,
timezoneJS = root.timezoneJS,
jstz = root.jstz,
toExtendedNative = function (wrapped) {
/* Tricks the isDate method in Angular into treating these objects like it
* would any other Date. May be horribly slow. */
var key, native = new Date();
for (key in wrapped) {
native[key] = wrapped[key];
}
return native;
},
module = angular.module('Timezones', []);
module.config(function () {
timezoneJS.fromLocalString = function (str, tz) {
// https://github.com/csnover/js-iso8601/blob/master/iso8601.js MIT license
//var minutesOffset = 0,
var struct = /^(\d{4}|[+\-]\d{6})(?:-(\d{2})(?:-(\d{2}))?)?(?:T(\d{1,2}):(\d{1,2})(?::(\d{1,2})(?:\.(\d{3}))?)?(?:(Z)|([+\-])(\d{2})(?::(\d{2}))?)?)?$/.exec(str),
numericKeys = [1, 4, 5, 6, 7, 10, 11],
i, k;
// avoid NaN timestamps caused by “undefined” values being passed to Date.UTC
for (i = 0;(k = numericKeys[i]); ++i) {
struct[k] = +struct[k] || 0;
}
// allow undefined days and months
struct[2] = (+struct[2] || 1) - 1;
struct[3] = +struct[3] || 1;
return toExtendedNative(new timezoneJS.Date(struct[1], struct[2], struct[3], struct[4], struct[5], struct[6], struct[7], tz));
};
});
module.constant('$timezones.definitions.location', '/tz/data');
module.run(['$timezones.definitions.location',
function (location) {
timezoneJS.timezone.zoneFileBasePath = location;
timezoneJS.timezone.init({ async: false });
}
]);
module.factory('$timezones',['$log', '$http', '$timezones.definitions.location', function ($log, $http, location) {
var resolve = function (timezone, reference) {
var name, result;
if ('number' === typeof (reference)) {
reference = new Date(reference);
}
/*
* TODO: Support resolution without reference dates.
*
* For now, we must use reference dates. There's just not enough time
* to write and test code that would resolve all possible definitions
* for any given timezone. Hopefully the TimezoneJS folks will support
* that some day.
*/
if (!angular.isDate(reference)) {
throw {
name : 'DateObjectExpected',
message : 'Expected a Date object; got "' + reference + '".'
};
}
/* This is not terribly efficient, but necessary because some timezone
* specifics (like the abbreviation and offset) are temporal. */
reference = new timezoneJS.Date(reference, timezone);
name = reference.getTimezone();
result = {
name: name,
abbreviation: reference.getTimezoneAbbreviation(),
offset: reference.getTimezoneOffset(),
region: name.split('/')[0],
locality: name.split('/')[1].replace('_', ' ')
};
return result;
};
return {
/**
* Aligns the provided Date object to the specified timezone.
*
* @param date
* Reference date.
*
* @param timezone
* An Olson name (e.g., America/New_York), or a timezone object
* (produced by the resolve function).
*
* @returns {*} A Date "aligned" to the desired timezone.
*/
align: function (date, timezone, silent) {
if (!angular.isDate(date)) {
throw {
name : 'DateObjectExpected',
message : 'Expected a Date object; got "' + date + '".'
};
}
// Lining up code with tests. If a bogus timezone is passed in and mode is silent, don't
// throw an error, return a date.
try {
if (angular.isObject(timezone) && timezone.name) {
return toExtendedNative(new timezoneJS.Date(date, timezone.name));
}
if (angular.isString(timezone)) {
return toExtendedNative(new timezoneJS.Date(date, timezone));
}
}
catch(e) {
if (true === silent) {
return date;
}
else {
throw new Error('The timezone argument must either be an Olson name (e.g., America/New_York), ' +
'or a timezone object (produced by the resolve function) bearing an Olson name on the name property.');
}
}
},
/**
* Generate an object that defines the timezone.
*
* @param timezone
* An Olson name (e.g., America/New_York) to resolve.
*
* @param reference
* A reference date used to determine values for temporal timezone
* properties like the offset and abbreviation (which vary between
* standard and daylight times).
*
* @returns {{name: string, abbreviation: string, offset: number, region: string, locality: string}}
*/
resolve: resolve,
/**
* If the jsTimezoneDetect library is available, use it to make an
* approximate guess at the current timezone Olson name. From there,
* perform resolution as usual (which implicitly gets the authoritative
* definitions from the IANA database).
*/
getLocal: function () {
var name, now;
if ('undefined' === typeof(jstz) || 'function' !== typeof(jstz.determine)) {
throw {
name : 'JSTZLibraryMissing',
message : 'The jsTimezoneDetect library, available at https://bitbucket.org/pellepim/jstimezonedetect, is required to detect the local timezone.'
};
}
name = jstz.determine().name();
now = new Date();
return resolve(name, now);
},
/**
* Given a date object or date string (in UTC format) and an Olson name, align the date object
* and add the offset minutes to the datetime value. The getUTC*(), toUTString() and getTime()
* methods on the resulting date object will return values that include the timezone offset
* minutes.
*
* Note: Don't pass in the result of new Date(), as it automatically adjusts the time to the
* local timezone. Pass in a string or object that has NOT already been adjusted or set relative
* to a specific zone.
*/
toUTC: function(dt, tz) {
var dateObj = (angular.isDate(dt)) ? dt : new Date(dt),
dateStr, tzDate;
// Make sure the date we're using is not already adjusted to the local
// timezone
dateStr = dateObj.getUTCFullYear() + '-' +
('00' + (dateObj.getUTCMonth() + 1)).substr(-2,2) + '-' +
('00' + dateObj.getUTCDate()).substr(-2,2) + 'T' +
('00' + dateObj.getUTCHours()).substr(-2,2) + ':' +
('00' + dateObj.getUTCMinutes()).substr(-2,2) + ':' +
('00' + dateObj.getUTCSeconds()).substr(-2,2) + '.000Z';
tzDate = this.align(new Date(dateStr), tz);
return new Date(tzDate.getTime() + (tzDate.getTimezoneOffset() * 60000));
},
/**
* Reads the zone.tab file and builds an array of objects: { name: <Olsan name> }.
* The array contains an object for every Olsan name. Use the array for populating
* <select> lists. Pass in $scope. When the list is ready 'zonesReady' will be
* emitted to the $scope. Catch using $scope.$on('zonesReady', function(e, zones) {...}),
* where zones is a pointer to the array.
*/
getZoneList: function(scope) {
var zones = [], sorted;
if (localStorage.zones) {
scope.$emit('zonesReady');
}
else {
$http({ method: 'GET', url: location + '/zone.tab'})
.success(function(data) {
var i, fields, lines = data.match(/[^\r\n]+/g);
for (i=0; i < lines.length; i++) {
if (!/^#/.test(lines[i])) {
fields = lines[i].split(/\s+/);
zones.push(fields[2]);
}
}
sorted = zones.sort();
zones = [];
for (i=0; i < sorted.length; i++) {
zones.push({ name: sorted[i] });
}
localStorage.zones = JSON.stringify(zones);
scope.$emit('zonesReady');
})
.error(function() {
$log.error('Failed to load ' + location + '/zone.tab');
});
}
}
};
}]);
module.filter('tzAlign', ['$timezones', function ($timezones) {
return function (date, timezone) {
if (!(angular.isDate(date) || angular.isNumber(date) || angular.isString(date)) || !(angular.isString(timezone) || angular.isObject(timezone))) {
return date;
}
var alignedDate, milliseconds, verifiedDate = date;
if (angular.isNumber(date)) {
verifiedDate = new Date(date);
} else if (angular.isString(date)) {
milliseconds = parseInt(date);
if (!angular.isNumber(milliseconds)) {
milliseconds = Date.parse(date);
}
verifiedDate = new Date(milliseconds);
}
if (!angular.isDate(verifiedDate) || isNaN(verifiedDate.getTime())) {
return date;
}
try {
alignedDate = $timezones.align(verifiedDate, timezone, true);
}
catch(e) {
return date;
}
if (!alignedDate || !alignedDate.getTime || isNaN(alignedDate.getTime())) {
return date;
}
return alignedDate;
};
}]);
})(this);

View File

@ -0,0 +1 @@
/*! angular-tz-extensions - v0.0.1 - 2014-03-03 */!function(a){var b=a.angular,c=a.timezoneJS,d=a.jstz,e=function(a){var b,c=new Date;for(b in a)c[b]=a[b];return c},f=b.module("Timezones",[]);f.config(function(){c.fromLocalString=function(a,b){var d,f,g=/^(\d{4}|[+\-]\d{6})(?:-(\d{2})(?:-(\d{2}))?)?(?:T(\d{1,2}):(\d{1,2})(?::(\d{1,2})(?:\.(\d{3}))?)?(?:(Z)|([+\-])(\d{2})(?::(\d{2}))?)?)?$/.exec(a),h=[1,4,5,6,7,10,11];for(d=0;f=h[d];++d)g[f]=+g[f]||0;return g[2]=(+g[2]||1)-1,g[3]=+g[3]||1,e(new c.Date(g[1],g[2],g[3],g[4],g[5],g[6],g[7],b))}}),f.constant("$timezones.definitions.location","/tz/data"),f.run(["$timezones.definitions.location",function(a){c.timezone.zoneFileBasePath=a,c.timezone.init({async:!1})}]),f.factory("$timezones",["$log","$http","$timezones.definitions.location",function(a,f,g){var h=function(a,d){var e,f;if("number"==typeof d&&(d=new Date(d)),!b.isDate(d))throw{name:"DateObjectExpected",message:'Expected a Date object; got "'+d+'".'};return d=new c.Date(d,a),e=d.getTimezone(),f={name:e,abbreviation:d.getTimezoneAbbreviation(),offset:d.getTimezoneOffset(),region:e.split("/")[0],locality:e.split("/")[1].replace("_"," ")}};return{align:function(a,d,f){if(!b.isDate(a))throw{name:"DateObjectExpected",message:'Expected a Date object; got "'+a+'".'};try{if(b.isObject(d)&&d.name)return e(new c.Date(a,d.name));if(b.isString(d))return e(new c.Date(a,d))}catch(g){if(!0===f)return a;throw new Error("The timezone argument must either be an Olson name (e.g., America/New_York), or a timezone object (produced by the resolve function) bearing an Olson name on the name property.")}},resolve:h,getLocal:function(){var a,b;if("undefined"==typeof d||"function"!=typeof d.determine)throw{name:"JSTZLibraryMissing",message:"The jsTimezoneDetect library, available at https://bitbucket.org/pellepim/jstimezonedetect, is required to detect the local timezone."};return a=d.determine().name(),b=new Date,h(a,b)},toUTC:function(a,c){var d,e,f=b.isDate(a)?a:new Date(a);return d=f.getUTCFullYear()+"-"+("00"+(f.getUTCMonth()+1)).substr(-2,2)+"-"+("00"+f.getUTCDate()).substr(-2,2)+"T"+("00"+f.getUTCHours()).substr(-2,2)+":"+("00"+f.getUTCMinutes()).substr(-2,2)+":"+("00"+f.getUTCSeconds()).substr(-2,2)+".000Z",e=this.align(new Date(d),c),new Date(e.getTime()+6e4*e.getTimezoneOffset())},getZoneList:function(b){var c,d=[];localStorage.zones?b.$emit("zonesReady"):f({method:"GET",url:g+"/zone.tab"}).success(function(a){var e,f,g=a.match(/[^\r\n]+/g);for(e=0;e<g.length;e++)/^#/.test(g[e])||(f=g[e].split(/\s+/),d.push(f[2]));for(c=d.sort(),d=[],e=0;e<c.length;e++)d.push({name:c[e]});localStorage.zones=JSON.stringify(d),b.$emit("zonesReady")}).error(function(){a.error("Failed to load "+g+"/zone.tab")})}}}]),f.filter("tzAlign",["$timezones",function(a){return function(c,d){if(!(b.isDate(c)||b.isNumber(c)||b.isString(c))||!b.isString(d)&&!b.isObject(d))return c;var e,f,g=c;if(b.isNumber(c)?g=new Date(c):b.isString(c)&&(f=parseInt(c),b.isNumber(f)||(f=Date.parse(c)),g=new Date(f)),!b.isDate(g)||isNaN(g.getTime()))return c;try{e=a.align(g,d,!0)}catch(h){return c}return e&&e.getTime&&!isNaN(e.getTime())?e:c}}])}(this);

View File

@ -0,0 +1,22 @@
MIT License
Copyright (c) 2012 Jon Nylander, project maintained at
https://bitbucket.org/pellepim/jstimezonedetect
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to
do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

View File

@ -0,0 +1,62 @@
## Introduction
This script gives you the zone info key representing your device's time zone setting.
The return value is an [IANA zone info key][1] (aka the Olson time zone database).
The IANA timezone database is pretty much standard for most platforms (UNIX and Mac support it natively, and every programming language in the world either has native support or well maintained libraries that support it).
## Example Use
Since version 1.0.4 the [library is hosted on cdnjs.com][10]. I strongly recommend including it from there.
Invoke the script by calling
:::javascript
var tz = jstz.determine(); // Determines the time zone of the browser client
tz.name(); // Returns the name of the time zone eg "Europe/Berlin"
## Use Case
The script is useful if you do not want to disturb your users with questions about what time zone they are in. You can rely on this script to give you a key that is usable for server side datetime normalisations across time zones.
## Limitations
This script does not do geo-location, nor does it care very much about historical time zones.
So if you are unhappy with the time zone "Europe/Berlin" when the user is in fact in "Europe/Stockholm" - this script is not for you. (They are both identical in modern time).
Also, if it is important to you to know that in Europe/Simferopool (Ukraine) the UTC offset before 1924 was +2.67, sorry, this script will not help you.
Time zones are a screwed up thing, generally speaking, and the scope of this script is to solve problems concerning modern time zones, in this case from 2010 and forward.
## Demo
There is an updated demo running on: [http://pellepim.bitbucket.org/jstz/][2].
## Contribute?
If you want to contribute to the project (perhaps fix a bug, or reflect a change in time zone rules), please simply issue a Pull Request. Don't worry about [Grunt][4] builds etc, all you need to modify is the jstz.js file and I'll take care of the testing/minifying etc.
## Credits
Thanks to
- [Josh Fraser][5] for the original idea
- [Brian Donovan][6] for making jstz CommonJS compliant
- [Ilya Sedlovsky][7] for help with namespacing
- [Jordan Magnuson][9] for adding to cdnjs, documentation tags, and for reporting important issues
Other contributors:
[Gilmore Davidson][8]
[1]: http://www.iana.org/time-zones
[2]: http://pellepim.bitbucket.org/jstz/
[3]: https://bitbucket.org/pellepim/jstimezonedetect/src
[4]: https://github.com/gruntjs/grunt
[5]: http://www.onlineaspect.com/about/
[6]: https://bitbucket.org/eventualbuddha
[7]: https://bitbucket.org/purebill
[8]: https://bitbucket.org/gdavidson
[9]: https://github.com/JordanMagnuson
[10]: http://cdnjs.com

View File

@ -0,0 +1,22 @@
/*global module*/
module.exports = function (grunt) {
grunt.initConfig({
pkg: '<json:package.json>',
meta: {
banner: '/*! <%= pkg.name %> - v<%= pkg.version %> - ' + '<%= grunt.template.today("yyyy-mm-dd") %> */'
},
lint: {
all: ['jstz.js']
},
min: {
dist: {
src: ['<banner>','jstz.js'],
dest: 'jstz.min.js'
}
}
});
// Default task.
grunt.registerTask('default', 'lint min');
};

View File

@ -0,0 +1,358 @@
/**
* This script gives you the zone info key representing your device's time zone setting.
*
* @name jsTimezoneDetect
* @version 1.0.5
* @author Jon Nylander
* @license MIT License - http://www.opensource.org/licenses/mit-license.php
*
* For usage and examples, visit:
* http://pellepim.bitbucket.org/jstz/
*
* Copyright (c) Jon Nylander
*/
/*jslint undef: true */
/*global console, exports*/
(function(root) {
/**
* Namespace to hold all the code for timezone detection.
*/
var jstz = (function () {
'use strict';
var HEMISPHERE_SOUTH = 's',
/**
* Gets the offset in minutes from UTC for a certain date.
* @param {Date} date
* @returns {Number}
*/
get_date_offset = function (date) {
var offset = -date.getTimezoneOffset();
return (offset !== null ? offset : 0);
},
get_date = function (year, month, date) {
var d = new Date();
if (year !== undefined) {
d.setFullYear(year);
}
d.setMonth(month);
d.setDate(date);
return d;
},
get_january_offset = function (year) {
return get_date_offset(get_date(year, 0 ,2));
},
get_june_offset = function (year) {
return get_date_offset(get_date(year, 5, 2));
},
/**
* Private method.
* Checks whether a given date is in daylight saving time.
* If the date supplied is after august, we assume that we're checking
* for southern hemisphere DST.
* @param {Date} date
* @returns {Boolean}
*/
date_is_dst = function (date) {
var is_southern = date.getMonth() > 7,
base_offset = is_southern ? get_june_offset(date.getFullYear()) :
get_january_offset(date.getFullYear()),
date_offset = get_date_offset(date),
is_west = base_offset < 0,
dst_offset = base_offset - date_offset;
if (!is_west && !is_southern) {
return dst_offset < 0;
}
return dst_offset !== 0;
},
/**
* This function does some basic calculations to create information about
* the user's timezone. It uses REFERENCE_YEAR as a solid year for which
* the script has been tested rather than depend on the year set by the
* client device.
*
* Returns a key that can be used to do lookups in jstz.olson.timezones.
* eg: "720,1,2".
*
* @returns {String}
*/
lookup_key = function () {
var january_offset = get_january_offset(),
june_offset = get_june_offset(),
diff = january_offset - june_offset;
if (diff < 0) {
return january_offset + ",1";
} else if (diff > 0) {
return june_offset + ",1," + HEMISPHERE_SOUTH;
}
return january_offset + ",0";
},
/**
* Uses get_timezone_info() to formulate a key to use in the olson.timezones dictionary.
*
* Returns a primitive object on the format:
* {'timezone': TimeZone, 'key' : 'the key used to find the TimeZone object'}
*
* @returns Object
*/
determine = function () {
var key = lookup_key();
return new jstz.TimeZone(jstz.olson.timezones[key]);
},
/**
* This object contains information on when daylight savings starts for
* different timezones.
*
* The list is short for a reason. Often we do not have to be very specific
* to single out the correct timezone. But when we do, this list comes in
* handy.
*
* Each value is a date denoting when daylight savings starts for that timezone.
*/
dst_start_for = function (tz_name) {
var ru_pre_dst_change = new Date(2010, 6, 15, 1, 0, 0, 0), // In 2010 Russia had DST, this allows us to detect Russia :)
dst_starts = {
'America/Denver': new Date(2011, 2, 13, 3, 0, 0, 0),
'America/Mazatlan': new Date(2011, 3, 3, 3, 0, 0, 0),
'America/Chicago': new Date(2011, 2, 13, 3, 0, 0, 0),
'America/Mexico_City': new Date(2011, 3, 3, 3, 0, 0, 0),
'America/Asuncion': new Date(2012, 9, 7, 3, 0, 0, 0),
'America/Santiago': new Date(2012, 9, 3, 3, 0, 0, 0),
'America/Campo_Grande': new Date(2012, 9, 21, 5, 0, 0, 0),
'America/Montevideo': new Date(2011, 9, 2, 3, 0, 0, 0),
'America/Sao_Paulo': new Date(2011, 9, 16, 5, 0, 0, 0),
'America/Los_Angeles': new Date(2011, 2, 13, 8, 0, 0, 0),
'America/Santa_Isabel': new Date(2011, 3, 5, 8, 0, 0, 0),
'America/Havana': new Date(2012, 2, 10, 2, 0, 0, 0),
'America/New_York': new Date(2012, 2, 10, 7, 0, 0, 0),
'Europe/Helsinki': new Date(2013, 2, 31, 5, 0, 0, 0),
'Pacific/Auckland': new Date(2011, 8, 26, 7, 0, 0, 0),
'America/Halifax': new Date(2011, 2, 13, 6, 0, 0, 0),
'America/Goose_Bay': new Date(2011, 2, 13, 2, 1, 0, 0),
'America/Miquelon': new Date(2011, 2, 13, 5, 0, 0, 0),
'America/Godthab': new Date(2011, 2, 27, 1, 0, 0, 0),
'Europe/Moscow': ru_pre_dst_change,
'Asia/Amman': new Date(2013, 2, 29, 1, 0, 0, 0),
'Asia/Beirut': new Date(2013, 2, 31, 2, 0, 0, 0),
'Asia/Damascus': new Date(2013, 3, 6, 2, 0, 0, 0),
'Asia/Jerusalem': new Date(2013, 2, 29, 5, 0, 0, 0),
'Asia/Yekaterinburg': ru_pre_dst_change,
'Asia/Omsk': ru_pre_dst_change,
'Asia/Krasnoyarsk': ru_pre_dst_change,
'Asia/Irkutsk': ru_pre_dst_change,
'Asia/Yakutsk': ru_pre_dst_change,
'Asia/Vladivostok': ru_pre_dst_change,
'Asia/Baku': new Date(2013, 2, 31, 4, 0, 0),
'Asia/Yerevan': new Date(2013, 2, 31, 3, 0, 0),
'Asia/Kamchatka': ru_pre_dst_change,
'Asia/Gaza': new Date(2010, 2, 27, 4, 0, 0),
'Africa/Cairo': new Date(2010, 4, 1, 3, 0, 0),
'Europe/Minsk': ru_pre_dst_change,
'Pacific/Apia': new Date(2010, 10, 1, 1, 0, 0, 0),
'Pacific/Fiji': new Date(2010, 11, 1, 0, 0, 0),
'Australia/Perth': new Date(2008, 10, 1, 1, 0, 0, 0)
};
return dst_starts[tz_name];
};
return {
determine: determine,
date_is_dst: date_is_dst,
dst_start_for: dst_start_for
};
}());
/**
* Simple object to perform ambiguity check and to return name of time zone.
*/
jstz.TimeZone = function (tz_name) {
'use strict';
/**
* The keys in this object are timezones that we know may be ambiguous after
* a preliminary scan through the olson_tz object.
*
* The array of timezones to compare must be in the order that daylight savings
* starts for the regions.
*/
var AMBIGUITIES = {
'America/Denver': ['America/Denver', 'America/Mazatlan'],
'America/Chicago': ['America/Chicago', 'America/Mexico_City'],
'America/Santiago': ['America/Santiago', 'America/Asuncion', 'America/Campo_Grande'],
'America/Montevideo': ['America/Montevideo', 'America/Sao_Paulo'],
'Asia/Beirut': ['Asia/Amman', 'Asia/Jerusalem', 'Asia/Beirut', 'Europe/Helsinki','Asia/Damascus'],
'Pacific/Auckland': ['Pacific/Auckland', 'Pacific/Fiji'],
'America/Los_Angeles': ['America/Los_Angeles', 'America/Santa_Isabel'],
'America/New_York': ['America/Havana', 'America/New_York'],
'America/Halifax': ['America/Goose_Bay', 'America/Halifax'],
'America/Godthab': ['America/Miquelon', 'America/Godthab'],
'Asia/Dubai': ['Europe/Moscow'],
'Asia/Dhaka': ['Asia/Yekaterinburg'],
'Asia/Jakarta': ['Asia/Omsk'],
'Asia/Shanghai': ['Asia/Krasnoyarsk', 'Australia/Perth'],
'Asia/Tokyo': ['Asia/Irkutsk'],
'Australia/Brisbane': ['Asia/Yakutsk'],
'Pacific/Noumea': ['Asia/Vladivostok'],
'Pacific/Tarawa': ['Asia/Kamchatka', 'Pacific/Fiji'],
'Pacific/Tongatapu': ['Pacific/Apia'],
'Asia/Baghdad': ['Europe/Minsk'],
'Asia/Baku': ['Asia/Yerevan','Asia/Baku'],
'Africa/Johannesburg': ['Asia/Gaza', 'Africa/Cairo']
},
timezone_name = tz_name,
/**
* Checks if a timezone has possible ambiguities. I.e timezones that are similar.
*
* For example, if the preliminary scan determines that we're in America/Denver.
* We double check here that we're really there and not in America/Mazatlan.
*
* This is done by checking known dates for when daylight savings start for different
* timezones during 2010 and 2011.
*/
ambiguity_check = function () {
var ambiguity_list = AMBIGUITIES[timezone_name],
length = ambiguity_list.length,
i = 0,
tz = ambiguity_list[0];
for (; i < length; i += 1) {
tz = ambiguity_list[i];
if (jstz.date_is_dst(jstz.dst_start_for(tz))) {
timezone_name = tz;
return;
}
}
},
/**
* Checks if it is possible that the timezone is ambiguous.
*/
is_ambiguous = function () {
return typeof (AMBIGUITIES[timezone_name]) !== 'undefined';
};
if (is_ambiguous()) {
ambiguity_check();
}
return {
name: function () {
return timezone_name;
}
};
};
jstz.olson = {};
/*
* The keys in this dictionary are comma separated as such:
*
* First the offset compared to UTC time in minutes.
*
* Then a flag which is 0 if the timezone does not take daylight savings into account and 1 if it
* does.
*
* Thirdly an optional 's' signifies that the timezone is in the southern hemisphere,
* only interesting for timezones with DST.
*
* The mapped arrays is used for constructing the jstz.TimeZone object from within
* jstz.determine_timezone();
*/
jstz.olson.timezones = {
'-720,0' : 'Pacific/Majuro',
'-660,0' : 'Pacific/Pago_Pago',
'-600,1' : 'America/Adak',
'-600,0' : 'Pacific/Honolulu',
'-570,0' : 'Pacific/Marquesas',
'-540,0' : 'Pacific/Gambier',
'-540,1' : 'America/Anchorage',
'-480,1' : 'America/Los_Angeles',
'-480,0' : 'Pacific/Pitcairn',
'-420,0' : 'America/Phoenix',
'-420,1' : 'America/Denver',
'-360,0' : 'America/Guatemala',
'-360,1' : 'America/Chicago',
'-360,1,s' : 'Pacific/Easter',
'-300,0' : 'America/Bogota',
'-300,1' : 'America/New_York',
'-270,0' : 'America/Caracas',
'-240,1' : 'America/Halifax',
'-240,0' : 'America/Santo_Domingo',
'-240,1,s' : 'America/Santiago',
'-210,1' : 'America/St_Johns',
'-180,1' : 'America/Godthab',
'-180,0' : 'America/Argentina/Buenos_Aires',
'-180,1,s' : 'America/Montevideo',
'-120,0' : 'America/Noronha',
'-120,1' : 'America/Noronha',
'-60,1' : 'Atlantic/Azores',
'-60,0' : 'Atlantic/Cape_Verde',
'0,0' : 'UTC',
'0,1' : 'Europe/London',
'60,1' : 'Europe/Berlin',
'60,0' : 'Africa/Lagos',
'60,1,s' : 'Africa/Windhoek',
'120,1' : 'Asia/Beirut',
'120,0' : 'Africa/Johannesburg',
'180,0' : 'Asia/Baghdad',
'180,1' : 'Europe/Moscow',
'210,1' : 'Asia/Tehran',
'240,0' : 'Asia/Dubai',
'240,1' : 'Asia/Baku',
'270,0' : 'Asia/Kabul',
'300,1' : 'Asia/Yekaterinburg',
'300,0' : 'Asia/Karachi',
'330,0' : 'Asia/Kolkata',
'345,0' : 'Asia/Kathmandu',
'360,0' : 'Asia/Dhaka',
'360,1' : 'Asia/Omsk',
'390,0' : 'Asia/Rangoon',
'420,1' : 'Asia/Krasnoyarsk',
'420,0' : 'Asia/Jakarta',
'480,0' : 'Asia/Shanghai',
'480,1' : 'Asia/Irkutsk',
'525,0' : 'Australia/Eucla',
'525,1,s' : 'Australia/Eucla',
'540,1' : 'Asia/Yakutsk',
'540,0' : 'Asia/Tokyo',
'570,0' : 'Australia/Darwin',
'570,1,s' : 'Australia/Adelaide',
'600,0' : 'Australia/Brisbane',
'600,1' : 'Asia/Vladivostok',
'600,1,s' : 'Australia/Sydney',
'630,1,s' : 'Australia/Lord_Howe',
'660,1' : 'Asia/Kamchatka',
'660,0' : 'Pacific/Noumea',
'690,0' : 'Pacific/Norfolk',
'720,1,s' : 'Pacific/Auckland',
'720,0' : 'Pacific/Tarawa',
'765,1,s' : 'Pacific/Chatham',
'780,0' : 'Pacific/Tongatapu',
'780,1,s' : 'Pacific/Apia',
'840,0' : 'Pacific/Kiritimati'
};
if (typeof exports !== 'undefined') {
exports.jstz = jstz;
} else {
root.jstz = jstz;
}
})(this);

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,244 @@
#!/usr/bin/env node
var util = require('util'),
http = require('http'),
fs = require('fs'),
url = require('url'),
events = require('events');
var DEFAULT_PORT = 8000;
function main(argv) {
new HttpServer({
'GET': createServlet(StaticServlet),
'HEAD': createServlet(StaticServlet)
}).start(Number(argv[2]) || DEFAULT_PORT);
}
function escapeHtml(value) {
return value.toString().
replace('<', '&lt;').
replace('>', '&gt;').
replace('"', '&quot;');
}
function createServlet(Class) {
var servlet = new Class();
return servlet.handleRequest.bind(servlet);
}
/**
* An Http server implementation that uses a map of methods to decide
* action routing.
*
* @param {Object} Map of method => Handler function
*/
function HttpServer(handlers) {
this.handlers = handlers;
this.server = http.createServer(this.handleRequest_.bind(this));
}
HttpServer.prototype.start = function(port) {
this.port = port;
this.server.listen(port);
util.puts('Http Server running at http://localhost:' + port + '/');
};
HttpServer.prototype.parseUrl_ = function(urlString) {
var parsed = url.parse(urlString);
parsed.pathname = url.resolve('/', parsed.pathname);
return url.parse(url.format(parsed), true);
};
HttpServer.prototype.handleRequest_ = function(req, res) {
var logEntry = req.method + ' ' + req.url;
if (req.headers['user-agent']) {
logEntry += ' ' + req.headers['user-agent'];
}
util.puts(logEntry);
req.url = this.parseUrl_(req.url);
var handler = this.handlers[req.method];
if (!handler) {
res.writeHead(501);
res.end();
} else {
handler.call(this, req, res);
}
};
/**
* Handles static content.
*/
function StaticServlet() {}
StaticServlet.MimeMap = {
'txt': 'text/plain',
'html': 'text/html',
'css': 'text/css',
'xml': 'application/xml',
'json': 'application/json',
'js': 'application/javascript',
'jpg': 'image/jpeg',
'jpeg': 'image/jpeg',
'gif': 'image/gif',
'png': 'image/png',
  'svg': 'image/svg+xml'
};
StaticServlet.prototype.handleRequest = function(req, res) {
var self = this;
var path = ('./' + req.url.pathname).replace('//','/').replace(/%(..)/g, function(match, hex){
return String.fromCharCode(parseInt(hex, 16));
});
var parts = path.split('/');
if (parts[parts.length-1].charAt(0) === '.')
return self.sendForbidden_(req, res, path);
fs.stat(path, function(err, stat) {
if (err)
return self.sendMissing_(req, res, path);
if (stat.isDirectory())
return self.sendDirectory_(req, res, path);
return self.sendFile_(req, res, path);
});
}
StaticServlet.prototype.sendError_ = function(req, res, error) {
res.writeHead(500, {
'Content-Type': 'text/html'
});
res.write('<!doctype html>\n');
res.write('<title>Internal Server Error</title>\n');
res.write('<h1>Internal Server Error</h1>');
res.write('<pre>' + escapeHtml(util.inspect(error)) + '</pre>');
util.puts('500 Internal Server Error');
util.puts(util.inspect(error));
};
StaticServlet.prototype.sendMissing_ = function(req, res, path) {
path = path.substring(1);
res.writeHead(404, {
'Content-Type': 'text/html'
});
res.write('<!doctype html>\n');
res.write('<title>404 Not Found</title>\n');
res.write('<h1>Not Found</h1>');
res.write(
'<p>The requested URL ' +
escapeHtml(path) +
' was not found on this server.</p>'
);
res.end();
util.puts('404 Not Found: ' + path);
};
StaticServlet.prototype.sendForbidden_ = function(req, res, path) {
path = path.substring(1);
res.writeHead(403, {
'Content-Type': 'text/html'
});
res.write('<!doctype html>\n');
res.write('<title>403 Forbidden</title>\n');
res.write('<h1>Forbidden</h1>');
res.write(
'<p>You do not have permission to access ' +
escapeHtml(path) + ' on this server.</p>'
);
res.end();
util.puts('403 Forbidden: ' + path);
};
StaticServlet.prototype.sendRedirect_ = function(req, res, redirectUrl) {
res.writeHead(301, {
'Content-Type': 'text/html',
'Location': redirectUrl
});
res.write('<!doctype html>\n');
res.write('<title>301 Moved Permanently</title>\n');
res.write('<h1>Moved Permanently</h1>');
res.write(
'<p>The document has moved <a href="' +
redirectUrl +
'">here</a>.</p>'
);
res.end();
util.puts('301 Moved Permanently: ' + redirectUrl);
};
StaticServlet.prototype.sendFile_ = function(req, res, path) {
var self = this;
var file = fs.createReadStream(path);
res.writeHead(200, {
'Content-Type': StaticServlet.
MimeMap[path.split('.').pop()] || 'text/plain'
});
if (req.method === 'HEAD') {
res.end();
} else {
file.on('data', res.write.bind(res));
file.on('close', function() {
res.end();
});
file.on('error', function(error) {
self.sendError_(req, res, error);
});
}
};
StaticServlet.prototype.sendDirectory_ = function(req, res, path) {
var self = this;
if (path.match(/[^\/]$/)) {
req.url.pathname += '/';
var redirectUrl = url.format(url.parse(url.format(req.url)));
return self.sendRedirect_(req, res, redirectUrl);
}
fs.readdir(path, function(err, files) {
if (err)
return self.sendError_(req, res, error);
if (!files.length)
return self.writeDirectoryIndex_(req, res, path, []);
var remaining = files.length;
files.forEach(function(fileName, index) {
fs.stat(path + '/' + fileName, function(err, stat) {
if (err)
return self.sendError_(req, res, err);
if (stat.isDirectory()) {
files[index] = fileName + '/';
}
if (!(--remaining))
return self.writeDirectoryIndex_(req, res, path, files);
});
});
});
};
StaticServlet.prototype.writeDirectoryIndex_ = function(req, res, path, files) {
path = path.substring(1);
res.writeHead(200, {
'Content-Type': 'text/html'
});
if (req.method === 'HEAD') {
res.end();
return;
}
res.write('<!doctype html>\n');
res.write('<title>' + escapeHtml(path) + '</title>\n');
res.write('<style>\n');
res.write(' ol { list-style-type: none; font-size: 1.2em; }\n');
res.write('</style>\n');
res.write('<h1>Directory: ' + escapeHtml(path) + '</h1>');
res.write('<ol>');
files.forEach(function(fileName) {
if (fileName.charAt(0) !== '.') {
res.write('<li><a href="' +
escapeHtml(fileName) + '">' +
escapeHtml(fileName) + '</a></li>');
}
});
res.write('</ol>');
res.end();
};
// Must be last,
main(process.argv);

View File

@ -0,0 +1,523 @@
# <pre>
# This file is in the public domain, so clarified as of
# 2009-05-17 by Arthur David Olson.
# Package name for the code distribution.
PACKAGE= tzcode
# Version numbers of the code and data distributions.
VERSION= 2013c
# Email address for bug reports.
BUGEMAIL= tz@iana.org
# Change the line below for your time zone (after finding the zone you want in
# the time zone files, or adding it to a time zone file).
# Alternately, if you discover you've got the wrong time zone, you can just
# zic -l rightzone
# to correct things.
# Use the command
# make zonenames
# to get a list of the values you can use for LOCALTIME.
LOCALTIME= GMT
# If you want something other than Eastern United States time as a template
# for handling POSIX-style time zone environment variables,
# change the line below (after finding the zone you want in the
# time zone files, or adding it to a time zone file).
# (When a POSIX-style environment variable is handled, the rules in the
# template file are used to determine "spring forward" and "fall back" days and
# times; the environment variable itself specifies UTC offsets of standard and
# summer time.)
# Alternately, if you discover you've got the wrong time zone, you can just
# zic -p rightzone
# to correct things.
# Use the command
# make zonenames
# to get a list of the values you can use for POSIXRULES.
# If you want POSIX compatibility, use "America/New_York".
POSIXRULES= America/New_York
# Also see TZDEFRULESTRING below, which takes effect only
# if the time zone files cannot be accessed.
# Everything gets put in subdirectories of. . .
TOPDIR= /usr/local
# "Compiled" time zone information is placed in the "TZDIR" directory
# (and subdirectories).
# Use an absolute path name for TZDIR unless you're just testing the software.
TZDIR= $(TOPDIR)/etc/zoneinfo
# The "tzselect", "zic", and "zdump" commands get installed in. . .
ETCDIR= $(TOPDIR)/etc
# If you "make INSTALL", the "date" command gets installed in. . .
BINDIR= $(TOPDIR)/bin
# Manual pages go in subdirectories of. . .
MANDIR= $(TOPDIR)/man
# Library functions are put in an archive in LIBDIR.
LIBDIR= $(TOPDIR)/lib
TZLIB= $(LIBDIR)/libtz.a
# If you always want time values interpreted as "seconds since the epoch
# (not counting leap seconds)", use
# REDO= posix_only
# below. If you always want right time values interpreted as "seconds since
# the epoch" (counting leap seconds)", use
# REDO= right_only
# below. If you want both sets of data available, with leap seconds not
# counted normally, use
# REDO= posix_right
# below. If you want both sets of data available, with leap seconds counted
# normally, use
# REDO= right_posix
# below.
# POSIX mandates that leap seconds not be counted; for compatibility with it,
# use either "posix_only" or "posix_right".
REDO= posix_right
# Since "." may not be in PATH...
YEARISTYPE= ./yearistype
# Non-default libraries needed to link.
# Add -lintl if you want to use `gettext' on Solaris.
LDLIBS=
# Add the following to the end of the "CFLAGS=" line as needed.
# -DHAVE_ADJTIME=0 if `adjtime' does not exist (SVR0?)
# -DHAVE_GETTEXT=1 if `gettext' works (GNU, Linux, Solaris); also see LDLIBS
# -DHAVE_INCOMPATIBLE_CTIME_R=1 if your system's time.h declares
# ctime_r and asctime_r incompatibly with the POSIX standard (Solaris 8).
# -DHAVE_SETTIMEOFDAY=0 if settimeofday does not exist (SVR0?)
# -DHAVE_SETTIMEOFDAY=1 if settimeofday has just 1 arg (SVR4)
# -DHAVE_SETTIMEOFDAY=2 if settimeofday uses 2nd arg (4.3BSD)
# -DHAVE_SETTIMEOFDAY=3 if settimeofday ignores 2nd arg (4.4BSD)
# -DHAVE_STDINT_H=1 if you have a pre-C99 compiler with "stdint.h"
# -DHAVE_SYMLINK=0 if your system lacks the symlink function
# -DHAVE_SYS_STAT_H=0 if your compiler lacks a "sys/stat.h"
# -DHAVE_SYS_WAIT_H=0 if your compiler lacks a "sys/wait.h"
# -DLOCALE_HOME=\"path\" if locales are in "path", not "/usr/lib/locale"
# -DHAVE_UNISTD_H=0 if your compiler lacks a "unistd.h" (Microsoft C++ 7?)
# -DHAVE_UTMPX_H=1 if your compiler has a "utmpx.h"
# -DTZDEFRULESTRING=\",date/time,date/time\" to default to the specified
# DST transitions if the time zone files cannot be accessed
# -DTZ_DOMAIN=\"foo\" to use "foo" for gettext domain name; default is "tz"
# -TTZ_DOMAINDIR=\"/path\" to use "/path" for gettext directory;
# the default is system-supplied, typically "/usr/lib/locale"
# $(GCC_DEBUG_FLAGS) if you are using GCC and want lots of checking
# -DNO_RUN_TIME_WARNINGS_ABOUT_YEAR_2000_PROBLEMS_THANK_YOU=1
# if you do not want run time warnings about formats that may cause
# year 2000 grief
# -DZIC_MAX_ABBR_LEN_WO_WARN=3
# (or some other number) to set the maximum time zone abbreviation length
# that zic will accept without a warning (the default is 6)
GCC_DEBUG_FLAGS = -Dlint -g3 -O3 -fno-common -fstrict-aliasing \
-Wall -Wextra \
-Wbad-function-cast -Wcast-align -Wcast-qual \
-Wformat=2 -Winit-self \
-Wmissing-declarations -Wmissing-noreturn -Wmissing-prototypes \
-Wnested-externs \
-Wno-format-nonliteral -Wno-sign-compare -Wno-sign-conversion \
-Wno-type-limits \
-Wno-unused-parameter -Woverlength-strings -Wpointer-arith \
-Wshadow -Wstrict-prototypes -Wsuggest-attribute=const \
-Wsuggest-attribute=noreturn -Wsuggest-attribute=pure -Wtrampolines \
-Wwrite-strings
#
# If you want to use System V compatibility code, add
# -DUSG_COMPAT
# to the end of the "CFLAGS=" line. This arrange for "timezone" and "daylight"
# variables to be kept up-to-date by the time conversion functions. Neither
# "timezone" nor "daylight" is described in X3J11's work.
#
# If your system has a "GMT offset" field in its "struct tm"s
# (or if you decide to add such a field in your system's "time.h" file),
# add the name to a define such as
# -DTM_GMTOFF=tm_gmtoff
# or
# -DTM_GMTOFF=_tm_gmtoff
# to the end of the "CFLAGS=" line.
# Neither tm_gmtoff nor _tm_gmtoff is described in X3J11's work;
# in its work, use of "tm_gmtoff" is described as non-conforming.
# Both Linux and BSD have done the equivalent of defining TM_GMTOFF in
# their recent releases.
#
# If your system has a "zone abbreviation" field in its "struct tm"s
# (or if you decide to add such a field in your system's "time.h" file),
# add the name to a define such as
# -DTM_ZONE=tm_zone
# or
# -DTM_ZONE=_tm_zone
# to the end of the "CFLAGS=" line.
# Neither tm_zone nor _tm_zone is described in X3J11's work;
# in its work, use of "tm_zone" is described as non-conforming.
# Both UCB and Sun have done the equivalent of defining TM_ZONE in
# their recent releases.
#
# If you want functions that were inspired by early versions of X3J11's work,
# add
# -DSTD_INSPIRED
# to the end of the "CFLAGS=" line. This arranges for the functions
# "tzsetwall", "offtime", "timelocal", "timegm", "timeoff",
# "posix2time", and "time2posix" to be added to the time conversion library.
# "tzsetwall" is like "tzset" except that it arranges for local wall clock
# time (rather than the time specified in the TZ environment variable)
# to be used.
# "offtime" is like "gmtime" except that it accepts a second (long) argument
# that gives an offset to add to the time_t when converting it.
# "timelocal" is equivalent to "mktime".
# "timegm" is like "timelocal" except that it turns a struct tm into
# a time_t using UTC (rather than local time as "timelocal" does).
# "timeoff" is like "timegm" except that it accepts a second (long) argument
# that gives an offset to use when converting to a time_t.
# "posix2time" and "time2posix" are described in an included manual page.
# X3J11's work does not describe any of these functions.
# Sun has provided "tzsetwall", "timelocal", and "timegm" in SunOS 4.0.
# These functions may well disappear in future releases of the time
# conversion package.
#
# If you want Source Code Control System ID's left out of object modules, add
# -DNOID
# to the end of the "CFLAGS=" line.
#
# If you'll never want to handle solar-time-based time zones, add
# -DNOSOLAR
# to the end of the "CFLAGS=" line
# (and comment out the "SDATA=" line below).
# This reduces (slightly) the run-time data-space requirements of
# the time conversion functions; it may reduce the acceptability of your system
# to folks in oil- and cash-rich places.
#
# If you want to allocate state structures in localtime, add
# -DALL_STATE
# to the end of the "CFLAGS=" line. Storage is obtained by calling malloc.
#
# If you want an "altzone" variable (a la System V Release 3.1), add
# -DALTZONE
# to the end of the "CFLAGS=" line.
# This variable is not described in X3J11's work.
#
# If you want a "gtime" function (a la MACH), add
# -DCMUCS
# to the end of the "CFLAGS=" line
# This function is not described in X3J11's work.
#
# NIST-PCTS:151-2, Version 1.4, (1993-12-03) is a test suite put
# out by the National Institute of Standards and Technology
# which claims to test C and Posix conformance. If you want to pass PCTS, add
# -DPCTS
# to the end of the "CFLAGS=" line.
#
# If you want strict compliance with XPG4 as of 1994-04-09, add
# -DXPG4_1994_04_09
# to the end of the "CFLAGS=" line. This causes "strftime" to always return
# 53 as a week number (rather than 52 or 53) for those days in January that
# before the first Monday in January when a "%V" format is used and January 1
# falls on a Friday, Saturday, or Sunday.
CFLAGS=
# Linker flags. Default to $(LFLAGS) for backwards compatibility
# to tzcode2012h and earlier.
LDFLAGS= $(LFLAGS)
zic= ./zic
ZIC= $(zic) $(ZFLAGS)
# The name of a Posix-compliant `awk' on your system.
AWK= awk
# The full path name of a Posix-compliant shell that supports the Korn shell's
# 'select' statement, as an extension. These days, Bash is the most popular.
KSHELL= /bin/bash
# The path where SGML DTDs are kept.
# The default is appropriate for Ubuntu 12.10.
SGML_TOPDIR= /usr
SGML_DTDDIR= $(SGML_TOPDIR)/share/xml/w3c-sgml-lib/schema/dtd
SGML_SEARCH_PATH= $(SGML_DTDDIR)/REC-html401-19991224
# The catalog file(s) to use when validating.
SGML_CATALOG_FILES= HTML4.cat
# The name, arguments and environment of a program to validate your web pages.
# See <http://www.jclark.com/sp/> for a validator, and
# <http://validator.w3.org/source/> for a validation library.
VALIDATE = nsgmls
VALIDATE_FLAGS = -s -B -wall -wno-unused-param
VALIDATE_ENV = \
SGML_CATALOG_FILES=$(SGML_CATALOG_FILES) \
SGML_SEARCH_PATH=$(SGML_SEARCH_PATH) \
SP_CHARSET_FIXED=YES \
SP_ENCODING=UTF-8
# INVALID_CHAR is a regular expression that matches invalid characters in
# distributed files. For now, stick to a safe subset of ASCII.
# The caller must set the shell variable 'sharp' to the character '#',
# since Makefile macros cannot contain '#'.
# TAB_CHAR is a single tab character, in single quotes.
TAB_CHAR= ' '
INVALID_CHAR1= $(TAB_CHAR)' !\"'$$sharp'$$%&'\''()*+,./0123456789:;<=>?@'
INVALID_CHAR2= 'ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\^_`'
INVALID_CHAR3= 'abcdefghijklmnopqrstuvwxyz{|}~'
INVALID_CHAR= '[^]'$(INVALID_CHAR1)$(INVALID_CHAR2)$(INVALID_CHAR3)'-]'
# Flags to give 'tar' when making a distribution.
# Try to use flags appropriate for GNU tar.
GNUTARFLAGS= --numeric-owner --owner=0 --group=0 --mode=go+u,go-w
TARFLAGS= `if tar $(GNUTARFLAGS) --version >/dev/null 2>&1; \
then echo $(GNUTARFLAGS); \
else :; \
fi`
# Flags to give 'gzip' when making a distribution.
GZIPFLAGS= -9n
###############################################################################
cc= cc
CC= $(cc) -DTZDIR=\"$(TZDIR)\"
TZCSRCS= zic.c localtime.c asctime.c scheck.c ialloc.c
TZCOBJS= zic.o localtime.o asctime.o scheck.o ialloc.o
TZDSRCS= zdump.c localtime.c ialloc.c
TZDOBJS= zdump.o localtime.o ialloc.o
DATESRCS= date.c localtime.c strftime.c asctime.c
DATEOBJS= date.o localtime.o strftime.o asctime.o
LIBSRCS= localtime.c asctime.c difftime.c
LIBOBJS= localtime.o asctime.o difftime.o
HEADERS= tzfile.h private.h
NONLIBSRCS= zic.c zdump.c scheck.c ialloc.c
NEWUCBSRCS= date.c strftime.c
SOURCES= $(HEADERS) $(LIBSRCS) $(NONLIBSRCS) $(NEWUCBSRCS) tzselect.ksh
MANS= newctime.3 newstrftime.3 newtzset.3 time2posix.3 \
tzfile.5 tzselect.8 zic.8 zdump.8
COMMON= Makefile
DOCS= README Theory $(MANS) date.1
PRIMARY_YDATA= africa antarctica asia australasia \
europe northamerica southamerica
YDATA= $(PRIMARY_YDATA) pacificnew etcetera backward
NDATA= systemv factory
SDATA= solar87 solar88 solar89
TDATA= $(YDATA) $(NDATA) $(SDATA)
TABDATA= iso3166.tab zone.tab
DATA= $(YDATA) $(NDATA) $(SDATA) $(TABDATA) leapseconds yearistype.sh
WEB_PAGES= tz-art.htm tz-link.htm
MISC= usno1988 usno1989 usno1989a usno1995 usno1997 usno1998 \
$(WEB_PAGES) checktab.awk workman.sh \
zoneinfo2tdf.pl
ENCHILADA= $(COMMON) $(DOCS) $(SOURCES) $(DATA) $(MISC)
# And for the benefit of csh users on systems that assume the user
# shell should be used to handle commands in Makefiles. . .
SHELL= /bin/sh
all: tzselect zic zdump $(LIBOBJS)
ALL: all date
install: all $(DATA) $(REDO) $(TZLIB) $(MANS) $(TABDATA)
$(ZIC) -y $(YEARISTYPE) \
-d $(TZDIR) -l $(LOCALTIME) -p $(POSIXRULES)
-rm -f $(TZDIR)/iso3166.tab $(TZDIR)/zone.tab
cp iso3166.tab zone.tab $(TZDIR)/.
-mkdir $(TOPDIR) $(ETCDIR)
cp tzselect zic zdump $(ETCDIR)/.
-mkdir $(TOPDIR) $(MANDIR) \
$(MANDIR)/man3 $(MANDIR)/man5 $(MANDIR)/man8
-rm -f $(MANDIR)/man3/newctime.3 \
$(MANDIR)/man3/newtzset.3 \
$(MANDIR)/man5/tzfile.5 \
$(MANDIR)/man8/tzselect.8 \
$(MANDIR)/man8/zdump.8 \
$(MANDIR)/man8/zic.8
cp newctime.3 newtzset.3 $(MANDIR)/man3/.
cp tzfile.5 $(MANDIR)/man5/.
cp tzselect.8 zdump.8 zic.8 $(MANDIR)/man8/.
INSTALL: ALL install date.1
-mkdir $(TOPDIR) $(BINDIR)
cp date $(BINDIR)/.
-mkdir $(TOPDIR) $(MANDIR) $(MANDIR)/man1
-rm -f $(MANDIR)/man1/date.1
cp date.1 $(MANDIR)/man1/.
version.h:
(echo 'static char const PKGVERSION[]="($(PACKAGE)) ";' && \
echo 'static char const TZVERSION[]="$(VERSION)";' && \
echo 'static char const REPORT_BUGS_TO[]="$(BUGEMAIL)";') >$@
zdump: $(TZDOBJS)
$(CC) -o $@ $(CFLAGS) $(LDFLAGS) $(TZDOBJS) $(LDLIBS)
zic: $(TZCOBJS) yearistype
$(CC) -o $@ $(CFLAGS) $(LDFLAGS) $(TZCOBJS) $(LDLIBS)
yearistype: yearistype.sh
cp yearistype.sh yearistype
chmod +x yearistype
posix_only: zic $(TDATA)
$(ZIC) -y $(YEARISTYPE) -d $(TZDIR) -L /dev/null $(TDATA)
right_only: zic leapseconds $(TDATA)
$(ZIC) -y $(YEARISTYPE) -d $(TZDIR) -L leapseconds $(TDATA)
# In earlier versions of this makefile, the other two directories were
# subdirectories of $(TZDIR). However, this led to configuration errors.
# For example, with posix_right under the earlier scheme,
# TZ='right/Australia/Adelaide' got you localtime with leap seconds,
# but gmtime without leap seconds, which led to problems with applications
# like sendmail that subtract gmtime from localtime.
# Therefore, the other two directories are now siblings of $(TZDIR).
# You must replace all of $(TZDIR) to switch from not using leap seconds
# to using them, or vice versa.
other_two: zic leapseconds $(TDATA)
$(ZIC) -y $(YEARISTYPE) -d $(TZDIR)-posix -L /dev/null $(TDATA)
$(ZIC) -y $(YEARISTYPE) \
-d $(TZDIR)-leaps -L leapseconds $(TDATA)
posix_right: posix_only other_two
right_posix: right_only other_two
zones: $(REDO)
$(TZLIB): $(LIBOBJS)
-mkdir $(TOPDIR) $(LIBDIR)
ar ru $@ $(LIBOBJS)
if [ -x /usr/ucb/ranlib ] || [ -x /usr/bin/ranlib ]; \
then ranlib $@ ; fi
date: $(DATEOBJS)
$(CC) -o $@ $(CFLAGS) $(LDFLAGS) $(DATEOBJS) $(LDLIBS)
tzselect: tzselect.ksh
sed \
-e 's|#!/bin/bash|#!$(KSHELL)|g' \
-e 's|AWK=[^}]*|AWK=$(AWK)|g' \
-e 's|\(PKGVERSION\)=.*|\1='\''($(PACKAGE)) '\''|' \
-e 's|\(REPORT_BUGS_TO\)=.*|\1=$(BUGEMAIL)|' \
-e 's|TZDIR=[^}]*|TZDIR=$(TZDIR)|' \
-e 's|\(TZVERSION\)=.*|\1=$(VERSION)|' \
<$? >$@
chmod +x $@
check: check_character_set check_tables check_web
check_character_set: $(ENCHILADA)
sharp='#'; ! grep -n $(INVALID_CHAR) $(ENCHILADA)
check_tables: checktab.awk $(PRIMARY_YDATA)
$(AWK) -f checktab.awk $(PRIMARY_YDATA)
check_web: $(WEB_PAGES)
$(VALIDATE_ENV) $(VALIDATE) $(VALIDATE_FLAGS) $(WEB_PAGES)
clean:
rm -f core *.o *.out \
date tzselect version.h zdump zic yearistype
rm -f -r tzpublic
maintainer-clean: clean
@echo 'This command is intended for maintainers to use; it'
@echo 'deletes files that may need special tools to rebuild.'
rm -f *.[1-8].txt *.asc *.tar.gz
names:
@echo $(ENCHILADA)
public: check check_public set-timestamps tarballs signatures
# Set the time stamps to those of the git repository, if available,
# and if the files have not changed since then.
# This uses GNU 'touch' syntax 'touch -d@N FILE',
# where N is the number of seconds since 1970.
# If git or GNU 'touch' is absent, do nothing.
set-timestamps:
-TZ=UTC0 && export TZ && files=`git ls-files` && \
touch -d @1 test.out && rm -f test.out && \
for file in $$files; do \
test -z "`git diff --name-only $$file`" || continue; \
cmd="touch -d @`git log -1 --format='format:%ct' $$file \
` $$file" && \
echo "$$cmd" && \
$$cmd || exit; \
done
# The zics below ensure that each data file can stand on its own.
# We also do an all-files run to catch links to links.
check_public: $(ENCHILADA)
make maintainer-clean
make "CFLAGS=$(GCC_DEBUG_FLAGS)"
mkdir tzpublic
for i in $(TDATA) ; do \
$(zic) -v -d tzpublic $$i 2>&1 || exit; \
done
$(zic) -v -d tzpublic $(TDATA)
rm -f -r tzpublic
tarballs: tzcode$(VERSION).tar.gz tzdata$(VERSION).tar.gz
tzcode$(VERSION).tar.gz: $(COMMON) $(DOCS) $(SOURCES) $(MISC)
for i in *.[1-8] ; do \
LC_ALL=C sh workman.sh $$i > $$i.txt && \
touch -r $$i $$i.txt || exit; \
done
LC_ALL=C && export LC_ALL && \
tar $(TARFLAGS) -cf - \
$(COMMON) $(DOCS) $(SOURCES) $(MISC) *.[1-8].txt | \
gzip $(GZIPFLAGS) > $@
tzdata$(VERSION).tar.gz: $(COMMON) $(DATA)
LC_ALL=C && export LC_ALL && \
tar $(TARFLAGS) -cf - $(COMMON) $(DATA) | \
gzip $(GZIPFLAGS) > $@
signatures: tzcode$(VERSION).tar.gz.asc tzdata$(VERSION).tar.gz.asc
tzcode$(VERSION).tar.gz.asc: tzcode$(VERSION).tar.gz
gpg --armor --detach-sign $?
tzdata$(VERSION).tar.gz.asc: tzdata$(VERSION).tar.gz
gpg --armor --detach-sign $?
typecheck:
make clean
for i in "long long" unsigned double; \
do \
make CFLAGS="-DTYPECHECK -D__time_t_defined -D_TIME_T \"-Dtime_t=$$i\"" ; \
./zdump -v Europe/Rome ; \
make clean ; \
done
zonenames: $(TDATA)
@$(AWK) '/^Zone/ { print $$2 } /^Link/ { print $$3 }' $(TDATA)
asctime.o: private.h tzfile.h
date.o: private.h
difftime.o: private.h
ialloc.o: private.h
localtime.o: private.h tzfile.h
scheck.o: private.h
strftime.o: tzfile.h
zdump.o: version.h
zic.o: private.h tzfile.h version.h
.KEEP_STATE:

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,371 @@
# <pre>
# This file is in the public domain, so clarified as of
# 2009-05-17 by Arthur David Olson.
# From Paul Eggert (1999-11-15):
# To keep things manageable, we list only locations occupied year-round; see
# <a href="http://www.comnap.aq/comnap/comnap.nsf/P/Stations/">
# COMNAP - Stations and Bases
# </a>
# and
# <a href="http://www.spri.cam.ac.uk/bob/periant.htm">
# Summary of the Peri-Antarctic Islands (1998-07-23)
# </a>
# for information.
# Unless otherwise specified, we have no time zone information.
#
# Except for the French entries,
# I made up all time zone abbreviations mentioned here; corrections welcome!
# FORMAT is `zzz' and GMTOFF is 0 for locations while uninhabited.
# These rules are stolen from the `southamerica' file.
# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S
Rule ArgAQ 1964 1966 - Mar 1 0:00 0 -
Rule ArgAQ 1964 1966 - Oct 15 0:00 1:00 S
Rule ArgAQ 1967 only - Apr 2 0:00 0 -
Rule ArgAQ 1967 1968 - Oct Sun>=1 0:00 1:00 S
Rule ArgAQ 1968 1969 - Apr Sun>=1 0:00 0 -
Rule ArgAQ 1974 only - Jan 23 0:00 1:00 S
Rule ArgAQ 1974 only - May 1 0:00 0 -
Rule ChileAQ 1972 1986 - Mar Sun>=9 3:00u 0 -
Rule ChileAQ 1974 1987 - Oct Sun>=9 4:00u 1:00 S
Rule ChileAQ 1987 only - Apr 12 3:00u 0 -
Rule ChileAQ 1988 1989 - Mar Sun>=9 3:00u 0 -
Rule ChileAQ 1988 only - Oct Sun>=1 4:00u 1:00 S
Rule ChileAQ 1989 only - Oct Sun>=9 4:00u 1:00 S
Rule ChileAQ 1990 only - Mar 18 3:00u 0 -
Rule ChileAQ 1990 only - Sep 16 4:00u 1:00 S
Rule ChileAQ 1991 1996 - Mar Sun>=9 3:00u 0 -
Rule ChileAQ 1991 1997 - Oct Sun>=9 4:00u 1:00 S
Rule ChileAQ 1997 only - Mar 30 3:00u 0 -
Rule ChileAQ 1998 only - Mar Sun>=9 3:00u 0 -
Rule ChileAQ 1998 only - Sep 27 4:00u 1:00 S
Rule ChileAQ 1999 only - Apr 4 3:00u 0 -
Rule ChileAQ 1999 2010 - Oct Sun>=9 4:00u 1:00 S
Rule ChileAQ 2000 2007 - Mar Sun>=9 3:00u 0 -
# N.B.: the end of March 29 in Chile is March 30 in Universal time,
# which is used below in specifying the transition.
Rule ChileAQ 2008 only - Mar 30 3:00u 0 -
Rule ChileAQ 2009 only - Mar Sun>=9 3:00u 0 -
Rule ChileAQ 2010 only - Apr Sun>=1 3:00u 0 -
Rule ChileAQ 2011 only - May Sun>=2 3:00u 0 -
Rule ChileAQ 2011 only - Aug Sun>=16 4:00u 1:00 S
Rule ChileAQ 2012 max - Apr Sun>=23 3:00u 0 -
Rule ChileAQ 2012 max - Sep Sun>=2 4:00u 1:00 S
# Argentina - year-round bases
# Belgrano II, Confin Coast, -770227-0343737, since 1972-02-05
# Esperanza, San Martin Land, -6323-05659, since 1952-12-17
# Jubany, Potter Peninsula, King George Island, -6414-0602320, since 1982-01
# Marambio, Seymour I, -6414-05637, since 1969-10-29
# Orcadas, Laurie I, -6016-04444, since 1904-02-22
# San Martin, Debenham I, -6807-06708, since 1951-03-21
# (except 1960-03 / 1976-03-21)
# Australia - territories
# Heard Island, McDonald Islands (uninhabited)
# previously sealers and scientific personnel wintered
# <a href="http://web.archive.org/web/20021204222245/http://www.dstc.qut.edu.au/DST/marg/daylight.html">
# Margaret Turner reports
# </a> (1999-09-30) that they're UTC+5, with no DST;
# presumably this is when they have visitors.
#
# year-round bases
# Casey, Bailey Peninsula, -6617+11032, since 1969
# Davis, Vestfold Hills, -6835+07759, since 1957-01-13
# (except 1964-11 - 1969-02)
# Mawson, Holme Bay, -6736+06253, since 1954-02-13
# From Steffen Thorsen (2009-03-11):
# Three Australian stations in Antarctica have changed their time zone:
# Casey moved from UTC+8 to UTC+11
# Davis moved from UTC+7 to UTC+5
# Mawson moved from UTC+6 to UTC+5
# The changes occurred on 2009-10-18 at 02:00 (local times).
#
# Government source: (Australian Antarctic Division)
# <a href="http://www.aad.gov.au/default.asp?casid=37079">
# http://www.aad.gov.au/default.asp?casid=37079
# </a>
#
# We have more background information here:
# <a href="http://www.timeanddate.com/news/time/antarctica-new-times.html">
# http://www.timeanddate.com/news/time/antarctica-new-times.html
# </a>
# From Steffen Thorsen (2010-03-10):
# We got these changes from the Australian Antarctic Division: ...
#
# - Casey station reverted to its normal time of UTC+8 on 5 March 2010.
# The change to UTC+11 is being considered as a regular summer thing but
# has not been decided yet.
#
# - Davis station will revert to its normal time of UTC+7 at 10 March 2010
# 20:00 UTC.
#
# - Mawson station stays on UTC+5.
#
# Background:
# <a href="http://www.timeanddate.com/news/time/antartica-time-changes-2010.html">
# http://www.timeanddate.com/news/time/antartica-time-changes-2010.html
# </a>
# Zone NAME GMTOFF RULES FORMAT [UNTIL]
Zone Antarctica/Casey 0 - zzz 1969
8:00 - WST 2009 Oct 18 2:00
# Western (Aus) Standard Time
11:00 - CAST 2010 Mar 5 2:00
# Casey Time
8:00 - WST 2011 Oct 28 2:00
11:00 - CAST 2012 Feb 21 17:00u
8:00 - WST
Zone Antarctica/Davis 0 - zzz 1957 Jan 13
7:00 - DAVT 1964 Nov # Davis Time
0 - zzz 1969 Feb
7:00 - DAVT 2009 Oct 18 2:00
5:00 - DAVT 2010 Mar 10 20:00u
7:00 - DAVT 2011 Oct 28 2:00
5:00 - DAVT 2012 Feb 21 20:00u
7:00 - DAVT
Zone Antarctica/Mawson 0 - zzz 1954 Feb 13
6:00 - MAWT 2009 Oct 18 2:00
# Mawson Time
5:00 - MAWT
# References:
# <a href="http://www.antdiv.gov.au/aad/exop/sfo/casey/casey_aws.html">
# Casey Weather (1998-02-26)
# </a>
# <a href="http://www.antdiv.gov.au/aad/exop/sfo/davis/video.html">
# Davis Station, Antarctica (1998-02-26)
# </a>
# <a href="http://www.antdiv.gov.au/aad/exop/sfo/mawson/video.html">
# Mawson Station, Antarctica (1998-02-25)
# </a>
# Brazil - year-round base
# Comandante Ferraz, King George Island, -6205+05824, since 1983/4
# Chile - year-round bases and towns
# Escudero, South Shetland Is, -621157-0585735, since 1994
# Presidente Eduadro Frei, King George Island, -6214-05848, since 1969-03-07
# General Bernardo O'Higgins, Antarctic Peninsula, -6319-05704, since 1948-02
# Capitan Arturo Prat, -6230-05941
# Villa Las Estrellas (a town), around the Frei base, since 1984-04-09
# These locations have always used Santiago time; use TZ='America/Santiago'.
# China - year-round bases
# Great Wall, King George Island, -6213-05858, since 1985-02-20
# Zhongshan, Larsemann Hills, Prydz Bay, -6922+07623, since 1989-02-26
# France - year-round bases
#
# From Antoine Leca (1997-01-20):
# Time data are from Nicole Pailleau at the IFRTP
# (French Institute for Polar Research and Technology).
# She confirms that French Southern Territories and Terre Adelie bases
# don't observe daylight saving time, even if Terre Adelie supplies came
# from Tasmania.
#
# French Southern Territories with year-round inhabitants
#
# Martin-de-Vivies Base, Amsterdam Island, -374105+0773155, since 1950
# Alfred-Faure Base, Crozet Islands, -462551+0515152, since 1964
# Port-aux-Francais, Kerguelen Islands, -492110+0701303, since 1951;
# whaling & sealing station operated 1908/1914, 1920/1929, and 1951/1956
#
# St Paul Island - near Amsterdam, uninhabited
# fishing stations operated variously 1819/1931
#
# Zone NAME GMTOFF RULES FORMAT [UNTIL]
Zone Indian/Kerguelen 0 - zzz 1950 # Port-aux-Francais
5:00 - TFT # ISO code TF Time
#
# year-round base in the main continent
# Dumont-d'Urville, Ile des Petrels, -6640+14001, since 1956-11
#
# Another base at Port-Martin, 50km east, began operation in 1947.
# It was destroyed by fire on 1952-01-14.
#
# Zone NAME GMTOFF RULES FORMAT [UNTIL]
Zone Antarctica/DumontDUrville 0 - zzz 1947
10:00 - PMT 1952 Jan 14 # Port-Martin Time
0 - zzz 1956 Nov
10:00 - DDUT # Dumont-d'Urville Time
# Reference:
# <a href="http://en.wikipedia.org/wiki/Dumont_d'Urville_Station">
# Dumont d'Urville Station (2005-12-05)
# </a>
# Germany - year-round base
# Georg von Neumayer, -7039-00815
# India - year-round base
# Dakshin Gangotri, -7005+01200
# Japan - year-round bases
# Dome Fuji, -7719+03942
# Syowa, -690022+0393524
#
# From Hideyuki Suzuki (1999-02-06):
# In all Japanese stations, +0300 is used as the standard time.
#
# Syowa station, which is the first antarctic station of Japan,
# was established on 1957-01-29. Since Syowa station is still the main
# station of Japan, it's appropriate for the principal location.
# Zone NAME GMTOFF RULES FORMAT [UNTIL]
Zone Antarctica/Syowa 0 - zzz 1957 Jan 29
3:00 - SYOT # Syowa Time
# See:
# <a href="http://www.nipr.ac.jp/english/ara01.html">
# NIPR Antarctic Research Activities (1999-08-17)
# </a>
# S Korea - year-round base
# King Sejong, King George Island, -6213-05847, since 1988
# New Zealand - claims
# Balleny Islands (never inhabited)
# Scott Island (never inhabited)
#
# year-round base
# Scott, Ross Island, since 1957-01, is like Antarctica/McMurdo.
#
# These rules for New Zealand are stolen from the `australasia' file.
# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S
Rule NZAQ 1974 only - Nov 3 2:00s 1:00 D
Rule NZAQ 1975 1988 - Oct lastSun 2:00s 1:00 D
Rule NZAQ 1989 only - Oct 8 2:00s 1:00 D
Rule NZAQ 1990 2006 - Oct Sun>=1 2:00s 1:00 D
Rule NZAQ 1975 only - Feb 23 2:00s 0 S
Rule NZAQ 1976 1989 - Mar Sun>=1 2:00s 0 S
Rule NZAQ 1990 2007 - Mar Sun>=15 2:00s 0 S
Rule NZAQ 2007 max - Sep lastSun 2:00s 1:00 D
Rule NZAQ 2008 max - Apr Sun>=1 2:00s 0 S
# Norway - territories
# Bouvet (never inhabited)
#
# claims
# Peter I Island (never inhabited)
# Poland - year-round base
# Arctowski, King George Island, -620945-0582745, since 1977
# Russia - year-round bases
# Bellingshausen, King George Island, -621159-0585337, since 1968-02-22
# Mirny, Davis coast, -6633+09301, since 1956-02
# Molodezhnaya, Alasheyev Bay, -6740+04551,
# year-round from 1962-02 to 1999-07-01
# Novolazarevskaya, Queen Maud Land, -7046+01150,
# year-round from 1960/61 to 1992
# Vostok, since 1957-12-16, temporarily closed 1994-02/1994-11
# <a href="http://quest.arc.nasa.gov/antarctica/QA/computers/Directions,Time,ZIP">
# From Craig Mundell (1994-12-15)</a>:
# Vostok, which is one of the Russian stations, is set on the same
# time as Moscow, Russia.
#
# From Lee Hotz (2001-03-08):
# I queried the folks at Columbia who spent the summer at Vostok and this is
# what they had to say about time there:
# ``in the US Camp (East Camp) we have been on New Zealand (McMurdo)
# time, which is 12 hours ahead of GMT. The Russian Station Vostok was
# 6 hours behind that (although only 2 miles away, i.e. 6 hours ahead
# of GMT). This is a time zone I think two hours east of Moscow. The
# natural time zone is in between the two: 8 hours ahead of GMT.''
#
# From Paul Eggert (2001-05-04):
# This seems to be hopelessly confusing, so I asked Lee Hotz about it
# in person. He said that some Antartic locations set their local
# time so that noon is the warmest part of the day, and that this
# changes during the year and does not necessarily correspond to mean
# solar noon. So the Vostok time might have been whatever the clocks
# happened to be during their visit. So we still don't really know what time
# it is at Vostok. But we'll guess UTC+6.
#
Zone Antarctica/Vostok 0 - zzz 1957 Dec 16
6:00 - VOST # Vostok time
# S Africa - year-round bases
# Marion Island, -4653+03752
# Sanae, -7141-00250
# UK
#
# British Antarctic Territories (BAT) claims
# South Orkney Islands
# scientific station from 1903
# whaling station at Signy I 1920/1926
# South Shetland Islands
#
# year-round bases
# Bird Island, South Georgia, -5400-03803, since 1983
# Deception Island, -6259-06034, whaling station 1912/1931,
# scientific station 1943/1967,
# previously sealers and a scientific expedition wintered by accident,
# and a garrison was deployed briefly
# Halley, Coates Land, -7535-02604, since 1956-01-06
# Halley is on a moving ice shelf and is periodically relocated
# so that it is never more than 10km from its nominal location.
# Rothera, Adelaide Island, -6734-6808, since 1976-12-01
#
# From Paul Eggert (2002-10-22)
# <http://webexhibits.org/daylightsaving/g.html> says Rothera is -03 all year.
#
# Zone NAME GMTOFF RULES FORMAT [UNTIL]
Zone Antarctica/Rothera 0 - zzz 1976 Dec 1
-3:00 - ROTT # Rothera time
# Uruguay - year round base
# Artigas, King George Island, -621104-0585107
# USA - year-round bases
#
# Palmer, Anvers Island, since 1965 (moved 2 miles in 1968)
#
# From Ethan Dicks (1996-10-06):
# It keeps the same time as Punta Arenas, Chile, because, just like us
# and the South Pole, that's the other end of their supply line....
# I verified with someone who was there that since 1980,
# Palmer has followed Chile. Prior to that, before the Falklands War,
# Palmer used to be supplied from Argentina.
#
# Zone NAME GMTOFF RULES FORMAT [UNTIL]
Zone Antarctica/Palmer 0 - zzz 1965
-4:00 ArgAQ AR%sT 1969 Oct 5
-3:00 ArgAQ AR%sT 1982 May
-4:00 ChileAQ CL%sT
#
#
# McMurdo, Ross Island, since 1955-12
# Zone NAME GMTOFF RULES FORMAT [UNTIL]
Zone Antarctica/McMurdo 0 - zzz 1956
12:00 NZAQ NZ%sT
#
# Amundsen-Scott, South Pole, continuously occupied since 1956-11-20
#
# From Paul Eggert (1996-09-03):
# Normally it wouldn't have a separate entry, since it's like the
# larger Antarctica/McMurdo since 1970, but it's too famous to omit.
#
# From Chris Carrier (1996-06-27):
# Siple, the first commander of the South Pole station,
# stated that he would have liked to have kept GMT at the station,
# but that he found it more convenient to keep GMT+12
# as supplies for the station were coming from McMurdo Sound,
# which was on GMT+12 because New Zealand was on GMT+12 all year
# at that time (1957). (Source: Siple's book 90 degrees SOUTH.)
#
# From Susan Smith
# http://www.cybertours.com/whs/pole10.html
# (1995-11-13 16:24:56 +1300, no longer available):
# We use the same time as McMurdo does.
# And they use the same time as Christchurch, NZ does....
# One last quirk about South Pole time.
# All the electric clocks are usually wrong.
# Something about the generators running at 60.1hertz or something
# makes all of the clocks run fast. So every couple of days,
# we have to go around and set them back 5 minutes or so.
# Maybe if we let them run fast all of the time, we'd get to leave here sooner!!
#
Link Antarctica/McMurdo Antarctica/South_Pole

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,117 @@
# <pre>
# This file is in the public domain, so clarified as of
# 2009-05-17 by Arthur David Olson.
# This file provides links between current names for time zones
# and their old names. Many names changed in late 1993.
Link Africa/Asmara Africa/Asmera
Link Africa/Bamako Africa/Timbuktu
Link America/Argentina/Catamarca America/Argentina/ComodRivadavia
Link America/Adak America/Atka
Link America/Argentina/Buenos_Aires America/Buenos_Aires
Link America/Argentina/Catamarca America/Catamarca
Link America/Atikokan America/Coral_Harbour
Link America/Argentina/Cordoba America/Cordoba
Link America/Tijuana America/Ensenada
Link America/Indiana/Indianapolis America/Fort_Wayne
Link America/Indiana/Indianapolis America/Indianapolis
Link America/Argentina/Jujuy America/Jujuy
Link America/Indiana/Knox America/Knox_IN
Link America/Kentucky/Louisville America/Louisville
Link America/Argentina/Mendoza America/Mendoza
Link America/Rio_Branco America/Porto_Acre
Link America/Argentina/Cordoba America/Rosario
Link America/St_Thomas America/Virgin
Link Asia/Ashgabat Asia/Ashkhabad
Link Asia/Chongqing Asia/Chungking
Link Asia/Dhaka Asia/Dacca
Link Asia/Kathmandu Asia/Katmandu
Link Asia/Kolkata Asia/Calcutta
Link Asia/Macau Asia/Macao
Link Asia/Jerusalem Asia/Tel_Aviv
Link Asia/Ho_Chi_Minh Asia/Saigon
Link Asia/Thimphu Asia/Thimbu
Link Asia/Makassar Asia/Ujung_Pandang
Link Asia/Ulaanbaatar Asia/Ulan_Bator
Link Atlantic/Faroe Atlantic/Faeroe
Link Europe/Oslo Atlantic/Jan_Mayen
Link Australia/Sydney Australia/ACT
Link Australia/Sydney Australia/Canberra
Link Australia/Lord_Howe Australia/LHI
Link Australia/Sydney Australia/NSW
Link Australia/Darwin Australia/North
Link Australia/Brisbane Australia/Queensland
Link Australia/Adelaide Australia/South
Link Australia/Hobart Australia/Tasmania
Link Australia/Melbourne Australia/Victoria
Link Australia/Perth Australia/West
Link Australia/Broken_Hill Australia/Yancowinna
Link America/Rio_Branco Brazil/Acre
Link America/Noronha Brazil/DeNoronha
Link America/Sao_Paulo Brazil/East
Link America/Manaus Brazil/West
Link America/Halifax Canada/Atlantic
Link America/Winnipeg Canada/Central
Link America/Regina Canada/East-Saskatchewan
Link America/Toronto Canada/Eastern
Link America/Edmonton Canada/Mountain
Link America/St_Johns Canada/Newfoundland
Link America/Vancouver Canada/Pacific
Link America/Regina Canada/Saskatchewan
Link America/Whitehorse Canada/Yukon
Link America/Santiago Chile/Continental
Link Pacific/Easter Chile/EasterIsland
Link America/Havana Cuba
Link Africa/Cairo Egypt
Link Europe/Dublin Eire
Link Europe/London Europe/Belfast
Link Europe/Chisinau Europe/Tiraspol
Link Europe/London GB
Link Europe/London GB-Eire
Link Etc/GMT GMT+0
Link Etc/GMT GMT-0
Link Etc/GMT GMT0
Link Etc/GMT Greenwich
Link Asia/Hong_Kong Hongkong
Link Atlantic/Reykjavik Iceland
Link Asia/Tehran Iran
Link Asia/Jerusalem Israel
Link America/Jamaica Jamaica
Link Asia/Tokyo Japan
Link Pacific/Kwajalein Kwajalein
Link Africa/Tripoli Libya
Link America/Tijuana Mexico/BajaNorte
Link America/Mazatlan Mexico/BajaSur
Link America/Mexico_City Mexico/General
Link Pacific/Auckland NZ
Link Pacific/Chatham NZ-CHAT
Link America/Denver Navajo
Link Asia/Shanghai PRC
Link Pacific/Pago_Pago Pacific/Samoa
Link Pacific/Chuuk Pacific/Yap
Link Pacific/Chuuk Pacific/Truk
Link Pacific/Pohnpei Pacific/Ponape
Link Europe/Warsaw Poland
Link Europe/Lisbon Portugal
Link Asia/Taipei ROC
Link Asia/Seoul ROK
Link Asia/Singapore Singapore
Link Europe/Istanbul Turkey
Link Etc/UCT UCT
Link America/Anchorage US/Alaska
Link America/Adak US/Aleutian
Link America/Phoenix US/Arizona
Link America/Chicago US/Central
Link America/Indiana/Indianapolis US/East-Indiana
Link America/New_York US/Eastern
Link Pacific/Honolulu US/Hawaii
Link America/Indiana/Knox US/Indiana-Starke
Link America/Detroit US/Michigan
Link America/Denver US/Mountain
Link America/Los_Angeles US/Pacific
Link Pacific/Pago_Pago US/Samoa
Link Etc/UTC UTC
Link Etc/UTC Universal
Link Europe/Moscow W-SU
Link Etc/UTC Zulu

View File

@ -0,0 +1,81 @@
# <pre>
# This file is in the public domain, so clarified as of
# 2009-05-17 by Arthur David Olson.
# These entries are mostly present for historical reasons, so that
# people in areas not otherwise covered by the tz files could "zic -l"
# to a time zone that was right for their area. These days, the
# tz files cover almost all the inhabited world, and the only practical
# need now for the entries that are not on UTC are for ships at sea
# that cannot use POSIX TZ settings.
Zone Etc/GMT 0 - GMT
Zone Etc/UTC 0 - UTC
Zone Etc/UCT 0 - UCT
# The following link uses older naming conventions,
# but it belongs here, not in the file `backward',
# as functions like gmtime load the "GMT" file to handle leap seconds properly.
# We want this to work even on installations that omit the other older names.
Link Etc/GMT GMT
Link Etc/UTC Etc/Universal
Link Etc/UTC Etc/Zulu
Link Etc/GMT Etc/Greenwich
Link Etc/GMT Etc/GMT-0
Link Etc/GMT Etc/GMT+0
Link Etc/GMT Etc/GMT0
# We use POSIX-style signs in the Zone names and the output abbreviations,
# even though this is the opposite of what many people expect.
# POSIX has positive signs west of Greenwich, but many people expect
# positive signs east of Greenwich. For example, TZ='Etc/GMT+4' uses
# the abbreviation "GMT+4" and corresponds to 4 hours behind UTC
# (i.e. west of Greenwich) even though many people would expect it to
# mean 4 hours ahead of UTC (i.e. east of Greenwich).
#
# In the draft 5 of POSIX 1003.1-200x, the angle bracket notation allows for
# TZ='<GMT-4>+4'; if you want time zone abbreviations conforming to
# ISO 8601 you can use TZ='<-0400>+4'. Thus the commonly-expected
# offset is kept within the angle bracket (and is used for display)
# while the POSIX sign is kept outside the angle bracket (and is used
# for calculation).
#
# Do not use a TZ setting like TZ='GMT+4', which is four hours behind
# GMT but uses the completely misleading abbreviation "GMT".
# Earlier incarnations of this package were not POSIX-compliant,
# and had lines such as
# Zone GMT-12 -12 - GMT-1200
# We did not want things to change quietly if someone accustomed to the old
# way does a
# zic -l GMT-12
# so we moved the names into the Etc subdirectory.
Zone Etc/GMT-14 14 - GMT-14 # 14 hours ahead of GMT
Zone Etc/GMT-13 13 - GMT-13
Zone Etc/GMT-12 12 - GMT-12
Zone Etc/GMT-11 11 - GMT-11
Zone Etc/GMT-10 10 - GMT-10
Zone Etc/GMT-9 9 - GMT-9
Zone Etc/GMT-8 8 - GMT-8
Zone Etc/GMT-7 7 - GMT-7
Zone Etc/GMT-6 6 - GMT-6
Zone Etc/GMT-5 5 - GMT-5
Zone Etc/GMT-4 4 - GMT-4
Zone Etc/GMT-3 3 - GMT-3
Zone Etc/GMT-2 2 - GMT-2
Zone Etc/GMT-1 1 - GMT-1
Zone Etc/GMT+1 -1 - GMT+1
Zone Etc/GMT+2 -2 - GMT+2
Zone Etc/GMT+3 -3 - GMT+3
Zone Etc/GMT+4 -4 - GMT+4
Zone Etc/GMT+5 -5 - GMT+5
Zone Etc/GMT+6 -6 - GMT+6
Zone Etc/GMT+7 -7 - GMT+7
Zone Etc/GMT+8 -8 - GMT+8
Zone Etc/GMT+9 -9 - GMT+9
Zone Etc/GMT+10 -10 - GMT+10
Zone Etc/GMT+11 -11 - GMT+11
Zone Etc/GMT+12 -12 - GMT+12

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,10 @@
# <pre>
# This file is in the public domain, so clarified as of
# 2009-05-17 by Arthur David Olson.
# For companies who don't want to put time zone specification in
# their installation procedures. When users run date, they'll get the message.
# Also useful for the "comp.sources" version.
# Zone NAME GMTOFF RULES FORMAT
Zone Factory 0 - "Local time zone must be set--see zic manual page"

View File

@ -0,0 +1,276 @@
# <pre>
# This file is in the public domain, so clarified as of
# 2009-05-17 by Arthur David Olson.
# ISO 3166 alpha-2 country codes
#
# From Paul Eggert (2006-09-27):
#
# This file contains a table with the following columns:
# 1. ISO 3166-1 alpha-2 country code, current as of
# ISO 3166-1 Newsletter VI-1 (2007-09-21). See:
# <a href="http://www.iso.org/iso/en/prods-services/iso3166ma/index.html">
# ISO 3166 Maintenance agency (ISO 3166/MA)
# </a>.
# 2. The usual English name for the country,
# chosen so that alphabetic sorting of subsets produces helpful lists.
# This is not the same as the English name in the ISO 3166 tables.
#
# Columns are separated by a single tab.
# The table is sorted by country code.
#
# Lines beginning with `#' are comments.
#
# From Arthur David Olson (2011-08-17):
# Resynchronized today with the ISO 3166 site (adding SS for South Sudan).
#
#country-
#code country name
AD Andorra
AE United Arab Emirates
AF Afghanistan
AG Antigua & Barbuda
AI Anguilla
AL Albania
AM Armenia
AO Angola
AQ Antarctica
AR Argentina
AS Samoa (American)
AT Austria
AU Australia
AW Aruba
AX Aaland Islands
AZ Azerbaijan
BA Bosnia & Herzegovina
BB Barbados
BD Bangladesh
BE Belgium
BF Burkina Faso
BG Bulgaria
BH Bahrain
BI Burundi
BJ Benin
BL St Barthelemy
BM Bermuda
BN Brunei
BO Bolivia
BQ Bonaire Sint Eustatius & Saba
BR Brazil
BS Bahamas
BT Bhutan
BV Bouvet Island
BW Botswana
BY Belarus
BZ Belize
CA Canada
CC Cocos (Keeling) Islands
CD Congo (Dem. Rep.)
CF Central African Rep.
CG Congo (Rep.)
CH Switzerland
CI Cote d'Ivoire
CK Cook Islands
CL Chile
CM Cameroon
CN China
CO Colombia
CR Costa Rica
CU Cuba
CV Cape Verde
CW Curacao
CX Christmas Island
CY Cyprus
CZ Czech Republic
DE Germany
DJ Djibouti
DK Denmark
DM Dominica
DO Dominican Republic
DZ Algeria
EC Ecuador
EE Estonia
EG Egypt
EH Western Sahara
ER Eritrea
ES Spain
ET Ethiopia
FI Finland
FJ Fiji
FK Falkland Islands
FM Micronesia
FO Faroe Islands
FR France
GA Gabon
GB Britain (UK)
GD Grenada
GE Georgia
GF French Guiana
GG Guernsey
GH Ghana
GI Gibraltar
GL Greenland
GM Gambia
GN Guinea
GP Guadeloupe
GQ Equatorial Guinea
GR Greece
GS South Georgia & the South Sandwich Islands
GT Guatemala
GU Guam
GW Guinea-Bissau
GY Guyana
HK Hong Kong
HM Heard Island & McDonald Islands
HN Honduras
HR Croatia
HT Haiti
HU Hungary
ID Indonesia
IE Ireland
IL Israel
IM Isle of Man
IN India
IO British Indian Ocean Territory
IQ Iraq
IR Iran
IS Iceland
IT Italy
JE Jersey
JM Jamaica
JO Jordan
JP Japan
KE Kenya
KG Kyrgyzstan
KH Cambodia
KI Kiribati
KM Comoros
KN St Kitts & Nevis
KP Korea (North)
KR Korea (South)
KW Kuwait
KY Cayman Islands
KZ Kazakhstan
LA Laos
LB Lebanon
LC St Lucia
LI Liechtenstein
LK Sri Lanka
LR Liberia
LS Lesotho
LT Lithuania
LU Luxembourg
LV Latvia
LY Libya
MA Morocco
MC Monaco
MD Moldova
ME Montenegro
MF St Martin (French part)
MG Madagascar
MH Marshall Islands
MK Macedonia
ML Mali
MM Myanmar (Burma)
MN Mongolia
MO Macau
MP Northern Mariana Islands
MQ Martinique
MR Mauritania
MS Montserrat
MT Malta
MU Mauritius
MV Maldives
MW Malawi
MX Mexico
MY Malaysia
MZ Mozambique
NA Namibia
NC New Caledonia
NE Niger
NF Norfolk Island
NG Nigeria
NI Nicaragua
NL Netherlands
NO Norway
NP Nepal
NR Nauru
NU Niue
NZ New Zealand
OM Oman
PA Panama
PE Peru
PF French Polynesia
PG Papua New Guinea
PH Philippines
PK Pakistan
PL Poland
PM St Pierre & Miquelon
PN Pitcairn
PR Puerto Rico
PS Palestine
PT Portugal
PW Palau
PY Paraguay
QA Qatar
RE Reunion
RO Romania
RS Serbia
RU Russia
RW Rwanda
SA Saudi Arabia
SB Solomon Islands
SC Seychelles
SD Sudan
SE Sweden
SG Singapore
SH St Helena
SI Slovenia
SJ Svalbard & Jan Mayen
SK Slovakia
SL Sierra Leone
SM San Marino
SN Senegal
SO Somalia
SR Suriname
SS South Sudan
ST Sao Tome & Principe
SV El Salvador
SX Sint Maarten
SY Syria
SZ Swaziland
TC Turks & Caicos Is
TD Chad
TF French Southern & Antarctic Lands
TG Togo
TH Thailand
TJ Tajikistan
TK Tokelau
TL East Timor
TM Turkmenistan
TN Tunisia
TO Tonga
TR Turkey
TT Trinidad & Tobago
TV Tuvalu
TW Taiwan
TZ Tanzania
UA Ukraine
UG Uganda
UM US minor outlying islands
US United States
UY Uruguay
UZ Uzbekistan
VA Vatican City
VC St Vincent
VE Venezuela
VG Virgin Islands (UK)
VI Virgin Islands (US)
VN Vietnam
VU Vanuatu
WF Wallis & Futuna
WS Samoa (western)
YE Yemen
YT Mayotte
ZA South Africa
ZM Zambia
ZW Zimbabwe

View File

@ -0,0 +1,100 @@
# <pre>
# This file is in the public domain, so clarified as of
# 2009-05-17 by Arthur David Olson.
# Allowance for leapseconds added to each timezone file.
# The International Earth Rotation Service periodically uses leap seconds
# to keep UTC to within 0.9 s of UT1
# (which measures the true angular orientation of the earth in space); see
# Terry J Quinn, The BIPM and the accurate measure of time,
# Proc IEEE 79, 7 (July 1991), 894-905.
# There were no leap seconds before 1972, because the official mechanism
# accounting for the discrepancy between atomic time and the earth's rotation
# did not exist until the early 1970s.
# The correction (+ or -) is made at the given time, so lines
# will typically look like:
# Leap YEAR MON DAY 23:59:60 + R/S
# or
# Leap YEAR MON DAY 23:59:59 - R/S
# If the leapsecond is Rolling (R) the given time is local time
# If the leapsecond is Stationary (S) the given time is UTC
# Leap YEAR MONTH DAY HH:MM:SS CORR R/S
Leap 1972 Jun 30 23:59:60 + S
Leap 1972 Dec 31 23:59:60 + S
Leap 1973 Dec 31 23:59:60 + S
Leap 1974 Dec 31 23:59:60 + S
Leap 1975 Dec 31 23:59:60 + S
Leap 1976 Dec 31 23:59:60 + S
Leap 1977 Dec 31 23:59:60 + S
Leap 1978 Dec 31 23:59:60 + S
Leap 1979 Dec 31 23:59:60 + S
Leap 1981 Jun 30 23:59:60 + S
Leap 1982 Jun 30 23:59:60 + S
Leap 1983 Jun 30 23:59:60 + S
Leap 1985 Jun 30 23:59:60 + S
Leap 1987 Dec 31 23:59:60 + S
Leap 1989 Dec 31 23:59:60 + S
Leap 1990 Dec 31 23:59:60 + S
Leap 1992 Jun 30 23:59:60 + S
Leap 1993 Jun 30 23:59:60 + S
Leap 1994 Jun 30 23:59:60 + S
Leap 1995 Dec 31 23:59:60 + S
Leap 1997 Jun 30 23:59:60 + S
Leap 1998 Dec 31 23:59:60 + S
Leap 2005 Dec 31 23:59:60 + S
Leap 2008 Dec 31 23:59:60 + S
Leap 2012 Jun 30 23:59:60 + S
# INTERNATIONAL EARTH ROTATION AND REFERENCE SYSTEMS SERVICE (IERS)
#
# SERVICE INTERNATIONAL DE LA ROTATION TERRESTRE ET DES SYSTEMES DE REFERENCE
#
#
# SERVICE DE LA ROTATION TERRESTRE
# OBSERVATOIRE DE PARIS
# 61, Av. de l'Observatoire 75014 PARIS (France)
# Tel. : 33 (0) 1 40 51 22 26
# FAX : 33 (0) 1 40 51 22 91
# e-mail : (E-Mail Removed)
# http://hpiers.obspm.fr/eop-pc
#
# Paris, 5 January 2012
#
#
# Bulletin C 43
#
# To authorities responsible
# for the measurement and
# distribution of time
#
#
# UTC TIME STEP
# on the 1st of July 2012
#
#
# A positive leap second will be introduced at the end of June 2012.
# The sequence of dates of the UTC second markers will be:
#
# 2012 June 30, 23h 59m 59s
# 2012 June 30, 23h 59m 60s
# 2012 July 1, 0h 0m 0s
#
# The difference between UTC and the International Atomic Time TAI is:
#
# from 2009 January 1, 0h UTC, to 2012 July 1 0h UTC : UTC-TAI = - 34s
# from 2012 July 1, 0h UTC, until further notice : UTC-TAI = - 35s
#
# Leap seconds can be introduced in UTC at the end of the months of December
# or June, depending on the evolution of UT1-TAI. Bulletin C is mailed every
# six months, either to announce a time step in UTC or to confirm that there
# will be no time step at the next possible date.
#
#
# Daniel GAMBIS
# Head
# Earth Orientation Center of IERS
# Observatoire de Paris, France

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,28 @@
# <pre>
# This file is in the public domain, so clarified as of
# 2009-05-17 by Arthur David Olson.
# From Arthur David Olson (1989-04-05):
# On 1989-04-05, the U. S. House of Representatives passed (238-154) a bill
# establishing "Pacific Presidential Election Time"; it was not acted on
# by the Senate or signed into law by the President.
# You might want to change the "PE" (Presidential Election) below to
# "Q" (Quadrennial) to maintain three-character zone abbreviations.
# If you're really conservative, you might want to change it to "D".
# Avoid "L" (Leap Year), which won't be true in 2100.
# If Presidential Election Time is ever established, replace "XXXX" below
# with the year the law takes effect and uncomment the "##" lines.
# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S
## Rule Twilite XXXX max - Apr Sun>=1 2:00 1:00 D
## Rule Twilite XXXX max uspres Oct lastSun 2:00 1:00 PE
## Rule Twilite XXXX max uspres Nov Sun>=7 2:00 0 S
## Rule Twilite XXXX max nonpres Oct lastSun 2:00 0 S
# Zone NAME GMTOFF RULES/SAVE FORMAT [UNTIL]
## Zone America/Los_Angeles-PET -8:00 US P%sT XXXX
## -8:00 Twilite P%sT
# For now...
Link America/Los_Angeles US/Pacific-New ##

View File

@ -0,0 +1,390 @@
# <pre>
# This file is in the public domain, so clarified as of
# 2009-05-17 by Arthur David Olson.
# So much for footnotes about Saudi Arabia.
# Apparent noon times below are for Riyadh; your mileage will vary.
# Times were computed using formulas in the U.S. Naval Observatory's
# Almanac for Computers 1987; the formulas "will give EqT to an accuracy of
# [plus or minus two] seconds during the current year."
#
# Rounding to the nearest five seconds results in fewer than
# 256 different "time types"--a limit that's faced because time types are
# stored on disk as unsigned chars.
# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S
Rule sol87 1987 only - Jan 1 12:03:20s -0:03:20 -
Rule sol87 1987 only - Jan 2 12:03:50s -0:03:50 -
Rule sol87 1987 only - Jan 3 12:04:15s -0:04:15 -
Rule sol87 1987 only - Jan 4 12:04:45s -0:04:45 -
Rule sol87 1987 only - Jan 5 12:05:10s -0:05:10 -
Rule sol87 1987 only - Jan 6 12:05:40s -0:05:40 -
Rule sol87 1987 only - Jan 7 12:06:05s -0:06:05 -
Rule sol87 1987 only - Jan 8 12:06:30s -0:06:30 -
Rule sol87 1987 only - Jan 9 12:06:55s -0:06:55 -
Rule sol87 1987 only - Jan 10 12:07:20s -0:07:20 -
Rule sol87 1987 only - Jan 11 12:07:45s -0:07:45 -
Rule sol87 1987 only - Jan 12 12:08:10s -0:08:10 -
Rule sol87 1987 only - Jan 13 12:08:30s -0:08:30 -
Rule sol87 1987 only - Jan 14 12:08:55s -0:08:55 -
Rule sol87 1987 only - Jan 15 12:09:15s -0:09:15 -
Rule sol87 1987 only - Jan 16 12:09:35s -0:09:35 -
Rule sol87 1987 only - Jan 17 12:09:55s -0:09:55 -
Rule sol87 1987 only - Jan 18 12:10:15s -0:10:15 -
Rule sol87 1987 only - Jan 19 12:10:35s -0:10:35 -
Rule sol87 1987 only - Jan 20 12:10:55s -0:10:55 -
Rule sol87 1987 only - Jan 21 12:11:10s -0:11:10 -
Rule sol87 1987 only - Jan 22 12:11:30s -0:11:30 -
Rule sol87 1987 only - Jan 23 12:11:45s -0:11:45 -
Rule sol87 1987 only - Jan 24 12:12:00s -0:12:00 -
Rule sol87 1987 only - Jan 25 12:12:15s -0:12:15 -
Rule sol87 1987 only - Jan 26 12:12:30s -0:12:30 -
Rule sol87 1987 only - Jan 27 12:12:40s -0:12:40 -
Rule sol87 1987 only - Jan 28 12:12:55s -0:12:55 -
Rule sol87 1987 only - Jan 29 12:13:05s -0:13:05 -
Rule sol87 1987 only - Jan 30 12:13:15s -0:13:15 -
Rule sol87 1987 only - Jan 31 12:13:25s -0:13:25 -
Rule sol87 1987 only - Feb 1 12:13:35s -0:13:35 -
Rule sol87 1987 only - Feb 2 12:13:40s -0:13:40 -
Rule sol87 1987 only - Feb 3 12:13:50s -0:13:50 -
Rule sol87 1987 only - Feb 4 12:13:55s -0:13:55 -
Rule sol87 1987 only - Feb 5 12:14:00s -0:14:00 -
Rule sol87 1987 only - Feb 6 12:14:05s -0:14:05 -
Rule sol87 1987 only - Feb 7 12:14:10s -0:14:10 -
Rule sol87 1987 only - Feb 8 12:14:10s -0:14:10 -
Rule sol87 1987 only - Feb 9 12:14:15s -0:14:15 -
Rule sol87 1987 only - Feb 10 12:14:15s -0:14:15 -
Rule sol87 1987 only - Feb 11 12:14:15s -0:14:15 -
Rule sol87 1987 only - Feb 12 12:14:15s -0:14:15 -
Rule sol87 1987 only - Feb 13 12:14:15s -0:14:15 -
Rule sol87 1987 only - Feb 14 12:14:15s -0:14:15 -
Rule sol87 1987 only - Feb 15 12:14:10s -0:14:10 -
Rule sol87 1987 only - Feb 16 12:14:10s -0:14:10 -
Rule sol87 1987 only - Feb 17 12:14:05s -0:14:05 -
Rule sol87 1987 only - Feb 18 12:14:00s -0:14:00 -
Rule sol87 1987 only - Feb 19 12:13:55s -0:13:55 -
Rule sol87 1987 only - Feb 20 12:13:50s -0:13:50 -
Rule sol87 1987 only - Feb 21 12:13:45s -0:13:45 -
Rule sol87 1987 only - Feb 22 12:13:35s -0:13:35 -
Rule sol87 1987 only - Feb 23 12:13:30s -0:13:30 -
Rule sol87 1987 only - Feb 24 12:13:20s -0:13:20 -
Rule sol87 1987 only - Feb 25 12:13:10s -0:13:10 -
Rule sol87 1987 only - Feb 26 12:13:00s -0:13:00 -
Rule sol87 1987 only - Feb 27 12:12:50s -0:12:50 -
Rule sol87 1987 only - Feb 28 12:12:40s -0:12:40 -
Rule sol87 1987 only - Mar 1 12:12:30s -0:12:30 -
Rule sol87 1987 only - Mar 2 12:12:20s -0:12:20 -
Rule sol87 1987 only - Mar 3 12:12:05s -0:12:05 -
Rule sol87 1987 only - Mar 4 12:11:55s -0:11:55 -
Rule sol87 1987 only - Mar 5 12:11:40s -0:11:40 -
Rule sol87 1987 only - Mar 6 12:11:25s -0:11:25 -
Rule sol87 1987 only - Mar 7 12:11:15s -0:11:15 -
Rule sol87 1987 only - Mar 8 12:11:00s -0:11:00 -
Rule sol87 1987 only - Mar 9 12:10:45s -0:10:45 -
Rule sol87 1987 only - Mar 10 12:10:30s -0:10:30 -
Rule sol87 1987 only - Mar 11 12:10:15s -0:10:15 -
Rule sol87 1987 only - Mar 12 12:09:55s -0:09:55 -
Rule sol87 1987 only - Mar 13 12:09:40s -0:09:40 -
Rule sol87 1987 only - Mar 14 12:09:25s -0:09:25 -
Rule sol87 1987 only - Mar 15 12:09:10s -0:09:10 -
Rule sol87 1987 only - Mar 16 12:08:50s -0:08:50 -
Rule sol87 1987 only - Mar 17 12:08:35s -0:08:35 -
Rule sol87 1987 only - Mar 18 12:08:15s -0:08:15 -
Rule sol87 1987 only - Mar 19 12:08:00s -0:08:00 -
Rule sol87 1987 only - Mar 20 12:07:40s -0:07:40 -
Rule sol87 1987 only - Mar 21 12:07:25s -0:07:25 -
Rule sol87 1987 only - Mar 22 12:07:05s -0:07:05 -
Rule sol87 1987 only - Mar 23 12:06:50s -0:06:50 -
Rule sol87 1987 only - Mar 24 12:06:30s -0:06:30 -
Rule sol87 1987 only - Mar 25 12:06:10s -0:06:10 -
Rule sol87 1987 only - Mar 26 12:05:55s -0:05:55 -
Rule sol87 1987 only - Mar 27 12:05:35s -0:05:35 -
Rule sol87 1987 only - Mar 28 12:05:15s -0:05:15 -
Rule sol87 1987 only - Mar 29 12:05:00s -0:05:00 -
Rule sol87 1987 only - Mar 30 12:04:40s -0:04:40 -
Rule sol87 1987 only - Mar 31 12:04:25s -0:04:25 -
Rule sol87 1987 only - Apr 1 12:04:05s -0:04:05 -
Rule sol87 1987 only - Apr 2 12:03:45s -0:03:45 -
Rule sol87 1987 only - Apr 3 12:03:30s -0:03:30 -
Rule sol87 1987 only - Apr 4 12:03:10s -0:03:10 -
Rule sol87 1987 only - Apr 5 12:02:55s -0:02:55 -
Rule sol87 1987 only - Apr 6 12:02:35s -0:02:35 -
Rule sol87 1987 only - Apr 7 12:02:20s -0:02:20 -
Rule sol87 1987 only - Apr 8 12:02:05s -0:02:05 -
Rule sol87 1987 only - Apr 9 12:01:45s -0:01:45 -
Rule sol87 1987 only - Apr 10 12:01:30s -0:01:30 -
Rule sol87 1987 only - Apr 11 12:01:15s -0:01:15 -
Rule sol87 1987 only - Apr 12 12:00:55s -0:00:55 -
Rule sol87 1987 only - Apr 13 12:00:40s -0:00:40 -
Rule sol87 1987 only - Apr 14 12:00:25s -0:00:25 -
Rule sol87 1987 only - Apr 15 12:00:10s -0:00:10 -
Rule sol87 1987 only - Apr 16 11:59:55s 0:00:05 -
Rule sol87 1987 only - Apr 17 11:59:45s 0:00:15 -
Rule sol87 1987 only - Apr 18 11:59:30s 0:00:30 -
Rule sol87 1987 only - Apr 19 11:59:15s 0:00:45 -
Rule sol87 1987 only - Apr 20 11:59:05s 0:00:55 -
Rule sol87 1987 only - Apr 21 11:58:50s 0:01:10 -
Rule sol87 1987 only - Apr 22 11:58:40s 0:01:20 -
Rule sol87 1987 only - Apr 23 11:58:25s 0:01:35 -
Rule sol87 1987 only - Apr 24 11:58:15s 0:01:45 -
Rule sol87 1987 only - Apr 25 11:58:05s 0:01:55 -
Rule sol87 1987 only - Apr 26 11:57:55s 0:02:05 -
Rule sol87 1987 only - Apr 27 11:57:45s 0:02:15 -
Rule sol87 1987 only - Apr 28 11:57:35s 0:02:25 -
Rule sol87 1987 only - Apr 29 11:57:25s 0:02:35 -
Rule sol87 1987 only - Apr 30 11:57:15s 0:02:45 -
Rule sol87 1987 only - May 1 11:57:10s 0:02:50 -
Rule sol87 1987 only - May 2 11:57:00s 0:03:00 -
Rule sol87 1987 only - May 3 11:56:55s 0:03:05 -
Rule sol87 1987 only - May 4 11:56:50s 0:03:10 -
Rule sol87 1987 only - May 5 11:56:45s 0:03:15 -
Rule sol87 1987 only - May 6 11:56:40s 0:03:20 -
Rule sol87 1987 only - May 7 11:56:35s 0:03:25 -
Rule sol87 1987 only - May 8 11:56:30s 0:03:30 -
Rule sol87 1987 only - May 9 11:56:25s 0:03:35 -
Rule sol87 1987 only - May 10 11:56:25s 0:03:35 -
Rule sol87 1987 only - May 11 11:56:20s 0:03:40 -
Rule sol87 1987 only - May 12 11:56:20s 0:03:40 -
Rule sol87 1987 only - May 13 11:56:20s 0:03:40 -
Rule sol87 1987 only - May 14 11:56:20s 0:03:40 -
Rule sol87 1987 only - May 15 11:56:20s 0:03:40 -
Rule sol87 1987 only - May 16 11:56:20s 0:03:40 -
Rule sol87 1987 only - May 17 11:56:20s 0:03:40 -
Rule sol87 1987 only - May 18 11:56:20s 0:03:40 -
Rule sol87 1987 only - May 19 11:56:25s 0:03:35 -
Rule sol87 1987 only - May 20 11:56:25s 0:03:35 -
Rule sol87 1987 only - May 21 11:56:30s 0:03:30 -
Rule sol87 1987 only - May 22 11:56:35s 0:03:25 -
Rule sol87 1987 only - May 23 11:56:40s 0:03:20 -
Rule sol87 1987 only - May 24 11:56:45s 0:03:15 -
Rule sol87 1987 only - May 25 11:56:50s 0:03:10 -
Rule sol87 1987 only - May 26 11:56:55s 0:03:05 -
Rule sol87 1987 only - May 27 11:57:00s 0:03:00 -
Rule sol87 1987 only - May 28 11:57:10s 0:02:50 -
Rule sol87 1987 only - May 29 11:57:15s 0:02:45 -
Rule sol87 1987 only - May 30 11:57:25s 0:02:35 -
Rule sol87 1987 only - May 31 11:57:30s 0:02:30 -
Rule sol87 1987 only - Jun 1 11:57:40s 0:02:20 -
Rule sol87 1987 only - Jun 2 11:57:50s 0:02:10 -
Rule sol87 1987 only - Jun 3 11:58:00s 0:02:00 -
Rule sol87 1987 only - Jun 4 11:58:10s 0:01:50 -
Rule sol87 1987 only - Jun 5 11:58:20s 0:01:40 -
Rule sol87 1987 only - Jun 6 11:58:30s 0:01:30 -
Rule sol87 1987 only - Jun 7 11:58:40s 0:01:20 -
Rule sol87 1987 only - Jun 8 11:58:50s 0:01:10 -
Rule sol87 1987 only - Jun 9 11:59:05s 0:00:55 -
Rule sol87 1987 only - Jun 10 11:59:15s 0:00:45 -
Rule sol87 1987 only - Jun 11 11:59:30s 0:00:30 -
Rule sol87 1987 only - Jun 12 11:59:40s 0:00:20 -
Rule sol87 1987 only - Jun 13 11:59:50s 0:00:10 -
Rule sol87 1987 only - Jun 14 12:00:05s -0:00:05 -
Rule sol87 1987 only - Jun 15 12:00:15s -0:00:15 -
Rule sol87 1987 only - Jun 16 12:00:30s -0:00:30 -
Rule sol87 1987 only - Jun 17 12:00:45s -0:00:45 -
Rule sol87 1987 only - Jun 18 12:00:55s -0:00:55 -
Rule sol87 1987 only - Jun 19 12:01:10s -0:01:10 -
Rule sol87 1987 only - Jun 20 12:01:20s -0:01:20 -
Rule sol87 1987 only - Jun 21 12:01:35s -0:01:35 -
Rule sol87 1987 only - Jun 22 12:01:50s -0:01:50 -
Rule sol87 1987 only - Jun 23 12:02:00s -0:02:00 -
Rule sol87 1987 only - Jun 24 12:02:15s -0:02:15 -
Rule sol87 1987 only - Jun 25 12:02:25s -0:02:25 -
Rule sol87 1987 only - Jun 26 12:02:40s -0:02:40 -
Rule sol87 1987 only - Jun 27 12:02:50s -0:02:50 -
Rule sol87 1987 only - Jun 28 12:03:05s -0:03:05 -
Rule sol87 1987 only - Jun 29 12:03:15s -0:03:15 -
Rule sol87 1987 only - Jun 30 12:03:30s -0:03:30 -
Rule sol87 1987 only - Jul 1 12:03:40s -0:03:40 -
Rule sol87 1987 only - Jul 2 12:03:50s -0:03:50 -
Rule sol87 1987 only - Jul 3 12:04:05s -0:04:05 -
Rule sol87 1987 only - Jul 4 12:04:15s -0:04:15 -
Rule sol87 1987 only - Jul 5 12:04:25s -0:04:25 -
Rule sol87 1987 only - Jul 6 12:04:35s -0:04:35 -
Rule sol87 1987 only - Jul 7 12:04:45s -0:04:45 -
Rule sol87 1987 only - Jul 8 12:04:55s -0:04:55 -
Rule sol87 1987 only - Jul 9 12:05:05s -0:05:05 -
Rule sol87 1987 only - Jul 10 12:05:15s -0:05:15 -
Rule sol87 1987 only - Jul 11 12:05:20s -0:05:20 -
Rule sol87 1987 only - Jul 12 12:05:30s -0:05:30 -
Rule sol87 1987 only - Jul 13 12:05:40s -0:05:40 -
Rule sol87 1987 only - Jul 14 12:05:45s -0:05:45 -
Rule sol87 1987 only - Jul 15 12:05:50s -0:05:50 -
Rule sol87 1987 only - Jul 16 12:06:00s -0:06:00 -
Rule sol87 1987 only - Jul 17 12:06:05s -0:06:05 -
Rule sol87 1987 only - Jul 18 12:06:10s -0:06:10 -
Rule sol87 1987 only - Jul 19 12:06:15s -0:06:15 -
Rule sol87 1987 only - Jul 20 12:06:15s -0:06:15 -
Rule sol87 1987 only - Jul 21 12:06:20s -0:06:20 -
Rule sol87 1987 only - Jul 22 12:06:25s -0:06:25 -
Rule sol87 1987 only - Jul 23 12:06:25s -0:06:25 -
Rule sol87 1987 only - Jul 24 12:06:25s -0:06:25 -
Rule sol87 1987 only - Jul 25 12:06:30s -0:06:30 -
Rule sol87 1987 only - Jul 26 12:06:30s -0:06:30 -
Rule sol87 1987 only - Jul 27 12:06:30s -0:06:30 -
Rule sol87 1987 only - Jul 28 12:06:30s -0:06:30 -
Rule sol87 1987 only - Jul 29 12:06:25s -0:06:25 -
Rule sol87 1987 only - Jul 30 12:06:25s -0:06:25 -
Rule sol87 1987 only - Jul 31 12:06:25s -0:06:25 -
Rule sol87 1987 only - Aug 1 12:06:20s -0:06:20 -
Rule sol87 1987 only - Aug 2 12:06:15s -0:06:15 -
Rule sol87 1987 only - Aug 3 12:06:10s -0:06:10 -
Rule sol87 1987 only - Aug 4 12:06:05s -0:06:05 -
Rule sol87 1987 only - Aug 5 12:06:00s -0:06:00 -
Rule sol87 1987 only - Aug 6 12:05:55s -0:05:55 -
Rule sol87 1987 only - Aug 7 12:05:50s -0:05:50 -
Rule sol87 1987 only - Aug 8 12:05:40s -0:05:40 -
Rule sol87 1987 only - Aug 9 12:05:35s -0:05:35 -
Rule sol87 1987 only - Aug 10 12:05:25s -0:05:25 -
Rule sol87 1987 only - Aug 11 12:05:15s -0:05:15 -
Rule sol87 1987 only - Aug 12 12:05:05s -0:05:05 -
Rule sol87 1987 only - Aug 13 12:04:55s -0:04:55 -
Rule sol87 1987 only - Aug 14 12:04:45s -0:04:45 -
Rule sol87 1987 only - Aug 15 12:04:35s -0:04:35 -
Rule sol87 1987 only - Aug 16 12:04:25s -0:04:25 -
Rule sol87 1987 only - Aug 17 12:04:10s -0:04:10 -
Rule sol87 1987 only - Aug 18 12:04:00s -0:04:00 -
Rule sol87 1987 only - Aug 19 12:03:45s -0:03:45 -
Rule sol87 1987 only - Aug 20 12:03:30s -0:03:30 -
Rule sol87 1987 only - Aug 21 12:03:15s -0:03:15 -
Rule sol87 1987 only - Aug 22 12:03:00s -0:03:00 -
Rule sol87 1987 only - Aug 23 12:02:45s -0:02:45 -
Rule sol87 1987 only - Aug 24 12:02:30s -0:02:30 -
Rule sol87 1987 only - Aug 25 12:02:15s -0:02:15 -
Rule sol87 1987 only - Aug 26 12:02:00s -0:02:00 -
Rule sol87 1987 only - Aug 27 12:01:40s -0:01:40 -
Rule sol87 1987 only - Aug 28 12:01:25s -0:01:25 -
Rule sol87 1987 only - Aug 29 12:01:05s -0:01:05 -
Rule sol87 1987 only - Aug 30 12:00:50s -0:00:50 -
Rule sol87 1987 only - Aug 31 12:00:30s -0:00:30 -
Rule sol87 1987 only - Sep 1 12:00:10s -0:00:10 -
Rule sol87 1987 only - Sep 2 11:59:50s 0:00:10 -
Rule sol87 1987 only - Sep 3 11:59:35s 0:00:25 -
Rule sol87 1987 only - Sep 4 11:59:15s 0:00:45 -
Rule sol87 1987 only - Sep 5 11:58:55s 0:01:05 -
Rule sol87 1987 only - Sep 6 11:58:35s 0:01:25 -
Rule sol87 1987 only - Sep 7 11:58:15s 0:01:45 -
Rule sol87 1987 only - Sep 8 11:57:55s 0:02:05 -
Rule sol87 1987 only - Sep 9 11:57:30s 0:02:30 -
Rule sol87 1987 only - Sep 10 11:57:10s 0:02:50 -
Rule sol87 1987 only - Sep 11 11:56:50s 0:03:10 -
Rule sol87 1987 only - Sep 12 11:56:30s 0:03:30 -
Rule sol87 1987 only - Sep 13 11:56:10s 0:03:50 -
Rule sol87 1987 only - Sep 14 11:55:45s 0:04:15 -
Rule sol87 1987 only - Sep 15 11:55:25s 0:04:35 -
Rule sol87 1987 only - Sep 16 11:55:05s 0:04:55 -
Rule sol87 1987 only - Sep 17 11:54:45s 0:05:15 -
Rule sol87 1987 only - Sep 18 11:54:20s 0:05:40 -
Rule sol87 1987 only - Sep 19 11:54:00s 0:06:00 -
Rule sol87 1987 only - Sep 20 11:53:40s 0:06:20 -
Rule sol87 1987 only - Sep 21 11:53:15s 0:06:45 -
Rule sol87 1987 only - Sep 22 11:52:55s 0:07:05 -
Rule sol87 1987 only - Sep 23 11:52:35s 0:07:25 -
Rule sol87 1987 only - Sep 24 11:52:15s 0:07:45 -
Rule sol87 1987 only - Sep 25 11:51:55s 0:08:05 -
Rule sol87 1987 only - Sep 26 11:51:35s 0:08:25 -
Rule sol87 1987 only - Sep 27 11:51:10s 0:08:50 -
Rule sol87 1987 only - Sep 28 11:50:50s 0:09:10 -
Rule sol87 1987 only - Sep 29 11:50:30s 0:09:30 -
Rule sol87 1987 only - Sep 30 11:50:10s 0:09:50 -
Rule sol87 1987 only - Oct 1 11:49:50s 0:10:10 -
Rule sol87 1987 only - Oct 2 11:49:35s 0:10:25 -
Rule sol87 1987 only - Oct 3 11:49:15s 0:10:45 -
Rule sol87 1987 only - Oct 4 11:48:55s 0:11:05 -
Rule sol87 1987 only - Oct 5 11:48:35s 0:11:25 -
Rule sol87 1987 only - Oct 6 11:48:20s 0:11:40 -
Rule sol87 1987 only - Oct 7 11:48:00s 0:12:00 -
Rule sol87 1987 only - Oct 8 11:47:45s 0:12:15 -
Rule sol87 1987 only - Oct 9 11:47:25s 0:12:35 -
Rule sol87 1987 only - Oct 10 11:47:10s 0:12:50 -
Rule sol87 1987 only - Oct 11 11:46:55s 0:13:05 -
Rule sol87 1987 only - Oct 12 11:46:40s 0:13:20 -
Rule sol87 1987 only - Oct 13 11:46:25s 0:13:35 -
Rule sol87 1987 only - Oct 14 11:46:10s 0:13:50 -
Rule sol87 1987 only - Oct 15 11:45:55s 0:14:05 -
Rule sol87 1987 only - Oct 16 11:45:45s 0:14:15 -
Rule sol87 1987 only - Oct 17 11:45:30s 0:14:30 -
Rule sol87 1987 only - Oct 18 11:45:20s 0:14:40 -
Rule sol87 1987 only - Oct 19 11:45:05s 0:14:55 -
Rule sol87 1987 only - Oct 20 11:44:55s 0:15:05 -
Rule sol87 1987 only - Oct 21 11:44:45s 0:15:15 -
Rule sol87 1987 only - Oct 22 11:44:35s 0:15:25 -
Rule sol87 1987 only - Oct 23 11:44:25s 0:15:35 -
Rule sol87 1987 only - Oct 24 11:44:20s 0:15:40 -
Rule sol87 1987 only - Oct 25 11:44:10s 0:15:50 -
Rule sol87 1987 only - Oct 26 11:44:05s 0:15:55 -
Rule sol87 1987 only - Oct 27 11:43:55s 0:16:05 -
Rule sol87 1987 only - Oct 28 11:43:50s 0:16:10 -
Rule sol87 1987 only - Oct 29 11:43:45s 0:16:15 -
Rule sol87 1987 only - Oct 30 11:43:45s 0:16:15 -
Rule sol87 1987 only - Oct 31 11:43:40s 0:16:20 -
Rule sol87 1987 only - Nov 1 11:43:40s 0:16:20 -
Rule sol87 1987 only - Nov 2 11:43:35s 0:16:25 -
Rule sol87 1987 only - Nov 3 11:43:35s 0:16:25 -
Rule sol87 1987 only - Nov 4 11:43:35s 0:16:25 -
Rule sol87 1987 only - Nov 5 11:43:35s 0:16:25 -
Rule sol87 1987 only - Nov 6 11:43:40s 0:16:20 -
Rule sol87 1987 only - Nov 7 11:43:40s 0:16:20 -
Rule sol87 1987 only - Nov 8 11:43:45s 0:16:15 -
Rule sol87 1987 only - Nov 9 11:43:50s 0:16:10 -
Rule sol87 1987 only - Nov 10 11:43:55s 0:16:05 -
Rule sol87 1987 only - Nov 11 11:44:00s 0:16:00 -
Rule sol87 1987 only - Nov 12 11:44:05s 0:15:55 -
Rule sol87 1987 only - Nov 13 11:44:15s 0:15:45 -
Rule sol87 1987 only - Nov 14 11:44:20s 0:15:40 -
Rule sol87 1987 only - Nov 15 11:44:30s 0:15:30 -
Rule sol87 1987 only - Nov 16 11:44:40s 0:15:20 -
Rule sol87 1987 only - Nov 17 11:44:50s 0:15:10 -
Rule sol87 1987 only - Nov 18 11:45:05s 0:14:55 -
Rule sol87 1987 only - Nov 19 11:45:15s 0:14:45 -
Rule sol87 1987 only - Nov 20 11:45:30s 0:14:30 -
Rule sol87 1987 only - Nov 21 11:45:45s 0:14:15 -
Rule sol87 1987 only - Nov 22 11:46:00s 0:14:00 -
Rule sol87 1987 only - Nov 23 11:46:15s 0:13:45 -
Rule sol87 1987 only - Nov 24 11:46:30s 0:13:30 -
Rule sol87 1987 only - Nov 25 11:46:50s 0:13:10 -
Rule sol87 1987 only - Nov 26 11:47:10s 0:12:50 -
Rule sol87 1987 only - Nov 27 11:47:25s 0:12:35 -
Rule sol87 1987 only - Nov 28 11:47:45s 0:12:15 -
Rule sol87 1987 only - Nov 29 11:48:05s 0:11:55 -
Rule sol87 1987 only - Nov 30 11:48:30s 0:11:30 -
Rule sol87 1987 only - Dec 1 11:48:50s 0:11:10 -
Rule sol87 1987 only - Dec 2 11:49:10s 0:10:50 -
Rule sol87 1987 only - Dec 3 11:49:35s 0:10:25 -
Rule sol87 1987 only - Dec 4 11:50:00s 0:10:00 -
Rule sol87 1987 only - Dec 5 11:50:25s 0:09:35 -
Rule sol87 1987 only - Dec 6 11:50:50s 0:09:10 -
Rule sol87 1987 only - Dec 7 11:51:15s 0:08:45 -
Rule sol87 1987 only - Dec 8 11:51:40s 0:08:20 -
Rule sol87 1987 only - Dec 9 11:52:05s 0:07:55 -
Rule sol87 1987 only - Dec 10 11:52:30s 0:07:30 -
Rule sol87 1987 only - Dec 11 11:53:00s 0:07:00 -
Rule sol87 1987 only - Dec 12 11:53:25s 0:06:35 -
Rule sol87 1987 only - Dec 13 11:53:55s 0:06:05 -
Rule sol87 1987 only - Dec 14 11:54:25s 0:05:35 -
Rule sol87 1987 only - Dec 15 11:54:50s 0:05:10 -
Rule sol87 1987 only - Dec 16 11:55:20s 0:04:40 -
Rule sol87 1987 only - Dec 17 11:55:50s 0:04:10 -
Rule sol87 1987 only - Dec 18 11:56:20s 0:03:40 -
Rule sol87 1987 only - Dec 19 11:56:50s 0:03:10 -
Rule sol87 1987 only - Dec 20 11:57:20s 0:02:40 -
Rule sol87 1987 only - Dec 21 11:57:50s 0:02:10 -
Rule sol87 1987 only - Dec 22 11:58:20s 0:01:40 -
Rule sol87 1987 only - Dec 23 11:58:50s 0:01:10 -
Rule sol87 1987 only - Dec 24 11:59:20s 0:00:40 -
Rule sol87 1987 only - Dec 25 11:59:50s 0:00:10 -
Rule sol87 1987 only - Dec 26 12:00:20s -0:00:20 -
Rule sol87 1987 only - Dec 27 12:00:45s -0:00:45 -
Rule sol87 1987 only - Dec 28 12:01:15s -0:01:15 -
Rule sol87 1987 only - Dec 29 12:01:45s -0:01:45 -
Rule sol87 1987 only - Dec 30 12:02:15s -0:02:15 -
Rule sol87 1987 only - Dec 31 12:02:45s -0:02:45 -
# Riyadh is at about 46 degrees 46 minutes East: 3 hrs, 7 mins, 4 secs
# Before and after 1987, we'll operate on local mean solar time.
# Zone NAME GMTOFF RULES/SAVE FORMAT [UNTIL]
Zone Asia/Riyadh87 3:07:04 - zzz 1987
3:07:04 sol87 zzz 1988
3:07:04 - zzz
# For backward compatibility...
Link Asia/Riyadh87 Mideast/Riyadh87

Some files were not shown because too many files have changed in this diff Show More