├── .gitignore ├── data.php ├── styles ├── css │ ├── screen.css │ └── gantti.css └── scss │ ├── screen.scss │ └── gantti.scss ├── readme.mdown ├── index.php └── lib ├── gantti.php └── calendar.php /.gitignore: -------------------------------------------------------------------------------- 1 | .sass-cache/b82a4c54823406b2e3fa108ceb67bf31fad36534/ 2 | sass.sh 3 | 4 | styles/css/.DS_Store 5 | 6 | lib/.DS_Store 7 | -------------------------------------------------------------------------------- /data.php: -------------------------------------------------------------------------------- 1 | 'Project 1', 7 | 'start' => '2012-04-20', 8 | 'end' => '2012-05-12' 9 | ); 10 | 11 | $data[] = array( 12 | 'label' => 'Project 2', 13 | 'start' => '2012-04-22', 14 | 'end' => '2012-05-22' 15 | ); 16 | 17 | $data[] = array( 18 | 'label' => 'Project 3', 19 | 'start' => '2012-05-25', 20 | 'end' => '2012-06-20' 21 | ); 22 | 23 | $data[] = array( 24 | 'label' => 'Project 4', 25 | 'start' => '2012-05-06', 26 | 'end' => '2012-06-17', 27 | 'class' => 'important', 28 | ); 29 | 30 | $data[] = array( 31 | 'label' => 'Project 5', 32 | 'start' => '2012-05-11', 33 | 'end' => '2012-06-03', 34 | 'class' => 'urgent', 35 | ); 36 | 37 | $data[] = array( 38 | 'label' => 'Project 6', 39 | 'start' => '2012-05-15', 40 | 'end' => '2012-07-03' 41 | ); 42 | 43 | $data[] = array( 44 | 'label' => 'Project 7', 45 | 'start' => '2012-06-01', 46 | 'end' => '2012-07-03', 47 | 'class' => 'important', 48 | ); 49 | 50 | $data[] = array( 51 | 'label' => 'Project 8', 52 | 'start' => '2012-06-01', 53 | 'end' => '2012-08-05' 54 | ); 55 | 56 | $data[] = array( 57 | 'label' => 'Project 9', 58 | 'start' => '2012-07-22', 59 | 'end' => '2012-09-05', 60 | 'class' => 'urgent', 61 | ); 62 | 63 | ?> -------------------------------------------------------------------------------- /styles/css/screen.css: -------------------------------------------------------------------------------- 1 | @import url(http://fonts.googleapis.com/css?family=Open+Sans:400italic,700italic,400,700); 2 | article, aside, details, figcaption, figure, footer, header, hgroup, nav, section { 3 | display: block; } 4 | 5 | * { 6 | margin: 0; 7 | padding: 0; } 8 | 9 | body { 10 | font-family: "Open Sans", "Helvetica Neue", Helvetica, Arial, sans-serif; 11 | font-size: 14px; 12 | line-height: 22px; 13 | background: #fdf6e3; 14 | color: #657b83; 15 | padding: 50px 0; } 16 | 17 | a { 18 | color: #d33682; 19 | text-decoration: none; 20 | border-bottom: 1px solid rgba(0, 0, 0, 0.1); } 21 | 22 | header, article { 23 | width: 500px; 24 | margin: 0 auto; 25 | padding: 50px 20px; } 26 | 27 | figure { 28 | font-size: 12px; 29 | line-height: 18px; } 30 | 31 | h1 { 32 | font-size: 30px; 33 | margin-bottom: 10px; 34 | text-transform: uppercase; 35 | color: #d33682; } 36 | 37 | h2 { 38 | color: #b58900; 39 | font-weight: normal; 40 | margin-bottom: 10px; } 41 | 42 | p { 43 | margin-bottom: 20px; } 44 | 45 | ul li { 46 | list-style: square; } 47 | 48 | article { 49 | padding-bottom: 100px; } 50 | 51 | pre { 52 | font-family: "Monaco", "Courier", monospace; 53 | position: relative; 54 | overflow: auto; 55 | background: #002b36; 56 | color: #93a1a1; 57 | box-shadow: rgba(0, 0, 0, 0.8) 0px 2px 10px inset; 58 | padding: 20px; 59 | font-size: 14px; 60 | line-height: 24px; 61 | margin-bottom: 24px; 62 | border-radius: 3px; } 63 | 64 | pre code { 65 | font-family: "Monaco", "Courier", monospace; 66 | background: none; 67 | padding: 0; 68 | white-space: pre; 69 | box-shadow: none; 70 | border-radius: 0; } 71 | -------------------------------------------------------------------------------- /styles/scss/screen.scss: -------------------------------------------------------------------------------- 1 | @import url(http://fonts.googleapis.com/css?family=Open+Sans:400italic,700italic,400,700); 2 | 3 | article, aside, details, figcaption, figure, footer, header, hgroup, nav, section { 4 | display: block; 5 | } 6 | 7 | * { 8 | margin: 0; 9 | padding: 0; 10 | } 11 | 12 | body { 13 | font-family: "Open Sans", "Helvetica Neue", Helvetica, Arial, sans-serif; 14 | font-size: 14px; 15 | line-height: 22px; 16 | background: #fdf6e3; 17 | color: #657b83; 18 | padding: 50px 0; 19 | } 20 | 21 | a { 22 | color: #d33682; 23 | text-decoration: none; 24 | border-bottom: 1px solid rgba(0,0,0, .1); 25 | } 26 | 27 | header, article { 28 | width: 500px; 29 | margin: 0 auto; 30 | padding: 50px 20px; 31 | } 32 | 33 | figure { 34 | font-size: 12px; 35 | line-height: 18px; 36 | } 37 | 38 | h1 { 39 | font-size: 30px; 40 | margin-bottom: 10px; 41 | text-transform: uppercase; 42 | color: #d33682; 43 | } 44 | 45 | h2 { 46 | color: #b58900; 47 | font-weight: normal; 48 | margin-bottom: 10px; 49 | } 50 | 51 | p { 52 | margin-bottom: 20px; 53 | } 54 | 55 | ul li { 56 | list-style: square; 57 | } 58 | 59 | article { 60 | padding-bottom: 100px; 61 | } 62 | 63 | pre { 64 | font-family: "Monaco", "Courier", monospace; 65 | position: relative; 66 | overflow: auto; 67 | background: #002b36; 68 | color: #93a1a1; 69 | box-shadow: rgba(0,0,0, .8) 0px 2px 10px inset; 70 | padding: 20px; 71 | font-size: 14px; 72 | line-height: 24px; 73 | margin-bottom: 24px; 74 | border-radius: 3px; 75 | } 76 | 77 | pre code { 78 | font-family: "Monaco", "Courier", monospace; 79 | background: none; 80 | padding: 0; 81 | white-space: pre; 82 | box-shadow: none; 83 | border-radius: 0; 84 | } 85 | -------------------------------------------------------------------------------- /readme.mdown: -------------------------------------------------------------------------------- 1 | # Gantti 2 | 3 | A simple PHP Gantt Class 4 | 5 | ## Features 6 | 7 | - Generates valid HTML5 8 | - Very easy to customize with SASS stylesheet 9 | - Works in all major browsers including IE7, IE8 and IE9 10 | - No javascript required 11 | 12 | ## Demo 13 | 14 | 15 | 16 | ## Usage 17 | 18 | ```php 19 | 20 | 'Project 1', 31 | 'start' => '2012-04-20', 32 | 'end' => '2012-05-12' 33 | ); 34 | 35 | $data[] = array( 36 | 'label' => 'Project 2', 37 | 'start' => '2012-04-22', 38 | 'end' => '2012-05-22', 39 | 'class' => 'important', 40 | ); 41 | 42 | $data[] = array( 43 | 'label' => 'Project 3', 44 | 'start' => '2012-05-25', 45 | 'end' => '2012-06-20' 46 | 'class' => 'urgent', 47 | ); 48 | 49 | $gantti = new Gantti($data, array( 50 | 'title' => 'Demo', 51 | 'cellwidth' => 25, 52 | 'cellheight' => 35 53 | )); 54 | 55 | echo $gantti; 56 | 57 | ?> 58 | 59 | ``` 60 | 61 | ## Data 62 | 63 | Data is defined as an associative array (see the example above). 64 | 65 | For each project you get the following options: 66 | 67 | - label: The label will be displayed in the sidebar 68 | - start: The start date. Must be in the following format: YYYY-MM-DD 69 | - end: The end date. Must be in the following format: YYYY-MM-DD 70 | - class: An optional class name. (available by default: important, urgent) 71 | 72 | 73 | ## Options 74 | 75 | ### title (optional, default: false) 76 | 77 | Set an optional title for your gantt diagram here. 78 | It will be displayed in the upper left corner. 79 | 80 | ### cellwidth (optional, default: 40) 81 | 82 | Set the width of all cells. 83 | 84 | ### cellheight (optional, default: 40) 85 | 86 | Set the height of all cells. 87 | 88 | ### today (optional, default: true) 89 | 90 | Show or hide the today marker. It will be displayed by default. 91 | 92 | ## Styles 93 | 94 | The default stylesheet is available as .scss ([SASS](http://sass-lang.com/)) It includes a set of predefined variables, which you can use to adjust the styles very easily. 95 | 96 | ## Colors 97 | The default color theme is an adaption of the wonderful 98 | [Solarized color theme by Ethan Schoonover](http://ethanschoonover.com/solarized) 99 | 100 | ## Author 101 | 102 | Bastian Allgeier 103 | 104 | 105 | 106 | ## License: 107 | 108 | MIT License - 109 | -------------------------------------------------------------------------------- /styles/css/gantti.css: -------------------------------------------------------------------------------- 1 | /* Sass Variables */ 2 | /* gantt styles */ 3 | .gantt { 4 | position: relative; 5 | overflow: hidden; 6 | color: #93a1a1; 7 | background: #002b36; } 8 | 9 | .gantt * { 10 | font-weight: normal; 11 | margin: 0; 12 | padding: 0; } 13 | 14 | .gantt li { 15 | list-style: none; } 16 | 17 | /* optional title */ 18 | .gantt figcaption { 19 | position: absolute; 20 | top: 25px; 21 | left: 20px; 22 | font-size: 20px; 23 | color: white; 24 | text-transform: uppercase; 25 | letter-spacing: 4px; } 26 | 27 | /* sidebar */ 28 | .gantt aside { 29 | position: absolute; 30 | left: 0; 31 | bottom: 0; 32 | top: 0; 33 | width: 199px; 34 | border-right: 1px solid #000b0d; 35 | z-index: 2; } 36 | 37 | .gantt aside:before { 38 | position: absolute; 39 | right: -7px; 40 | pointer-events: none; 41 | width: 7px; 42 | top: 0; 43 | bottom: 0; 44 | content: ""; 45 | border-left: 1px solid rgba(255, 255, 255, 0.03); 46 | background: -webkit-linear-gradient(left, rgba(0, 43, 54, 0.7), rgba(0, 43, 54, 0)); 47 | background: -moz-linear-gradient(left, rgba(0, 43, 54, 0.7), rgba(0, 43, 54, 0)); 48 | background: linear-gradient(left, rgba(0, 43, 54, 0.7), rgba(0, 43, 54, 0)); 49 | z-index: 3; } 50 | 51 | .gantt aside .gantt-labels { 52 | border-top: 1px solid #001f27; } 53 | 54 | .gantt aside .gantt-label strong { 55 | display: block; 56 | padding: 0 20px; 57 | color: #93a1a1; 58 | border-bottom: 1px solid #001f27; 59 | overflow: hidden; 60 | text-overflow: ellipsis; 61 | white-space: nowrap; } 62 | 63 | /* data section */ 64 | .gantt-data { 65 | position: relative; 66 | overflow-x: scroll; 67 | margin-left: 200px; 68 | white-space: nowrap; } 69 | 70 | /* data section header */ 71 | .gantt header .gantt-months { 72 | overflow: hidden; } 73 | 74 | .gantt header .gantt-month { 75 | float: left; 76 | text-align: center; } 77 | 78 | .gantt header .gantt-month strong { 79 | display: block; 80 | border-right: 1px solid #001f27; 81 | border-bottom: 1px solid #001f27; } 82 | 83 | .gantt header .gantt-day span { 84 | text-indent: 0; 85 | text-align: center; } 86 | 87 | .gantt header .gantt-day.today span { 88 | color: white; } 89 | 90 | /* data items */ 91 | .gantt-item { 92 | position: relative; } 93 | 94 | .gantt-days { 95 | overflow: hidden; } 96 | 97 | .gantt-day { 98 | float: left; } 99 | 100 | .gantt-day span { 101 | display: block; 102 | border-right: 1px solid #001f27; 103 | border-bottom: 1px solid #001f27; 104 | text-indent: -12000px; } 105 | 106 | .gantt-day.weekend span { 107 | background: #073642; } 108 | 109 | /* data blocks */ 110 | .gantt-block { 111 | position: absolute; 112 | top: 0; 113 | z-index: 1; 114 | margin: 4px; 115 | border-radius: 3px; 116 | -webkit-box-shadow: rgba(0, 0, 0, 0.9) 0 2px 6px, rgba(255, 255, 255, 0.2) 0 1px 0 inset; 117 | -moz-box-shadow: rgba(0, 0, 0, 0.9) 0 2px 6px, rgba(255, 255, 255, 0.2) 0 1px 0 inset; 118 | box-shadow: rgba(0, 0, 0, 0.9) 0 2px 6px, rgba(255, 255, 255, 0.2) 0 1px 0 inset; 119 | opacity: .9; } 120 | 121 | .gantt-block-label { 122 | display: block; 123 | color: white; 124 | padding: 5px 10px; } 125 | 126 | /* block colors */ 127 | .gantt-block { 128 | background: #268bd2; } 129 | 130 | .gantt-block.important { 131 | background: #b58900; } 132 | 133 | .gantt-block.urgent { 134 | background: #d33682; } 135 | 136 | /* today sign */ 137 | .gantt time { 138 | position: absolute; 139 | top: 0; 140 | width: 2px; 141 | background: white; 142 | bottom: 0; 143 | z-index: 1000; 144 | text-indent: -12000px; 145 | -webkit-box-shadow: rgba(0, 0, 0, 0.3) 0 0 10px; 146 | -moz-box-shadow: rgba(0, 0, 0, 0.3) 0 0 10px; 147 | box-shadow: rgba(0, 0, 0, 0.3) 0 0 10px; } 148 | 149 | .gantt time:before { 150 | position: absolute; 151 | content: ""; 152 | top: 0; 153 | left: -4px; 154 | border-left: 5px solid transparent; 155 | border-right: 5px solid transparent; 156 | border-top: 5px solid white; } 157 | 158 | /* scrollbar styles */ 159 | .gantt ::-webkit-scrollbar { 160 | background: #002b36; 161 | height: 10px; } 162 | 163 | .gantt ::-webkit-scrollbar-thumb { 164 | background: #93a1a1; 165 | -webkit-box-shadow: rgba(255, 255, 255, 0.1) 0 1px 0 inset; 166 | -moz-box-shadow: rgba(255, 255, 255, 0.1) 0 1px 0 inset; 167 | box-shadow: rgba(255, 255, 255, 0.1) 0 1px 0 inset; } 168 | 169 | /* selection styles */ 170 | .gantt ::-moz-selection { 171 | background: #fff; 172 | color: #000; } 173 | 174 | .gantt ::selection { 175 | background: #fff; 176 | color: #000; } 177 | -------------------------------------------------------------------------------- /index.php: -------------------------------------------------------------------------------- 1 | 'Demo', 11 | 'cellwidth' => 25, 12 | 'cellheight' => 35, 13 | 'today' => true 14 | )); 15 | 16 | ?> 17 | 18 | 19 | 20 | 21 | 22 | Mahatma Gantti – A simple PHP Gantt Class 23 | 24 | 25 | 26 | 27 | 28 | 31 | 32 | 33 | 34 | 35 | 36 |
37 | 38 |

Mahatma Gantti

39 |

A simple PHP Gantt Class

40 | 41 |
42 | 43 | 44 | 45 |
46 | 47 |

Download

48 | 49 |

50 | You can download the source for Gantti form Github:
51 | https://github.com/bastianallgeier/gantti 52 |

53 | 54 |

Features

55 | 56 |

57 |

    58 |
  • Generates valid HTML5
  • 59 |
  • Very easy to customize with SASS stylesheet
  • 60 |
  • Works in all major browsers including IE7, IE8 and IE9
  • 61 |
  • No javascript required
  • 62 |
63 |

64 | 65 |

Usage

66 | 67 |

 'Project 1',
 79 |   'start' => '2012-04-20', 
 80 |   'end'   => '2012-05-12'
 81 | );
 82 | 
 83 | \$data[] = array(
 84 |   'label' => 'Project 2',
 85 |   'start' => '2012-04-22', 
 86 |   'end'   => '2012-05-22', 
 87 |   'class' => 'important',
 88 | );
 89 | 
 90 | \$data[] = array(
 91 |   'label' => 'Project 3',
 92 |   'start' => '2012-05-25', 
 93 |   'end'   => '2012-06-20'
 94 |   'class' => 'urgent',
 95 | );
 96 | 
 97 | \$gantti = new Gantti(\$data, array(
 98 |   'title'      => 'Demo',
 99 |   'cellwidth'  => 25,
100 |   'cellheight' => 35
101 | ));
102 | 
103 | echo \$gantti;
104 | 
105 | ?>
106 | 
107 | ";
108 | 
109 | echo htmlentities(trim($code)); ?>
110 | 

111 | 112 |

Data

113 | 114 |

Data is defined as an associative array (see the example above).

115 | 116 |

117 | For each project you get the following options: 118 | 119 |

    120 |
  • label: The label will be displayed in the sidebar
  • 121 |
  • start: The start date. Must be in the following format: YYYY-MM-DD
  • 122 |
  • end: The end date. Must be in the following format: YYYY-MM-DD
  • 123 |
  • class: An optional class name. (available by default: important, urgent)
  • 124 |
125 | 126 |

127 | 128 |

Options

129 | 130 |

title (optional, default: false)

131 |

Set an optional title for your gantt diagram here.
It will be displayed in the upper left corner.

132 | 133 |

cellwidth (optional, default: 40)

134 |

Set the width of all cells.

135 | 136 |

cellheight (optional, default: 40)

137 |

Set the height of all cells.

138 | 139 |

today (optional, default: true)

140 |

Show or hide the today marker. It will be displayed by default.

141 | 142 |

Styles

143 | 144 |

145 | The default stylesheet is available as .scss (SASS) 146 | It includes a set of predefined variables, which you can use to adjust the styles very easily. 147 |

148 |

149 | You can check out the full SASS file over here: 150 | https://github.com/bastianallgeier/gantti/blob/master/styles/scss/gantti.scss 151 |

152 | 153 | 154 |

Colors

155 | 156 |

The default color theme is an adaption of the wonderful
Solarized color theme by Ethan Schoonover

157 | 158 |

Author

159 | 160 |

161 | Bastian Allgeier
162 | http://bastianallgeier.com
163 | Follow me on Twitter 164 | 165 |

166 | 167 |

License

168 | 169 | MIT License – http://www.opensource.org/licenses/mit-license.php 170 | 171 |
172 | 173 | Fork me on GitHub 174 | 175 | 176 | 177 | 178 | -------------------------------------------------------------------------------- /styles/scss/gantti.scss: -------------------------------------------------------------------------------- 1 | /* Sass Variables */ 2 | 3 | $color-background-dark: #002b36; 4 | $color-background-light: #073642; 5 | 6 | $color-lines: darken($color-background-dark, 3%); 7 | $color-today: #fff; 8 | 9 | $color-text: #93a1a1; 10 | $color-text-today: #fff; 11 | $color-text-label-aside: $color-text; 12 | $color-text-label-block: #fff; 13 | $color-text-title: #fff; 14 | 15 | $color-block-default: #268bd2; 16 | $color-block-important: #b58900; 17 | $color-block-urgent: #d33682; 18 | 19 | $color-scrollbar-back: $color-background-dark; 20 | $color-scrollbar-thumb: #93a1a1; 21 | 22 | $aside-width: 200px; 23 | 24 | /* gantt styles */ 25 | .gantt { 26 | position: relative; 27 | overflow: hidden; 28 | color: $color-text; 29 | background: $color-background-dark; 30 | } 31 | .gantt * { 32 | font-weight: normal; 33 | margin: 0; 34 | padding: 0; 35 | } 36 | .gantt li { 37 | list-style: none; 38 | } 39 | 40 | /* optional title */ 41 | .gantt figcaption { 42 | position: absolute; 43 | top: 25px; 44 | left: 20px; 45 | font-size: 20px; 46 | color: $color-text-title; 47 | text-transform: uppercase; 48 | letter-spacing: 4px; 49 | } 50 | 51 | /* sidebar */ 52 | .gantt aside { 53 | position: absolute; 54 | left: 0; 55 | bottom: 0; 56 | top: 0; 57 | width: $aside-width - 1; 58 | border-right: 1px solid darken($color-lines, 5%); 59 | z-index: 2; 60 | } 61 | .gantt aside:before { 62 | position: absolute; 63 | right: -7px; 64 | pointer-events: none; 65 | width: 7px; 66 | top: 0; 67 | bottom: 0; 68 | content: ""; 69 | border-left: 1px solid rgba(255,255,255, .03); 70 | background: -webkit-linear-gradient(left, rgba($color-background-dark,.7), rgba($color-background-dark,0)); 71 | background: -moz-linear-gradient(left, rgba($color-background-dark,.7), rgba($color-background-dark,0)); 72 | background: linear-gradient(left, rgba($color-background-dark,.7), rgba($color-background-dark,0)); 73 | z-index: 3; 74 | } 75 | .gantt aside .gantt-labels { 76 | border-top: 1px solid $color-lines; 77 | } 78 | .gantt aside .gantt-label strong { 79 | display: block; 80 | padding: 0 20px; 81 | color: $color-text-label-aside; 82 | border-bottom: 1px solid $color-lines; 83 | overflow: hidden; 84 | text-overflow: ellipsis; 85 | white-space: nowrap; 86 | } 87 | 88 | /* data section */ 89 | .gantt-data { 90 | position: relative; 91 | overflow-x: scroll; 92 | margin-left: $aside-width; 93 | white-space: nowrap; 94 | } 95 | 96 | /* data section header */ 97 | .gantt header .gantt-months { 98 | overflow: hidden; 99 | } 100 | .gantt header .gantt-month { 101 | float: left; 102 | text-align: center; 103 | } 104 | .gantt header .gantt-month strong { 105 | display: block; 106 | border-right: 1px solid $color-lines; 107 | border-bottom: 1px solid $color-lines; 108 | } 109 | .gantt header .gantt-day span { 110 | text-indent: 0; 111 | text-align: center; 112 | } 113 | .gantt header .gantt-day.today span { 114 | color: $color-text-today; 115 | } 116 | 117 | /* data items */ 118 | .gantt-item { 119 | position: relative; 120 | } 121 | .gantt-days { 122 | overflow: hidden; 123 | } 124 | .gantt-day { 125 | float: left; 126 | } 127 | .gantt-day span { 128 | display: block; 129 | border-right: 1px solid $color-lines; 130 | border-bottom: 1px solid $color-lines; 131 | text-indent: -12000px; 132 | } 133 | .gantt-day.weekend span { 134 | background: $color-background-light; 135 | } 136 | 137 | /* data blocks */ 138 | .gantt-block { 139 | position: absolute; 140 | top: 0; 141 | z-index: 1; 142 | margin: 4px; 143 | border-radius: 3px; 144 | -webkit-box-shadow: rgba(0,0,0, .9) 0 2px 6px, rgba(255,255,255, .2) 0 1px 0 inset; 145 | -moz-box-shadow: rgba(0,0,0, .9) 0 2px 6px, rgba(255,255,255, .2) 0 1px 0 inset; 146 | box-shadow: rgba(0,0,0, .9) 0 2px 6px, rgba(255,255,255, .2) 0 1px 0 inset; 147 | opacity: .9; 148 | } 149 | .gantt-block-label { 150 | display: block; 151 | color: $color-text-label-block; 152 | padding: 5px 10px; 153 | } 154 | 155 | /* block colors */ 156 | .gantt-block { 157 | background: $color-block-default; 158 | } 159 | .gantt-block.important { 160 | background: $color-block-important; 161 | } 162 | .gantt-block.urgent { 163 | background: $color-block-urgent; 164 | } 165 | 166 | /* today sign */ 167 | .gantt time { 168 | position: absolute; 169 | top: 0; 170 | width: 2px; 171 | background: $color-today; 172 | bottom: 0; 173 | z-index: 1000; 174 | text-indent: -12000px; 175 | -webkit-box-shadow: rgba(0,0,0, .3) 0 0 10px; 176 | -moz-box-shadow: rgba(0,0,0, .3) 0 0 10px; 177 | box-shadow: rgba(0,0,0, .3) 0 0 10px; 178 | } 179 | .gantt time:before { 180 | position: absolute; 181 | content: ""; 182 | top: 0; 183 | left: -4px; 184 | border-left: 5px solid transparent; 185 | border-right: 5px solid transparent; 186 | border-top: 5px solid $color-today; 187 | } 188 | 189 | /* scrollbar styles */ 190 | .gantt ::-webkit-scrollbar { 191 | background: $color-scrollbar-back; 192 | height: 10px; 193 | } 194 | .gantt ::-webkit-scrollbar-thumb { 195 | background: $color-scrollbar-thumb; 196 | -webkit-box-shadow: rgba(255,255,255, .1) 0 1px 0 inset; 197 | -moz-box-shadow: rgba(255,255,255, .1) 0 1px 0 inset; 198 | box-shadow: rgba(255,255,255, .1) 0 1px 0 inset; 199 | } 200 | 201 | /* selection styles */ 202 | .gantt ::-moz-selection { 203 | background: #fff; 204 | color: #000; 205 | } 206 | .gantt ::selection { 207 | background: #fff; 208 | color: #000; 209 | } -------------------------------------------------------------------------------- /lib/gantti.php: -------------------------------------------------------------------------------- 1 | false, 22 | 'cellwidth' => 40, 23 | 'cellheight' => 40, 24 | 'today' => true, 25 | ); 26 | 27 | $this->options = array_merge($defaults, $params); 28 | $this->cal = new Calendar(); 29 | $this->data = $data; 30 | $this->seconds = 60*60*24; 31 | 32 | $this->cellstyle = 'style="width: ' . $this->options['cellwidth'] . 'px; height: ' . $this->options['cellheight'] . 'px"'; 33 | 34 | // parse data and find first and last date 35 | $this->parse(); 36 | 37 | } 38 | 39 | function parse() { 40 | 41 | foreach($this->data as $d) { 42 | 43 | $this->blocks[] = array( 44 | 'label' => $d['label'], 45 | 'start' => $start = strtotime($d['start']), 46 | 'end' => $end = strtotime($d['end']), 47 | 'class' => @$d['class'] 48 | ); 49 | 50 | if(!$this->first || $this->first > $start) $this->first = $start; 51 | if(!$this->last || $this->last < $end) $this->last = $end; 52 | 53 | } 54 | 55 | $this->first = $this->cal->date($this->first); 56 | $this->last = $this->cal->date($this->last); 57 | 58 | $current = $this->first->month(); 59 | $lastDay = $this->last->month()->lastDay()->timestamp; 60 | 61 | // build the months 62 | while($current->lastDay()->timestamp <= $lastDay) { 63 | $month = $current->month(); 64 | $this->months[] = $month; 65 | foreach($month->days() as $day) { 66 | $this->days[] = $day; 67 | } 68 | $current = $current->next(); 69 | } 70 | 71 | } 72 | 73 | function render() { 74 | 75 | $html = array(); 76 | 77 | // common styles 78 | $cellstyle = 'style="line-height: ' . $this->options['cellheight'] . 'px; height: ' . $this->options['cellheight'] . 'px"'; 79 | $wrapstyle = 'style="width: ' . $this->options['cellwidth'] . 'px"'; 80 | $totalstyle = 'style="width: ' . (count($this->days)*$this->options['cellwidth']) . 'px"'; 81 | // start the diagram 82 | $html[] = '
'; 83 | 84 | // set a title if available 85 | if($this->options['title']) { 86 | $html[] = '
' . $this->options['title'] . '
'; 87 | } 88 | 89 | // sidebar with labels 90 | $html[] = ''; 97 | 98 | // data section 99 | $html[] = '
'; 100 | 101 | // data header section 102 | $html[] = '
'; 103 | 104 | // months headers 105 | $html[] = '
    '; 106 | foreach($this->months as $month) { 107 | $html[] = '
  • ' . $month->name() . '
  • '; 108 | } 109 | $html[] = '
'; 110 | 111 | // days headers 112 | $html[] = '
    '; 113 | foreach($this->days as $day) { 114 | 115 | $weekend = ($day->isWeekend()) ? ' weekend' : ''; 116 | $today = ($day->isToday()) ? ' today' : ''; 117 | 118 | $html[] = '
  • ' . $day->padded() . '
  • '; 119 | } 120 | $html[] = '
'; 121 | 122 | // end header 123 | $html[] = '
'; 124 | 125 | // main items 126 | $html[] = '
    '; 127 | 128 | foreach($this->blocks as $i => $block) { 129 | 130 | $html[] = '
  • '; 131 | 132 | // days 133 | $html[] = '
      '; 134 | foreach($this->days as $day) { 135 | 136 | $weekend = ($day->isWeekend()) ? ' weekend' : ''; 137 | $today = ($day->isToday()) ? ' today' : ''; 138 | 139 | $html[] = '
    • ' . $day . '
    • '; 140 | } 141 | $html[] = '
    '; 142 | 143 | // the block 144 | $days = (($block['end'] - $block['start']) / $this->seconds); 145 | $offset = (($block['start'] - $this->first->month()->timestamp) / $this->seconds); 146 | $top = round($i * ($this->options['cellheight'] + 1)); 147 | $left = round($offset * $this->options['cellwidth']); 148 | $width = round($days * $this->options['cellwidth'] - 9); 149 | $height = round($this->options['cellheight']-8); 150 | $class = ($block['class']) ? ' ' . $block['class'] : ''; 151 | $html[] = '' . $days . ''; 152 | $html[] = '
  • '; 153 | 154 | } 155 | 156 | $html[] = '
'; 157 | 158 | if($this->options['today']) { 159 | 160 | // today 161 | $today = $this->cal->today(); 162 | $offset = (($today->timestamp - $this->first->month()->timestamp) / $this->seconds); 163 | $left = round($offset * $this->options['cellwidth']) + round(($this->options['cellwidth'] / 2) - 1); 164 | 165 | if($today->timestamp > $this->first->month()->firstDay()->timestamp && $today->timestamp < $this->last->month()->lastDay()->timestamp) { 166 | $html[] = ''; 167 | } 168 | 169 | } 170 | 171 | // end data section 172 | $html[] = '
'; 173 | 174 | // end diagram 175 | $html[] = '
'; 176 | 177 | return implode('', $html); 178 | 179 | } 180 | 181 | function __toString() { 182 | return $this->render(); 183 | } 184 | 185 | } 186 | -------------------------------------------------------------------------------- /lib/calendar.php: -------------------------------------------------------------------------------- 1 | _ = $array; 9 | } 10 | 11 | function __toString() { 12 | $result = ''; 13 | foreach($this->_ as $date) { 14 | $result .= $date . '
'; 15 | } 16 | return $result; 17 | } 18 | 19 | function rewind() { 20 | reset($this->_); 21 | } 22 | 23 | function current() { 24 | return current($this->_); 25 | } 26 | 27 | function key() { 28 | return key($this->_); 29 | } 30 | 31 | function next() { 32 | return next($this->_); 33 | } 34 | 35 | function prev() { 36 | return prev($this->_); 37 | } 38 | 39 | function valid() { 40 | $key = key($this->_); 41 | $var = ($key !== null && $key !== false); 42 | return $var; 43 | } 44 | 45 | function count() { 46 | return count($this->_); 47 | } 48 | 49 | function first() { 50 | return array_shift($this->_); 51 | } 52 | 53 | function last() { 54 | return array_pop($this->_); 55 | } 56 | 57 | function nth($n) { 58 | $values = array_values($this->_); 59 | return isset($values[$n]) ? $values[$n] : null; 60 | } 61 | 62 | function indexOf($needle) { 63 | return array_search($needle, array_values($this->_)); 64 | } 65 | 66 | function toArray() { 67 | return $this->_; 68 | } 69 | 70 | function slice($offset=null, $limit=null) { 71 | if($offset === null && $limit === null) return $this; 72 | return new CalendarIterator(array_slice($this->_, $offset, $limit)); 73 | } 74 | 75 | function limit($limit) { 76 | return $this->slice(0, $limit); 77 | } 78 | 79 | } 80 | 81 | class CalendarObj { 82 | 83 | var $yearINT; 84 | var $monthINT; 85 | var $dayINT; 86 | var $hourINT; 87 | var $minuteINT; 88 | var $secondINT; 89 | var $timestamp = 0; 90 | 91 | function __construct($year=false, $month=1, $day=1, $hour=0, $minute=0, $second=0) { 92 | 93 | if(!$year) $year = date('Y'); 94 | if(!$month) $month = date('m'); 95 | if(!$day) $day = date('d'); 96 | 97 | $this->yearINT = intval($year); 98 | $this->monthINT = intval($month); 99 | $this->dayINT = intval($day); 100 | $this->hourINT = intval($hour); 101 | $this->minuteINT = intval($minute); 102 | $this->secondINT = intval($second); 103 | 104 | // convert this to timestamp 105 | $this->timestamp = mktime($hour, $minute, $second, $month, $day, $year); 106 | } 107 | 108 | function year($year=false) { 109 | if(!$year) $year = $this->yearINT; 110 | return new CalendarYear($year, 1, 1, 0, 0, 0); 111 | } 112 | 113 | function month($month=false) { 114 | if(!$month) $month = $this->monthINT; 115 | return new CalendarMonth($this->yearINT, $month, 1, 0, 0, 0); 116 | } 117 | 118 | function day($day=false) { 119 | if(!$day) $day = $this->dayINT; 120 | return new CalendarDay($this->yearINT, $this->monthINT, $day, 0, 0, 0); 121 | } 122 | 123 | function hour($hour=false) { 124 | if(!$hour) $hour = $this->hourINT; 125 | return new CalendarHour($this->yearINT, $this->monthINT, $this->dayINT, $hour, 0, 0); 126 | } 127 | 128 | function minute($minute=false) { 129 | if(!$minute) $minute = $this->minuteINT; 130 | return new CalendarMinute($this->yearINT, $this->monthINT, $this->dayINT, $this->hourINT, $minute, 0); 131 | } 132 | 133 | function second($second=false) { 134 | if(!$second) $second = $this->secondINT; 135 | return new CalendarSecond($this->yearINT, $this->monthINT, $this->dayINT, $this->hourINT, $this->minuteINT, $second); 136 | } 137 | 138 | function timestamp() { 139 | return $this->timestamp; 140 | } 141 | 142 | function __toString() { 143 | return date('Y-m-d H:i:s', $this->timestamp); 144 | } 145 | 146 | function format($format) { 147 | return date($format, $this->timestamp); 148 | } 149 | 150 | function iso() { 151 | return date(DATE_ISO, $this->timestamp); 152 | } 153 | 154 | function cookie() { 155 | return date(DATE_COOKIE, $this->timestamp); 156 | } 157 | 158 | function rss() { 159 | return date(DATE_RSS, $this->timestamp); 160 | } 161 | 162 | function atom() { 163 | return date(DATE_ATOM, $this->timestamp); 164 | } 165 | 166 | function mysql() { 167 | return date('Y-m-d H:i:s', $this->timestamp); 168 | } 169 | 170 | function time() { 171 | return strftime('%T', $this->timestamp); 172 | } 173 | 174 | function ampm() { 175 | return strftime('%p', $this->timestamp); 176 | } 177 | 178 | function modify($string) { 179 | $ts = (is_int($string)) ? $this->timestamp+$string : strtotime($string, $this->timestamp); 180 | 181 | list($year, $month, $day, $hour, $minute, $second) = explode('-', date('Y-m-d-H-i-s', $ts)); 182 | return new CalendarDay($year, $month, $day, $hour, $minute, $second); 183 | } 184 | 185 | function plus($string) { 186 | $modifier = (is_int($string)) ? $string : '+' . $string; 187 | return $this->modify($modifier); 188 | } 189 | 190 | function add($string) { 191 | return $this->plus($string); 192 | } 193 | 194 | function minus($string) { 195 | $modifier = (is_int($string)) ? -$string : '-' . $string; 196 | return $this->modify($modifier); 197 | } 198 | 199 | function sub($string) { 200 | return $this->minus($string); 201 | } 202 | 203 | function dmy() { 204 | return $this->format('d.m.Y'); 205 | } 206 | 207 | function padded() { 208 | return str_pad($this->int(),2,'0',STR_PAD_LEFT); 209 | } 210 | 211 | } 212 | 213 | class Calendar { 214 | 215 | static $now = 0; 216 | 217 | function __construct() { 218 | Calendar::$now = time(); 219 | } 220 | 221 | function years($start, $end) { 222 | $array = array(); 223 | foreach(range($start, $end) as $year) { 224 | $array[] = $this->year($year); 225 | } 226 | return new CalendarIterator($array); 227 | } 228 | 229 | function year($year) { 230 | return new CalendarYear($year, 1, 1, 0, 0, 0); 231 | } 232 | 233 | function months($year=false) { 234 | $year = new CalendarYear($year, 1, 1, 0, 0, 0); 235 | return $year->months(); 236 | } 237 | 238 | function month($year, $month) { 239 | return new CalendarMonth($year, $month, 1, 0, 0); 240 | } 241 | 242 | function week($year=false, $week=false) { 243 | return new CalendarWeek($year, $week); 244 | } 245 | 246 | function days($year=false) { 247 | $year = new CalendarYear($year); 248 | return $year->days(); 249 | } 250 | 251 | function day($year=false, $month=false, $day=false) { 252 | return new CalendarDay($year, $month, $day); 253 | } 254 | 255 | function date() { 256 | 257 | $args = func_get_args(); 258 | 259 | if(count($args) > 1) { 260 | 261 | $year = isset($args[0]) ? $args[0] : false; 262 | $month = isset($args[1]) ? $args[1] : 1; 263 | $day = isset($args[2]) ? $args[2] : 1; 264 | $hour = isset($args[3]) ? $args[3] : 0; 265 | $minute = isset($args[4]) ? $args[4] : 0; 266 | $second = isset($args[5]) ? $args[5] : 0; 267 | 268 | } else { 269 | 270 | if(isset($args[0])) { 271 | $ts = (is_int($args[0])) ? $args[0] : strtotime($args[0]); 272 | } else { 273 | $ts = time(); 274 | } 275 | 276 | if(!$ts) return false; 277 | 278 | list($year, $month, $day, $hour, $minute, $second) = explode('-', date('Y-m-d-H-i-s', $ts)); 279 | 280 | } 281 | 282 | return new CalendarDay($year, $month, $day, $hour, $minute, $second); 283 | 284 | } 285 | 286 | function today() { 287 | return $this->date('today'); 288 | } 289 | 290 | function now() { 291 | return $this->today(); 292 | } 293 | 294 | function tomorrow() { 295 | return $this->date('tomorrow'); 296 | } 297 | 298 | function yesterday() { 299 | return $this->date('yesterday'); 300 | } 301 | 302 | } 303 | 304 | class CalendarYear extends CalendarObj { 305 | 306 | function __toString() { 307 | return $this->format('Y'); 308 | } 309 | 310 | function int() { 311 | return $this->yearINT; 312 | } 313 | 314 | function months() { 315 | $array = array(); 316 | foreach(range(1, 12) as $month) { 317 | $array[] = $this->month($month); 318 | } 319 | return new CalendarIterator($array); 320 | } 321 | 322 | function month($month=1) { 323 | return new CalendarMonth($this->yearINT, $month); 324 | } 325 | 326 | function weeks() { 327 | $array = array(); 328 | $weeks = (int)date('W', mktime(0,0,0,12,31,$this->int))+1; 329 | foreach(range(1,$weeks) as $week) { 330 | $array[] = new CalendarWeek($this, $week); 331 | } 332 | return new CalendarIterator($array); 333 | } 334 | 335 | function week($week=1) { 336 | return new CalendarWeek($this, $week); 337 | } 338 | 339 | function countDays() { 340 | return (int)date('z', mktime(0,0,0,12,31,$this->yearINT))+1; 341 | } 342 | 343 | function days() { 344 | 345 | $days = $this->countDays(); 346 | $array = array(); 347 | $ts = false; 348 | 349 | for($x=0; $x<$days; $x++) { 350 | $ts = (!$ts) ? $this->timestamp : strtotime('tomorrow', $ts); 351 | $month = date('m', $ts); 352 | $day = date('d', $ts); 353 | $array[] = $this->month($month)->day($day); 354 | } 355 | 356 | return new CalendarIterator($array); 357 | 358 | } 359 | 360 | function next() { 361 | return $this->plus('1year')->year(); 362 | } 363 | 364 | function prev() { 365 | return $this->minus('1year')->year(); 366 | } 367 | 368 | function name() { 369 | return $this->int(); 370 | } 371 | 372 | function firstMonday() { 373 | $cal = new Calendar(); 374 | return $cal->date(strtotime('first monday of ' . date('Y', $this->timestamp))); 375 | } 376 | 377 | function firstSunday() { 378 | $cal = new Calendar(); 379 | return $cal->date(strtotime('first sunday of ' . date('Y', $this->timestamp))); 380 | } 381 | 382 | } 383 | 384 | class CalendarMonth extends CalendarObj { 385 | 386 | function __toString() { 387 | return $this->format('Y-m'); 388 | } 389 | 390 | function int() { 391 | return $this->monthINT; 392 | } 393 | 394 | function weeks($force=false) { 395 | 396 | $first = $this->firstDay(); 397 | $week = $first->week(); 398 | 399 | $currentMonth = $this->int(); 400 | $nextMonth = $this->next()->int(); 401 | 402 | $max = ($force) ? $force : 6; 403 | 404 | for($x=0; $x<$max; $x++) { 405 | 406 | // make sure not to add weeks without a single day in the same month 407 | if(!$force && $x>0 && $week->firstDay()->month()->int() != $currentMonth) break; 408 | 409 | $array[] = $week; 410 | 411 | // make sure not to add weeks without a single day in the same month 412 | if(!$force && $week->lastDay()->month()->int() != $currentMonth) break; 413 | 414 | $week = $week->next(); 415 | 416 | } 417 | 418 | return new CalendarIterator($array); 419 | 420 | } 421 | 422 | function countDays() { 423 | return date('t', $this->timestamp); 424 | } 425 | 426 | function firstDay() { 427 | return new CalendarDay($this->yearINT, $this->monthINT, 1); 428 | } 429 | 430 | function lastDay() { 431 | return new CalendarDay($this->yearINT, $this->monthINT, $this->countDays()); 432 | } 433 | 434 | function days() { 435 | 436 | // number of days per month 437 | $days = date('t', $this->timestamp); 438 | $array = array(); 439 | $ts = $this->firstDay()->timestamp(); 440 | 441 | foreach(range(1, $days) as $day) { 442 | $array[] = $this->day($day); 443 | } 444 | 445 | return new CalendarIterator($array); 446 | 447 | } 448 | 449 | function day($day=1) { 450 | return new CalendarDay($this->yearINT, $this->monthINT, $day); 451 | } 452 | 453 | function next() { 454 | return $this->plus('1month')->month(); 455 | } 456 | 457 | function prev() { 458 | return $this->minus('1month')->month(); 459 | } 460 | 461 | function name() { 462 | return strftime('%B', $this->timestamp); 463 | } 464 | 465 | function shortname() { 466 | return strftime('%b', $this->timestamp); 467 | } 468 | 469 | } 470 | 471 | class CalendarWeek extends CalendarObj { 472 | 473 | function __toString() { 474 | return $this->firstDay()->format('Y-m-d') . ' - ' . $this->lastDay()->format('Y-m-d'); 475 | } 476 | 477 | var $weekINT; 478 | 479 | function int() { 480 | return $this->weekINT; 481 | } 482 | 483 | function __construct($year=false, $week=false) { 484 | 485 | if(!$year) $year = date('Y'); 486 | if(!$week) $week = date('W'); 487 | 488 | $this->yearINT = intval($year); 489 | $this->weekINT = intval($week); 490 | 491 | $ts = strtotime('Thursday', strtotime($year . 'W' . $this->padded())); 492 | $monday = strtotime('-3days', $ts); 493 | 494 | parent::__construct(date('Y', $monday), date('m', $monday), date('d', $monday), 0, 0, 0); 495 | 496 | } 497 | 498 | function years() { 499 | $array = array(); 500 | $array[] = $this->firstDay()->year(); 501 | $array[] = $this->lastDay()->year(); 502 | 503 | // remove duplicates 504 | $array = array_unique($array); 505 | 506 | return new CalendarIterator($array); 507 | } 508 | 509 | function months() { 510 | $array = array(); 511 | $array[] = $this->firstDay()->month(); 512 | $array[] = $this->lastDay()->month(); 513 | 514 | // remove duplicates 515 | $array = array_unique($array); 516 | 517 | return new CalendarIterator($array); 518 | } 519 | 520 | function firstDay() { 521 | $cal = new Calendar(); 522 | return $cal->date($this->timestamp); 523 | } 524 | 525 | function lastDay() { 526 | $first = $this->firstDay(); 527 | return $first->plus('6 days'); 528 | } 529 | 530 | function days() { 531 | 532 | $day = $this->firstDay(); 533 | $array = array(); 534 | 535 | for($x=0; $x<7; $x++) { 536 | $array[] = $day; 537 | $day = $day->next(); 538 | } 539 | 540 | return new CalendarIterator($array); 541 | 542 | } 543 | 544 | function next() { 545 | 546 | $next = strtotime('Thursday next week', $this->timestamp); 547 | $year = date('Y', $next); 548 | $week = date('W', $next); 549 | 550 | return new CalendarWeek($year, $week); 551 | 552 | } 553 | 554 | function prev() { 555 | 556 | $prev = strtotime('Monday last week', $this->timestamp); 557 | $year = date('Y', $prev); 558 | $week = date('W', $prev); 559 | 560 | return new CalendarWeek($year, $week); 561 | 562 | } 563 | 564 | } 565 | 566 | 567 | class CalendarDay extends CalendarObj { 568 | 569 | function __toString() { 570 | return $this->format('Y-m-d'); 571 | } 572 | 573 | function int() { 574 | return $this->dayINT; 575 | } 576 | 577 | function week() { 578 | $week = date('W', $this->timestamp); 579 | $year = ($this->monthINT == 1 && $week > 5) ? $this->year()->prev() : $this->year(); 580 | return new CalendarWeek($year->int(), $week); 581 | } 582 | 583 | function next() { 584 | return $this->plus('1day'); 585 | } 586 | 587 | function prev() { 588 | return $this->minus('1day'); 589 | } 590 | 591 | function weekday() { 592 | return date('N', $this->timestamp); 593 | } 594 | 595 | function name() { 596 | return strftime('%A', $this->timestamp); 597 | } 598 | 599 | function shortname() { 600 | return strftime('%a', $this->timestamp); 601 | } 602 | 603 | function isToday() { 604 | $cal = new Calendar(); 605 | return $this == $cal->today(); 606 | } 607 | 608 | function isYesterday() { 609 | $cal = new Calendar(); 610 | return $this == $cal->yesterday(); 611 | } 612 | 613 | function isTomorrow() { 614 | $cal = new Calendar(); 615 | return $this == $cal->tomorrow(); 616 | } 617 | 618 | function isInThePast() { 619 | return ($this->timestamp < Calendar::$now) ? true : false; 620 | } 621 | 622 | function isInTheFuture() { 623 | return ($this->timestamp > Calendar::$now) ? true : false; 624 | } 625 | 626 | function isWeekend() { 627 | $num = $this->format('w'); 628 | return ($num == 6 || $num == 0) ? true : false; 629 | } 630 | 631 | function hours() { 632 | 633 | $obj = $this; 634 | $array = array(); 635 | 636 | while($obj->int() == $this->int()) { 637 | $array[] = $obj->hour(); 638 | $obj = $obj->plus('1hour'); 639 | } 640 | 641 | return new CalendarIterator($array); 642 | 643 | } 644 | 645 | } 646 | 647 | class CalendarHour extends CalendarObj { 648 | 649 | function int() { 650 | return $this->hourINT; 651 | } 652 | 653 | function minutes() { 654 | 655 | $obj = $this; 656 | $array = array(); 657 | 658 | while($obj->hourINT == $this->hourINT) { 659 | $array[] = $obj; 660 | $obj = $obj->plus('1minute')->minute(); 661 | } 662 | 663 | return new CalendarIterator($array); 664 | 665 | } 666 | 667 | function next() { 668 | return $this->plus('1hour')->hour(); 669 | } 670 | 671 | function prev() { 672 | return $this->minus('1hour')->hour(); 673 | } 674 | 675 | } 676 | 677 | class CalendarMinute extends CalendarObj { 678 | 679 | function int() { 680 | return $this->minuteINT; 681 | } 682 | 683 | function seconds() { 684 | 685 | $obj = $this; 686 | $array = array(); 687 | 688 | while($obj->minuteINT == $this->minuteINT) { 689 | $array[] = $obj; 690 | $obj = $obj->plus('1second')->second(); 691 | } 692 | 693 | return new CalendarIterator($array); 694 | 695 | } 696 | 697 | function next() { 698 | return $this->plus('1minute')->minute(); 699 | } 700 | 701 | function prev() { 702 | return $this->minus('1minute')->minute(); 703 | } 704 | 705 | } 706 | 707 | class CalendarSecond extends CalendarObj { 708 | 709 | function int() { 710 | return $this->secondINT; 711 | } 712 | 713 | function next() { 714 | return $this->plus('1second')->second(); 715 | } 716 | 717 | function prev() { 718 | return $this->minus('1second')->second(); 719 | } 720 | 721 | } 722 | 723 | 724 | 725 | --------------------------------------------------------------------------------