diff --git a/Code/v2/js/PrayTimes.js b/Code/v2/js/PrayTimes.js index ff3bf56..0cf6f6f 100644 --- a/Code/v2/js/PrayTimes.js +++ b/Code/v2/js/PrayTimes.js @@ -1,61 +1,61 @@ //--------------------- Copyright Block ---------------------- /* -PrayTimes.js: Prayer Times Calculator (ver 2.3) -Copyright (C) 2007-2011 PrayTimes.org + PrayTimes.js: Prayer Times Calculator (ver 2.3) + Copyright (C) 2007-2011 PrayTimes.org -Developer: Hamid Zarrabi-Zadeh -License: GNU LGPL v3.0 + Developer: Hamid Zarrabi-Zadeh + License: GNU LGPL v3.0 -TERMS OF USE: - Permission is granted to use this code, with or - without modification, in any website or application - provided that credit is given to the original work - with a link back to PrayTimes.org. + TERMS OF USE: + Permission is granted to use this code, with or + without modification, in any website or application + provided that credit is given to the original work + with a link back to PrayTimes.org. -This program is distributed in the hope that it will -be useful, but WITHOUT ANY WARRANTY. + This program is distributed in the hope that it will + be useful, but WITHOUT ANY WARRANTY. -PLEASE DO NOT REMOVE THIS COPYRIGHT BLOCK. - -*/ + PLEASE DO NOT REMOVE THIS COPYRIGHT BLOCK. + + */ //--------------------- Help and Manual ---------------------- /* -User's Manual: -http://praytimes.org/manual + User's Manual: + http://praytimes.org/manual + + Calculation Formulas: + http://praytimes.org/calculation + -Calculation Formulas: -http://praytimes.org/calculation + //------------------------ User Interface ------------------------- -//------------------------ User Interface ------------------------- + getTimes (date, coordinates [, timeZone [, dst [, timeFormat]]]) + setMethod (method) // set calculation method + adjust (parameters) // adjust calculation parameters + tune (offsets) // tune times by given offsets - getTimes (date, coordinates [, timeZone [, dst [, timeFormat]]]) - - setMethod (method) // set calculation method - adjust (parameters) // adjust calculation parameters - tune (offsets) // tune times by given offsets + getMethod () // get calculation method + getSetting () // get current calculation parameters + getOffsets () // get current time offsets - getMethod () // get calculation method - getSetting () // get current calculation parameters - getOffsets () // get current time offsets + //------------------------- Sample Usage -------------------------- -//------------------------- Sample Usage -------------------------- + var PT = new PrayTimes('ISNA'); + var times = PT.getTimes(new Date(), [43, -80], -5); + document.write('Sunrise = '+ times.sunrise) - var PT = new PrayTimes('ISNA'); - var times = PT.getTimes(new Date(), [43, -80], -5); - document.write('Sunrise = '+ times.sunrise) + */ -*/ - //----------------------- PrayTimes Class ------------------------ @@ -63,510 +63,568 @@ http://praytimes.org/calculation function PrayTimes(method) { - //------------------------ Constants -------------------------- - var - - // Time Names - timeNames = { - imsak : 'Imsak', - fajr : 'Fajr', - sunrise : 'Sunrise', - dhuhr : 'Dhuhr', - asr : 'Asr', - sunset : 'Sunset', - maghrib : 'Maghrib', - isha : 'Isha', - midnight : 'Midnight' - }, - - - // Calculation Methods - methods = { - MWL: { - name: 'Muslim World League', - params: { fajr: 18, isha: 17 } }, - ISNA: { - name: 'Islamic Society of North America (ISNA)', - params: { fajr: 15, isha: 15 } }, - Egypt: { - name: 'Egyptian General Authority of Survey', - params: { fajr: 19.5, isha: 17.5 } }, - Makkah: { - name: 'Umm Al-Qura University, Makkah', - params: { fajr: 18.5, isha: '90 min' } }, // fajr was 19 degrees before 1430 hijri - Karachi: { - name: 'University of Islamic Sciences, Karachi', - params: { fajr: 18, isha: 18 } }, - Tehran: { - name: 'Institute of Geophysics, University of Tehran', - params: { fajr: 17.7, isha: 14, maghrib: 4.5, midnight: 'Jafari' } }, // isha is not explicitly specified in this method - Jafari: { - name: 'Shia Ithna-Ashari, Leva Institute, Qum', - params: { fajr: 16, isha: 14, maghrib: 4, midnight: 'Jafari' } } - }, - - - // Default Parameters in Calculation Methods - defaultParams = { - maghrib: '0 min', midnight: 'Standard' - - }, - - - //----------------------- Parameter Values ---------------------- - /* - - // Asr Juristic Methods - asrJuristics = [ - 'Standard', // Shafi`i, Maliki, Ja`fari, Hanbali - 'Hanafi' // Hanafi - ], - - - // Midnight Mode - midnightMethods = [ - 'Standard', // Mid Sunset to Sunrise - 'Jafari' // Mid Sunset to Fajr - ], - - - // Adjust Methods for Higher Latitudes - highLatMethods = [ - 'NightMiddle', // middle of night - 'AngleBased', // angle/60th of night - 'OneSeventh', // 1/7th of night - 'None' // No adjustment - ], - - - // Time Formats - timeFormats = [ - '24h', // 24-hour format - '12h', // 12-hour format - '12hNS', // 12-hour format with no suffix - 'Float' // floating point number - ], - */ - - - //---------------------- Default Settings -------------------- - - calcMethod = 'Makkah', - - // do not change anything here; use adjust method instead - setting = { - imsak : '10 min', - dhuhr : '0 min', - asr : 'Standard', - highLats : 'NightMiddle' - }, - - timeFormat = '24h', - timeSuffixes = ['am', 'pm'], - invalidTime = '-----', - - numIterations = 1, - offset = {}, - - - //----------------------- Local Variables --------------------- - - lat, lng, elv, // coordinates - timeZone, jDate; // time variables - - - //---------------------- Initialization ----------------------- - - - // set methods defaults - var defParams = defaultParams; - for (var i in methods) { - var params = methods[i].params; - for (var j in defParams) - if ((typeof(params[j]) == 'undefined')) - params[j] = defParams[j]; - }; - - // initialize settings - calcMethod = methods[method] ? method : calcMethod; - var params = methods[calcMethod].params; - for (var id in params) - setting[id] = params[id]; - - // init time offsets - for (var i in timeNames) - offset[i] = 0; - - - - //----------------------- Public Functions ------------------------ - return { - - - // set calculation method - setMethod: function(method) { - if (methods[method]) { - this.adjust(methods[method].params); - calcMethod = method; - } - }, - - - // set calculating parameters - adjust: function(params) { - for (var id in params) - setting[id] = params[id]; - }, - - - // set time offsets - tune: function(timeOffsets) { - for (var i in timeOffsets) - offset[i] = timeOffsets[i]; - }, - - - // get current calculation method - getMethod: function() { return calcMethod; }, - - // get current setting - getSetting: function() { return setting; }, - - // get current time offsets - getOffsets: function() { return offset; }, - - // get default calc parametrs - getDefaults: function() { return methods; }, - - - // return prayer times for a given date - getTimes: function(date, coords, timezone, dst, format) { - lat = 1* coords[0]; - lng = 1* coords[1]; - elv = coords[2] ? 1* coords[2] : 0; - timeFormat = format || timeFormat; - if (date.constructor === Date) - date = [date.getFullYear(), date.getMonth()+ 1, date.getDate()]; - if (typeof(timezone) == 'undefined' || timezone == 'auto') - timezone = this.getTimeZone(date); - if (typeof(dst) == 'undefined' || dst == 'auto') - dst = this.getDst(date); - timeZone = 1* timezone+ (1* dst ? 1 : 0); - jDate = this.julian(date[0], date[1], date[2])- lng/ (15* 24); - - return this.computeTimes(); - }, - - - // convert float time to the given format (see timeFormats) - getFormattedTime: function(time, format, suffixes) { - if (isNaN(time)) - return invalidTime; - if (format == 'Float') return time; - suffixes = suffixes || timeSuffixes; - - time = DMath.fixHour(time+ 0.5/ 60); // add 0.5 minutes to round - var hours = Math.floor(time); - var minutes = Math.floor((time- hours)* 60); - var suffix = (format == '12h') ? suffixes[hours < 12 ? 0 : 1] : ''; - var hour = (format == '24h') ? this.twoDigitsFormat(hours) : ((hours+ 12 -1)% 12+ 1); - return hour+ ':'+ this.twoDigitsFormat(minutes)+ (suffix ? ' '+ suffix : ''); - }, - - - //---------------------- Calculation Functions ----------------------- - - - // compute mid-day time - midDay: function(time) { - var eqt = this.sunPosition(jDate+ time).equation; - var noon = DMath.fixHour(12- eqt); - return noon; - }, - - - // compute the time at which sun reaches a specific angle below horizon - sunAngleTime: function(angle, time, direction) { - var decl = this.sunPosition(jDate+ time).declination; - var noon = this.midDay(time); - var t = 1/15* DMath.arccos((-DMath.sin(angle)- DMath.sin(decl)* DMath.sin(lat))/ - (DMath.cos(decl)* DMath.cos(lat))); - return noon+ (direction == 'ccw' ? -t : t); - }, - - - // compute asr time - asrTime: function(factor, time) { - var decl = this.sunPosition(jDate+ time).declination; - var angle = -DMath.arccot(factor+ DMath.tan(Math.abs(lat- decl))); - return this.sunAngleTime(angle, time); - }, - - - // compute declination angle of sun and equation of time - // Ref: http://aa.usno.navy.mil/faq/docs/SunApprox.php - sunPosition: function(jd) { - var D = jd - 2451545.0; - var g = DMath.fixAngle(357.529 + 0.98560028* D); - var q = DMath.fixAngle(280.459 + 0.98564736* D); - var L = DMath.fixAngle(q + 1.915* DMath.sin(g) + 0.020* DMath.sin(2*g)); - - var R = 1.00014 - 0.01671* DMath.cos(g) - 0.00014* DMath.cos(2*g); - var e = 23.439 - 0.00000036* D; - - var RA = DMath.arctan2(DMath.cos(e)* DMath.sin(L), DMath.cos(L))/ 15; - var eqt = q/15 - DMath.fixHour(RA); - var decl = DMath.arcsin(DMath.sin(e)* DMath.sin(L)); - - return {declination: decl, equation: eqt}; - }, - - - // convert Gregorian date to Julian day - // Ref: Astronomical Algorithms by Jean Meeus - julian: function(year, month, day) { - if (month <= 2) { - year -= 1; - month += 12; - }; - var A = Math.floor(year/ 100); - var B = 2- A+ Math.floor(A/ 4); - - var JD = Math.floor(365.25* (year+ 4716))+ Math.floor(30.6001* (month+ 1))+ day+ B- 1524.5; - return JD; - }, - - - //---------------------- Compute Prayer Times ----------------------- - - - // compute prayer times at given julian date - computePrayerTimes: function(times) { - times = this.dayPortion(times); - var params = setting; - - var imsak = this.sunAngleTime(this.eval(params.imsak), times.imsak, 'ccw'); - var fajr = this.sunAngleTime(this.eval(params.fajr), times.fajr, 'ccw'); - var sunrise = this.sunAngleTime(this.riseSetAngle(), times.sunrise, 'ccw'); - var dhuhr = this.midDay(times.dhuhr); - var asr = this.asrTime(this.asrFactor(params.asr), times.asr); - var sunset = this.sunAngleTime(this.riseSetAngle(), times.sunset);; - var maghrib = this.sunAngleTime(this.eval(params.maghrib), times.maghrib); - var isha = this.sunAngleTime(this.eval(params.isha), times.isha); - - return { - imsak: imsak, fajr: fajr, sunrise: sunrise, dhuhr: dhuhr, - asr: asr, sunset: sunset, maghrib: maghrib, isha: isha - }; - }, - - - // compute prayer times - computeTimes: function() { - // default times - var times = { - imsak: 5, fajr: 5, sunrise: 6, dhuhr: 12, - asr: 13, sunset: 18, maghrib: 18, isha: 18 - }; - - // main iterations - for (var i=1 ; i<=numIterations ; i++) - times = this.computePrayerTimes(times); - - times = this.adjustTimes(times); - - // add midnight time - times.midnight = (setting.midnight == 'Jafari') ? - times.sunset+ this.timeDiff(times.sunset, times.fajr)/ 2 : - times.sunset+ this.timeDiff(times.sunset, times.sunrise)/ 2; - - times = this.tuneTimes(times); - return this.modifyFormats(times); - }, - - - // adjust times - adjustTimes: function(times) { - var params = setting; - for (var i in times) - times[i] += timeZone- lng/ 15; - - if (params.highLats != 'None') - times = this.adjustHighLats(times); - - if (this.isMin(params.imsak)) - times.imsak = times.fajr- this.eval(params.imsak)/ 60; - if (this.isMin(params.maghrib)) - times.maghrib = times.sunset+ this.eval(params.maghrib)/ 60; - if (this.isMin(params.isha)) - times.isha = times.maghrib+ this.eval(params.isha)/ 60; - times.dhuhr += this.eval(params.dhuhr)/ 60; - - return times; - }, - - - // get asr shadow factor - asrFactor: function(asrParam) { - var factor = {Standard: 1, Hanafi: 2}[asrParam]; - return factor || this.eval(asrParam); - }, - - - // return sun angle for sunset/sunrise - riseSetAngle: function() { - //var earthRad = 6371009; // in meters - //var angle = DMath.arccos(earthRad/(earthRad+ elv)); - var angle = 0.0347* Math.sqrt(elv); // an approximation - return 0.833+ angle; - }, - - - // apply offsets to the times - tuneTimes: function(times) { - for (var i in times) - times[i] += offset[i]/ 60; - return times; - }, - - - // convert times to given time format - modifyFormats: function(times) { - for (var i in times) - times[i] = this.getFormattedTime(times[i], timeFormat); - return times; - }, - - - // adjust times for locations in higher latitudes - adjustHighLats: function(times) { - var params = setting; - var nightTime = this.timeDiff(times.sunset, times.sunrise); - - times.imsak = this.adjustHLTime(times.imsak, times.sunrise, this.eval(params.imsak), nightTime, 'ccw'); - times.fajr = this.adjustHLTime(times.fajr, times.sunrise, this.eval(params.fajr), nightTime, 'ccw'); - times.isha = this.adjustHLTime(times.isha, times.sunset, this.eval(params.isha), nightTime); - times.maghrib = this.adjustHLTime(times.maghrib, times.sunset, this.eval(params.maghrib), nightTime); - - return times; - }, - - - // adjust a time for higher latitudes - adjustHLTime: function(time, base, angle, night, direction) { - var portion = this.nightPortion(angle, night); - var timeDiff = (direction == 'ccw') ? - this.timeDiff(time, base): - this.timeDiff(base, time); - if (isNaN(time) || timeDiff > portion) - time = base+ (direction == 'ccw' ? -portion : portion); - return time; - }, - - - // the night portion used for adjusting times in higher latitudes - nightPortion: function(angle, night) { - var method = setting.highLats; - var portion = 1/2 // MidNight - if (method == 'AngleBased') - portion = 1/60* angle; - if (method == 'OneSeventh') - portion = 1/7; - return portion* night; - }, - - - // convert hours to day portions - dayPortion: function(times) { - for (var i in times) - times[i] /= 24; - return times; - }, - - - //---------------------- Time Zone Functions ----------------------- - - - // get local time zone - getTimeZone: function(date) { - var year = date[0]; - var t1 = this.gmtOffset([year, 0, 1]); - var t2 = this.gmtOffset([year, 6, 1]); - return Math.min(t1, t2); - }, - - - // get daylight saving for a given date - getDst: function(date) { - return 1* (this.gmtOffset(date) != this.getTimeZone(date)); - }, - - - // GMT offset for a given date - gmtOffset: function(date) { - var localDate = new Date(date[0], date[1]- 1, date[2], 12, 0, 0, 0); - var GMTString = localDate.toGMTString(); - var GMTDate = new Date(GMTString.substring(0, GMTString.lastIndexOf(' ')- 1)); - var hoursDiff = (localDate- GMTDate) / (1000* 60* 60); - return hoursDiff; - }, - - - //---------------------- Misc Functions ----------------------- - - // convert given string into a number - eval: function(str) { - return 1* (str+ '').split(/[^0-9.+-]/)[0]; - }, - - - // detect if input contains 'min' - isMin: function(arg) { - return (arg+ '').indexOf('min') != -1; - }, - - - // compute the difference between two times - timeDiff: function(time1, time2) { - return DMath.fixHour(time2- time1); - }, - - - // add a leading 0 if necessary - twoDigitsFormat: function(num) { - return (num <10) ? '0'+ num : num; - } - -}} + //------------------------ Constants -------------------------- + var + + // Time Names + timeNames = { + imsak: 'Imsak', + fajr: 'Fajr', + sunrise: 'Sunrise', + dhuhr: 'Dhuhr', + asr: 'Asr', + sunset: 'Sunset', + maghrib: 'Maghrib', + isha: 'Isha', + midnight: 'Midnight' + }, + + + // Calculation Methods + methods = { + MWL: { + name: 'Muslim World League', + params: {fajr: 18, isha: 17} + }, + ISNA: { + name: 'Islamic Society of North America (ISNA)', + params: {fajr: 15, isha: 15} + }, + Egypt: { + name: 'Egyptian General Authority of Survey', + params: {fajr: 19.5, isha: 17.5} + }, + Makkah: { + name: 'Umm Al-Qura University, Makkah', + params: {fajr: 18.5, isha: '90 min'} + }, // fajr was 19 degrees before 1430 hijri + Karachi: { + name: 'University of Islamic Sciences, Karachi', + params: {fajr: 18, isha: 18} + }, + Tehran: { + name: 'Institute of Geophysics, University of Tehran', + params: {fajr: 17.7, isha: 14, maghrib: 4.5, midnight: 'Jafari'} + }, // isha is not explicitly specified in this method + Jafari: { + name: 'Shia Ithna-Ashari, Leva Institute, Qum', + params: {fajr: 16, isha: 14, maghrib: 4, midnight: 'Jafari'} + } + }, + + + // Default Parameters in Calculation Methods + defaultParams = { + maghrib: '0 min', midnight: 'Standard' + + }, + + + //----------------------- Parameter Values ---------------------- + /* + + // Asr Juristic Methods + asrJuristics = [ + 'Standard', // Shafi`i, Maliki, Ja`fari, Hanbali + 'Hanafi' // Hanafi + ], + + + // Midnight Mode + midnightMethods = [ + 'Standard', // Mid Sunset to Sunrise + 'Jafari' // Mid Sunset to Fajr + ], + + + // Adjust Methods for Higher Latitudes + highLatMethods = [ + 'NightMiddle', // middle of night + 'AngleBased', // angle/60th of night + 'OneSeventh', // 1/7th of night + 'None' // No adjustment + ], + + + // Time Formats + timeFormats = [ + '24h', // 24-hour format + '12h', // 12-hour format + '12hNS', // 12-hour format with no suffix + 'Float' // floating point number + ], + */ + + + //---------------------- Default Settings -------------------- + + calcMethod = 'Makkah', + + // do not change anything here; use adjust method instead + setting = { + imsak: '10 min', + dhuhr: '0 min', + asr: 'Standard', + highLats: 'NightMiddle' + }, + + timeFormat = '24h', + timeSuffixes = ['am', 'pm'], + invalidTime = '-----', + + numIterations = 1, + offset = {}, + + + //----------------------- Local Variables --------------------- + + lat, lng, elv, // coordinates + timeZone, jDate; // time variables + + + //---------------------- Initialization ----------------------- + + + // set methods defaults + var defParams = defaultParams; + for (var i in methods) { + var params = methods[i].params; + for (var j in defParams) + if ((typeof(params[j]) == 'undefined')) + params[j] = defParams[j]; + } + ; + + // initialize settings + calcMethod = methods[method] ? method : calcMethod; + var params = methods[calcMethod].params; + for (var id in params) + setting[id] = params[id]; + + // init time offsets + for (var i in timeNames) + offset[i] = 0; + + + //----------------------- Public Functions ------------------------ + return { + // set calculation method + setMethod: function (method) { + if (methods[method]) { + this.adjust(methods[method].params); + calcMethod = method; + } + }, + + + // set calculating parameters + adjust: function (params) { + for (var id in params) + setting[id] = params[id]; + }, + + + // set time offsets + tune: function (timeOffsets) { + for (var i in timeOffsets) + offset[i] = timeOffsets[i]; + }, + + + // get current calculation method + getMethod: function () { + return calcMethod; + }, + + // get current setting + getSetting: function () { + return setting; + }, + + // get current time offsets + getOffsets: function () { + return offset; + }, + + // get default calc parametrs + getDefaults: function () { + return methods; + }, + + + // return prayer times for a given date + getTimes: function (date, coords, timezone, dst, format) { + lat = 1 * coords[0]; + lng = 1 * coords[1]; + elv = coords[2] ? 1 * coords[2] : 0; + timeFormat = format || timeFormat; + if (date.constructor === Date) + date = [date.getFullYear(), date.getMonth() + 1, date.getDate()]; + if (typeof(timezone) == 'undefined' || timezone == 'auto') + timezone = this.getTimeZone(date); + if (typeof(dst) == 'undefined' || dst == 'auto') + dst = this.getDst(date); + timeZone = 1 * timezone + (1 * dst ? 1 : 0); + jDate = this.julian(date[0], date[1], date[2]) - lng / (15 * 24); + + return this.computeTimes(); + }, + + // return prayer times for a given date in users' location + getTimesFromMyLocation: function (date, callback) { + // check if location services are available + if (navigator.geolocation) { + // get current location + navigator.geolocation.getCurrentPosition(function (pos) { + /* get prayer times with the given date, current location, + and current Timezone Offset ( multiplied by -1 and divided by 60 as + the built in function returns the current offset in minutes and in the opposite sign + */ + var prayer_times = prayTimes.getTimes(date, [pos.coords.latitude, pos.coords.longitude], -1 * date.getTimezoneOffset() / 60); + callback(prayer_times); + }); + } else { + throw "Location couldn't be found"; + } + }, + + // convert float time to the given format (see timeFormats) + getFormattedTime: function (time, format, suffixes) { + if (isNaN(time)) + return invalidTime; + if (format == 'Float') return time; + suffixes = suffixes || timeSuffixes; + + time = DMath.fixHour(time + 0.5 / 60); // add 0.5 minutes to round + var hours = Math.floor(time); + var minutes = Math.floor((time - hours) * 60); + var suffix = (format == '12h') ? suffixes[hours < 12 ? 0 : 1] : ''; + var hour = (format == '24h') ? this.twoDigitsFormat(hours) : ((hours + 12 - 1) % 12 + 1); + return hour + ':' + this.twoDigitsFormat(minutes) + (suffix ? ' ' + suffix : ''); + }, + + + //---------------------- Calculation Functions ----------------------- + + + // compute mid-day time + midDay: function (time) { + var eqt = this.sunPosition(jDate + time).equation; + var noon = DMath.fixHour(12 - eqt); + return noon; + }, + + + // compute the time at which sun reaches a specific angle below horizon + sunAngleTime: function (angle, time, direction) { + var decl = this.sunPosition(jDate + time).declination; + var noon = this.midDay(time); + var t = 1 / 15 * DMath.arccos((-DMath.sin(angle) - DMath.sin(decl) * DMath.sin(lat)) / + (DMath.cos(decl) * DMath.cos(lat))); + return noon + (direction == 'ccw' ? -t : t); + }, + + + // compute asr time + asrTime: function (factor, time) { + var decl = this.sunPosition(jDate + time).declination; + var angle = -DMath.arccot(factor + DMath.tan(Math.abs(lat - decl))); + return this.sunAngleTime(angle, time); + }, + + + // compute declination angle of sun and equation of time + // Ref: http://aa.usno.navy.mil/faq/docs/SunApprox.php + sunPosition: function (jd) { + var D = jd - 2451545.0; + var g = DMath.fixAngle(357.529 + 0.98560028 * D); + var q = DMath.fixAngle(280.459 + 0.98564736 * D); + var L = DMath.fixAngle(q + 1.915 * DMath.sin(g) + 0.020 * DMath.sin(2 * g)); + + var R = 1.00014 - 0.01671 * DMath.cos(g) - 0.00014 * DMath.cos(2 * g); + var e = 23.439 - 0.00000036 * D; + + var RA = DMath.arctan2(DMath.cos(e) * DMath.sin(L), DMath.cos(L)) / 15; + var eqt = q / 15 - DMath.fixHour(RA); + var decl = DMath.arcsin(DMath.sin(e) * DMath.sin(L)); + + return {declination: decl, equation: eqt}; + }, + + + // convert Gregorian date to Julian day + // Ref: Astronomical Algorithms by Jean Meeus + julian: function (year, month, day) { + if (month <= 2) { + year -= 1; + month += 12; + } + ; + var A = Math.floor(year / 100); + var B = 2 - A + Math.floor(A / 4); + + var JD = Math.floor(365.25 * (year + 4716)) + Math.floor(30.6001 * (month + 1)) + day + B - 1524.5; + return JD; + }, + + + //---------------------- Compute Prayer Times ----------------------- + + + // compute prayer times at given julian date + computePrayerTimes: function (times) { + times = this.dayPortion(times); + var params = setting; + + var imsak = this.sunAngleTime(this.eval(params.imsak), times.imsak, 'ccw'); + var fajr = this.sunAngleTime(this.eval(params.fajr), times.fajr, 'ccw'); + var sunrise = this.sunAngleTime(this.riseSetAngle(), times.sunrise, 'ccw'); + var dhuhr = this.midDay(times.dhuhr); + var asr = this.asrTime(this.asrFactor(params.asr), times.asr); + var sunset = this.sunAngleTime(this.riseSetAngle(), times.sunset); + ; + var maghrib = this.sunAngleTime(this.eval(params.maghrib), times.maghrib); + var isha = this.sunAngleTime(this.eval(params.isha), times.isha); + + return { + imsak: imsak, fajr: fajr, sunrise: sunrise, dhuhr: dhuhr, + asr: asr, sunset: sunset, maghrib: maghrib, isha: isha + }; + }, + + + // compute prayer times + computeTimes: function () { + // default times + var times = { + imsak: 5, fajr: 5, sunrise: 6, dhuhr: 12, + asr: 13, sunset: 18, maghrib: 18, isha: 18 + }; + + // main iterations + for (var i = 1; i <= numIterations; i++) + times = this.computePrayerTimes(times); + + times = this.adjustTimes(times); + + // add midnight time + times.midnight = (setting.midnight == 'Jafari') ? + times.sunset + this.timeDiff(times.sunset, times.fajr) / 2 : + times.sunset + this.timeDiff(times.sunset, times.sunrise) / 2; + + times = this.tuneTimes(times); + return this.modifyFormats(times); + }, + + + // adjust times + adjustTimes: function (times) { + var params = setting; + for (var i in times) + times[i] += timeZone - lng / 15; + + if (params.highLats != 'None') + times = this.adjustHighLats(times); + + if (this.isMin(params.imsak)) + times.imsak = times.fajr - this.eval(params.imsak) / 60; + if (this.isMin(params.maghrib)) + times.maghrib = times.sunset + this.eval(params.maghrib) / 60; + if (this.isMin(params.isha)) + times.isha = times.maghrib + this.eval(params.isha) / 60; + times.dhuhr += this.eval(params.dhuhr) / 60; + + return times; + }, + + + // get asr shadow factor + asrFactor: function (asrParam) { + var factor = {Standard: 1, Hanafi: 2}[asrParam]; + return factor || this.eval(asrParam); + }, -//---------------------- Degree-Based Math Class ----------------------- + + // return sun angle for sunset/sunrise + riseSetAngle: function () { + //var earthRad = 6371009; // in meters + //var angle = DMath.arccos(earthRad/(earthRad+ elv)); + var angle = 0.0347 * Math.sqrt(elv); // an approximation + return 0.833 + angle; + }, + + + // apply offsets to the times + tuneTimes: function (times) { + for (var i in times) + times[i] += offset[i] / 60; + return times; + }, -var DMath = { + // convert times to given time format + modifyFormats: function (times) { + for (var i in times) + times[i] = this.getFormattedTime(times[i], timeFormat); + return times; + }, + + + // adjust times for locations in higher latitudes + adjustHighLats: function (times) { + var params = setting; + var nightTime = this.timeDiff(times.sunset, times.sunrise); + + times.imsak = this.adjustHLTime(times.imsak, times.sunrise, this.eval(params.imsak), nightTime, 'ccw'); + times.fajr = this.adjustHLTime(times.fajr, times.sunrise, this.eval(params.fajr), nightTime, 'ccw'); + times.isha = this.adjustHLTime(times.isha, times.sunset, this.eval(params.isha), nightTime); + times.maghrib = this.adjustHLTime(times.maghrib, times.sunset, this.eval(params.maghrib), nightTime); + + return times; + }, + + + // adjust a time for higher latitudes + adjustHLTime: function (time, base, angle, night, direction) { + var portion = this.nightPortion(angle, night); + var timeDiff = (direction == 'ccw') ? + this.timeDiff(time, base) : + this.timeDiff(base, time); + if (isNaN(time) || timeDiff > portion) + time = base + (direction == 'ccw' ? -portion : portion); + return time; + }, + + + // the night portion used for adjusting times in higher latitudes + nightPortion: function (angle, night) { + var method = setting.highLats; + var portion = 1 / 2 // MidNight + if (method == 'AngleBased') + portion = 1 / 60 * angle; + if (method == 'OneSeventh') + portion = 1 / 7; + return portion * night; + }, + + + // convert hours to day portions + dayPortion: function (times) { + for (var i in times) + times[i] /= 24; + return times; + }, + + + //---------------------- Time Zone Functions ----------------------- + - dtr: function(d) { return (d * Math.PI) / 180.0; }, - rtd: function(r) { return (r * 180.0) / Math.PI; }, + // get local time zone + getTimeZone: function (date) { + var year = date[0]; + var t1 = this.gmtOffset([year, 0, 1]); + var t2 = this.gmtOffset([year, 6, 1]); + return Math.min(t1, t2); + }, - sin: function(d) { return Math.sin(this.dtr(d)); }, - cos: function(d) { return Math.cos(this.dtr(d)); }, - tan: function(d) { return Math.tan(this.dtr(d)); }, - arcsin: function(d) { return this.rtd(Math.asin(d)); }, - arccos: function(d) { return this.rtd(Math.acos(d)); }, - arctan: function(d) { return this.rtd(Math.atan(d)); }, + // get daylight saving for a given date + getDst: function (date) { + return 1 * (this.gmtOffset(date) != this.getTimeZone(date)); + }, - arccot: function(x) { return this.rtd(Math.atan(1/x)); }, - arctan2: function(y, x) { return this.rtd(Math.atan2(y, x)); }, - fixAngle: function(a) { return this.fix(a, 360); }, - fixHour: function(a) { return this.fix(a, 24 ); }, + // GMT offset for a given date + gmtOffset: function (date) { + var localDate = new Date(date[0], date[1] - 1, date[2], 12, 0, 0, 0); + var GMTString = localDate.toGMTString(); + var GMTDate = new Date(GMTString.substring(0, GMTString.lastIndexOf(' ') - 1)); + var hoursDiff = (localDate - GMTDate) / (1000 * 60 * 60); + return hoursDiff; + }, + + + //---------------------- Misc Functions ----------------------- + + // convert given string into a number + eval: function (str) { + return 1 * (str + '').split(/[^0-9.+-]/)[0]; + }, + + + // detect if input contains 'min' + isMin: function (arg) { + return (arg + '').indexOf('min') != -1; + }, + + + // compute the difference between two times + timeDiff: function (time1, time2) { + return DMath.fixHour(time2 - time1); + }, + + + // add a leading 0 if necessary + twoDigitsFormat: function (num) { + return (num < 10) ? '0' + num : num; + } + + } +} + + +//---------------------- Degree-Based Math Class ----------------------- + + +var DMath = { - fix: function(a, b) { - a = a- b* (Math.floor(a/ b)); - return (a < 0) ? a+ b : a; - } + dtr: function (d) { + return (d * Math.PI) / 180.0; + }, + rtd: function (r) { + return (r * 180.0) / Math.PI; + }, + + sin: function (d) { + return Math.sin(this.dtr(d)); + }, + cos: function (d) { + return Math.cos(this.dtr(d)); + }, + tan: function (d) { + return Math.tan(this.dtr(d)); + }, + + arcsin: function (d) { + return this.rtd(Math.asin(d)); + }, + arccos: function (d) { + return this.rtd(Math.acos(d)); + }, + arctan: function (d) { + return this.rtd(Math.atan(d)); + }, + + arccot: function (x) { + return this.rtd(Math.atan(1 / x)); + }, + arctan2: function (y, x) { + return this.rtd(Math.atan2(y, x)); + }, + + fixAngle: function (a) { + return this.fix(a, 360); + }, + fixHour: function (a) { + return this.fix(a, 24); + }, + + fix: function (a, b) { + a = a - b * (Math.floor(a / b)); + return (a < 0) ? a + b : a; + } }