1<?php
2/**
3 * @package Horde
4 */
5class Horde_Block_Moon extends Horde_Core_Block
6{
7    /**
8     */
9    public function __construct($app, $params = array())
10    {
11        parent::__construct($app, $params);
12
13        $this->_name = _("Moon Phases");
14    }
15
16    /**
17     */
18    protected function _params()
19    {
20        return array(
21            'phase' => array(
22                'name' => _("Which phases"),
23                'type' => 'enum',
24                'default' => 'current',
25                'values' => array('current' => _("Current 4 Phases"),
26                                  'next' => _("Next 4 Phases"))),
27            'hemisphere' => array(
28                'name' => _("Hemisphere"),
29                'type' => 'enum',
30                'default' => 'northern',
31                'values' => array('northern' => _("Northern Hemisphere"),
32                                  'southern' => _("Southern Hemisphere"))),
33            );
34    }
35
36    /**
37     */
38    protected function _content()
39    {
40        $phases = $this->_calculateMoonPhases(date('Y'));
41        $now = time();
42
43        $lastNew = 0;
44        $lastNewStamp = 0;
45        $offset = 0;
46        foreach ($phases as $key => $val) {
47            if ($key < $now && $key > $lastNewStamp && $val == _("New Moon")) {
48                $lastNew = $offset;
49                $lastNewStamp = $key;
50            }
51            $offset++;
52        }
53
54        $dates = (isset($this->_params['phase']) && ($this->_params['phase'] == 'next'))
55            ? array_slice(array_keys($phases), $lastNew + 4, 4)
56            : array_slice(array_keys($phases), $lastNew, 4);
57
58        $location = (isset($this->_params['hemisphere']) && ($this->_params['hemisphere'] == 'northern'))
59            ? _("Northern Hemisphere")
60            : _("Southern Hemisphere");
61
62        $html = '<table width="100%" height="100%" cellspacing="0">' .
63            '<tr><td colspan="4" class="control"><strong>' . $location . '</strong></td></tr>' .
64            '<tr height="100%"><td width="25%" align="center">' .
65            Horde_Themes_Image::tag('block/moon/newmoon.png', array('alt' => _("New Moon"))) .
66            '<br />' . strftime($GLOBALS['prefs']->getValue('date_format_mini'), $dates[0]) .
67            '</td>';
68
69        $html .= '<td width="25%" align="center">';
70        if (isset($this->_params['hemisphere']) &&
71            ($this->_params['hemisphere'] == 'northern')) {
72            $html .= Horde_Themes_Image::tag('block/moon/lastquarter.png', array('alt' => _("First Quarter")));
73        } else {
74            $html .= Horde_Themes_Image::tag('block/moon/firstquarter.png', array('alt' => _("First Quarter")));
75        }
76        $html .= '<br />' . strftime($GLOBALS['prefs']->getValue('date_format_mini'), $dates[1]) . '</td>';
77
78        $html .= '<td width="25%" align="center">' .
79            Horde_Themes_Image::tag('block/moon/fullmoon.png', array('alt' => _("Full Moon"))) .
80            '<br />' . strftime($GLOBALS['prefs']->getValue('date_format_mini'), $dates[2]) . '</td>';
81
82        $html .= '<td width="25%" align="center">';
83        if (isset($this->_params['hemisphere']) &&
84            ($this->_params['hemisphere'] == 'northern')) {
85            $html .= Horde_Themes_Image::tag('block/moon/firstquarter.png', array('alt' => _("Last Quarter")));
86        } else {
87            $html .= Horde_Themes_Image::tag('block/moon/lastquarter.png', array('alt' => _("Last Quarter")));
88        }
89        $html .= '<br />' . strftime($GLOBALS['prefs']->getValue('date_format_mini'), $dates[3]) . '</td></tr></table>';
90
91        return $html;
92    }
93
94    /**
95     * Returns an array with all the phases of the moon for a whole
96     * year.
97     *
98     * Based on code from
99     * http://www.zend.com/codex.php?id=830&single=1 by Are Pedersen.
100     *
101     * Converted from Basic by Roger W. Sinnot, Sky & Telescope, March 1985.
102     * Converted from javascript by Are Pedersen 2002
103     * Javascript found at http://www.stellafane.com/moon_phase/moon_phase.htm
104     *
105     * @param integer $year  The four digit year to return the moon phases
106     *                       for.
107     *
108     * @return array  The moon phases.
109     */
110    private function _calculateMoonPhases($Y)
111    {
112        $R1 = 3.14159265 / 180;
113        $U  = false;
114        $K0 = intval(($Y - 1900) * 12.3685);
115        $T  = ($Y - 1899.5) / 100;
116        $T2 = $T * $T;
117        $T3 = $T * $T * $T;
118        $J0 = 2415020 + 29 * $K0;
119        $F0 = 0.0001178 * $T2 - 0.000000155 * $T3;
120        $F0 += (0.75933 + 0.53058868*$K0);
121        $F0 -= (0.000837 * $T + 0.000335 * $T2);
122        $M0  = $K0 * 0.08084821133;
123        $M0  = 360 * ($M0 - intval($M0)) + 359.2242;
124        $M0 -= 0.0000333 * $T2;
125        $M0 -= 0.00000347 * $T3;
126        $M1  = $K0 * 0.07171366128;
127        $M1  = 360 * ($M1 - intval($M1)) + 306.0253;
128        $M1 += 0.0107306 * $T2;
129        $M1 += 0.00001236 * $T3;
130        $B1  = $K0 * 0.08519585128;
131        $B1  = 360 * ($B1 - intval($B1)) + 21.2964;
132        $B1 -= 0.0016528 * $T2;
133        $B1 -= 0.00000239 * $T3;
134        for ($K9 = 0; $K9 <= 28; $K9 = $K9 + 0.5) {
135            $J = $J0 + 14 * $K9;
136            $F = $F0 + 0.765294 * $K9;
137            $K = $K9 / 2;
138            $M5 = ($M0 + $K * 29.10535608) * $R1;
139            $M6 = ($M1 + $K * 385.81691806) * $R1;
140            $B6 = ($B1 + $K * 390.67050646) * $R1;
141            $F -= 0.4068 * sin($M6);
142            $F += (0.1734 - 0.000393 * $T) * sin($M5);
143            $F += 0.0161 * sin(2 * $M6);
144            $F += 0.0104 * sin(2 * $B6);
145            $F -= 0.0074 * sin($M5 - $M6);
146            $F -= 0.0051 * sin($M5 + $M6);
147            $F += 0.0021 * sin(2 * $M5);
148            $F += 0.0010 * sin(2 * $B6 - $M6);
149
150            /* Add 1/2 minute for proper rounding to minutes per Sky &
151             * Tel article. */
152            $F += 0.5 / 1440;
153            $J += intval($F);
154            $F -= intval($F);
155
156            /* Convert from JD to Calendar Date. */
157            $julian = $J + round($F);
158            $parts  = explode('/', $this->_jdtogregorian($julian));
159            $stamp  = gmmktime(0, 0, 0, $parts[0], $parts[1], $parts[2]);
160
161            /* half K. */
162            if (($K9 - floor($K9)) > 0) {
163                if ($U) {
164                    /* New half. */
165                    $phases[$stamp] = _("First Half");
166                } else {
167                    /* Full half. */
168                    $phases[$stamp] = _("Last Half");
169                }
170            } else {
171                /* full K. */
172                if (!$U) {
173                    $phases[$stamp] = _("New Moon");
174                } else {
175                    $phases[$stamp] = _("Full Moon");
176                }
177                $U = !$U;
178            }
179        }
180
181        return $phases;
182    }
183
184    /**
185     * Checks if the jdtogregorian function exists, and if not calculates the
186     * gregorian date manually.
187     *
188     * @param int $julian  The julian date.
189     *
190     * @return string  m/d/Y
191     */
192    private function _jdtogregorian($julian)
193    {
194        if (function_exists('jdtogregorian')) {
195            return jdtogregorian($julian);
196        }
197
198        // From http://php.net/manual/en/function.jdtogregorian.php
199        $julian = $julian - 1721119;
200        $calc1 = 4 * $julian - 1;
201        $year = floor($calc1 / 146097);
202        $julian = floor($calc1 - 146097 * $year);
203        $day = floor($julian / 4);
204        $calc2 = 4 * $day + 3;
205        $julian = floor($calc2 / 1461);
206        $day = $calc2 - 1461 * $julian;
207        $day = floor(($day + 4) / 4);
208        $calc3 = 5 * $day - 3;
209        $month = floor($calc3 / 153);
210        $day = $calc3 - 153 * $month;
211        $day = floor(($day + 5) / 5);
212        $year = 100 * $year + $julian;
213
214        if ($month < 10) {
215            $month = $month + 3;
216        } else {
217            $month = $month - 9;
218            $year = $year + 1;
219        }
220
221        return "$month/$day/$year";
222    }
223
224}
225