From 94d8c8f70706531b8bdc72d2752fe86bde21b5aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois?= Date: Fri, 13 Oct 2023 06:43:02 +0200 Subject: [PATCH] First --- README.md | 3 + action.php | 29 + ajax.php | 57 + ajaxClearCache.php | 32 + ajaxPOI.php | 45 + conf/default.php | 27 + conf/metadata.php | 28 + images/bul_a.png | Bin 0 -> 1014 bytes images/bul_b.png | Bin 0 -> 1135 bytes images/bul_c.png | Bin 0 -> 1084 bytes images/calendar.png | Bin 0 -> 549 bytes images/cat_a.png | Bin 0 -> 159 bytes images/cat_ab.png | Bin 0 -> 169 bytes images/cat_abc.png | Bin 0 -> 181 bytes images/cat_ac.png | Bin 0 -> 169 bytes images/cat_b.png | Bin 0 -> 159 bytes images/cat_bc.png | Bin 0 -> 169 bytes images/cat_c.png | Bin 0 -> 159 bytes images/edit.png | Bin 0 -> 668 bytes images/empty.png | Bin 0 -> 166 bytes images/orange.png | Bin 0 -> 1135 bytes images/poi.png | Bin 0 -> 1232 bytes images/poiRed.png | Bin 0 -> 6423 bytes images/red.png | Bin 0 -> 1014 bytes images/repeat.png | Bin 0 -> 1003 bytes images/warning.png | Bin 0 -> 514 bytes images/wizard.png | Bin 0 -> 797 bytes lang/en/lang.php | 121 ++ lang/en/settings.php | 29 + lang/fr/lang.php | 121 ++ lang/fr/settings.php | 33 + lib/scheduleCitiesMap.js | 600 ++++++++ lib/scheduleForm.js | 145 ++ lib/scheduleTable.js | 93 ++ plugin.info.txt | 7 + poiDB.class.php | 113 ++ schedule.class.php | 64 + scheduleInseeCities.php | 2830 ++++++++++++++++++++++++++++++++++++++ scheduleRoot.class.php | 465 +++++++ schedules.class.php | 1663 ++++++++++++++++++++++ script.js | 79 ++ style.css | 528 +++++++ syntax/block.php | 205 +++ syntax/display.php | 136 ++ 44 files changed, 7453 insertions(+) create mode 100644 action.php create mode 100644 ajax.php create mode 100644 ajaxClearCache.php create mode 100644 ajaxPOI.php create mode 100644 conf/default.php create mode 100644 conf/metadata.php create mode 100644 images/bul_a.png create mode 100644 images/bul_b.png create mode 100644 images/bul_c.png create mode 100644 images/calendar.png create mode 100644 images/cat_a.png create mode 100644 images/cat_ab.png create mode 100644 images/cat_abc.png create mode 100644 images/cat_ac.png create mode 100644 images/cat_b.png create mode 100644 images/cat_bc.png create mode 100644 images/cat_c.png create mode 100644 images/edit.png create mode 100644 images/empty.png create mode 100644 images/orange.png create mode 100644 images/poi.png create mode 100644 images/poiRed.png create mode 100644 images/red.png create mode 100644 images/repeat.png create mode 100644 images/warning.png create mode 100644 images/wizard.png create mode 100644 lang/en/lang.php create mode 100644 lang/en/settings.php create mode 100644 lang/fr/lang.php create mode 100644 lang/fr/settings.php create mode 100644 lib/scheduleCitiesMap.js create mode 100644 lib/scheduleForm.js create mode 100644 lib/scheduleTable.js create mode 100644 plugin.info.txt create mode 100644 poiDB.class.php create mode 100644 schedule.class.php create mode 100644 scheduleInseeCities.php create mode 100644 scheduleRoot.class.php create mode 100644 schedules.class.php create mode 100644 script.js create mode 100644 style.css create mode 100644 syntax/block.php create mode 100644 syntax/display.php diff --git a/README.md b/README.md index c70854a..88310e2 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,5 @@ # schedule +DokuWiki Extensions : https://www.dokuwiki.org/plugin:schedule + +Event management for multi-organization. diff --git a/action.php b/action.php new file mode 100644 index 0000000..60ed07b --- /dev/null +++ b/action.php @@ -0,0 +1,29 @@ + + * + * Plugin Schedule: manage events per wiki @groups + */ + +if (!defined ('DOKU_INC')) + die (); +if (!defined ('DOKU_PLUGIN')) + define ('DOKU_PLUGIN', DOKU_INC.'lib/plugins/'); +require_once (DOKU_PLUGIN.'action.php'); + +class action_plugin_schedule extends DokuWiki_Action_Plugin { + + // ============================================================ + function register (Doku_Event_Handler $controller) { + $controller->register_hook ('DOKUWIKI_STARTED', 'AFTER', $this, '_defconf'); + } + function _defconf (&$event, $param) { + global $JSINFO; + $JSINFO ['schedule']['useMap'] = $this->getConf ('useMap'); + $JSINFO ['schedule']['defaultCenter'] = json_decode ($this->getConf ('defaultCenter'), true); + $JSINFO ['schedule']['defaultZoom'] = json_decode ($this->getConf ('defaultZoom'), true); + } + + // ============================================================ +} diff --git a/ajax.php b/ajax.php new file mode 100644 index 0000000..57c5e81 --- /dev/null +++ b/ajax.php @@ -0,0 +1,57 @@ + + * + * Plugin Schedule: manage events per wiki @groups + + $df_ : 31/12/2000 + $ds_ : 20001231 + $dn_ : 978217200 + $dt_ : array ("Y" => 2000, "m" => 12, "d" => "31) +*/ + +if (!defined ('DOKU_INC')) + define ('DOKU_INC', realpath (dirname (__FILE__).'/../../../').'/'); +global $conf; +require_once (DOKU_INC.'inc/init.php'); +if (!$conf['lang']) + $conf['lang'] = 'fr'; +require_once (DOKU_INC.'inc/common.php'); +require_once (DOKU_INC.'inc/auth.php'); + +// ============================================================ +if (isset ($_SERVER['REMOTE_USER'])) + $INFO['userinfo'] = $auth->getUserData ($auth->cleanUser ($_SERVER['REMOTE_USER'])); +$schdplug =& plugin_load ('syntax', 'schedule_block'); +$scheduleRoot = new scheduleRoot ($schdplug); +ob_start (); +switch ($_REQUEST ['schd']['action']) { + // schedule Table +case 'changeDate': + global $conf; + $dn_showDay = mktime (0, 0, 0, date ("n"), date ("j"), date("Y")); + $ds_showDay = trim ($scheduleRoot->df2ds ($_REQUEST['schd']['date'])); + if (preg_match ("#(?[0-9][0-9][0-9][0-9])(?[0-9][0-9])(?[0-9][0-9])#", $ds_showDay, $dt_showDay)) + $dn_showDay = mktime (0, 0, 0, $dt_showDay["m"], $dt_showDay["d"], $dt_showDay["Y"]); + $schedules = new schedules ($scheduleRoot, $_REQUEST['schd']['ns']); + $schedules->load (); + $schedules->printScheduleCalendar ($_REQUEST['schd']['mapId'], $dn_showDay); + break; + + // schedule Root Admin +case 'moveSchedules': + $scheduleRoot->manageAction ($_REQUEST['schd']); + $scheduleRoot->printForm (); + break; + +default : + ptnl ('No Ajax Function !'); +} +$text = ob_get_contents (); +ob_end_clean (); +foreach ($scheduleRoot->message as $type => $msg) + $text = '
'.$msg.'
'.$text; +ptln ($text); + +// ============================================================ diff --git a/ajaxClearCache.php b/ajaxClearCache.php new file mode 100644 index 0000000..9083847 --- /dev/null +++ b/ajaxClearCache.php @@ -0,0 +1,32 @@ + + * + * Plugin Schedule: manage events per wiki @groups + + */ + +if (!defined ('DOKU_INC')) + define ('DOKU_INC', realpath (dirname (__FILE__).'/../../../').'/'); +require_once (DOKU_INC.'inc/init.php'); +require_once (DOKU_INC.'inc/common.php'); +require_once (DOKU_INC.'inc/auth.php'); + +// ============================================================ +if (isset ($_SERVER['REMOTE_USER'])) + $INFO['userinfo'] = $auth->getUserData ($auth->cleanUser ($_SERVER['REMOTE_USER'])); +$schdplug =& plugin_load ('syntax', 'schedule_block'); +$scheduleRoot = new scheduleRoot ($schdplug); + +switch ($_REQUEST ['schedule']['action']) { +case 'clear': + $schedules = new schedules ($scheduleRoot, $_REQUEST ['schedule']['ns']); + $schedules->clearCache (); + break; +case 'clearAll': + $scheduleRoot->clearCache (); + break; +} + +// ============================================================ diff --git a/ajaxPOI.php b/ajaxPOI.php new file mode 100644 index 0000000..2bd74d5 --- /dev/null +++ b/ajaxPOI.php @@ -0,0 +1,45 @@ + + * + * Plugin Schedule: manage events per wiki @groups + + */ + +// faire un XML + +if (!defined ('DOKU_INC')) + define ('DOKU_INC', realpath (dirname (__FILE__).'/../../../').'/'); +require_once (DOKU_INC.'inc/init.php'); +require_once (DOKU_INC.'inc/common.php'); +require_once (DOKU_INC.'inc/auth.php'); +require_once (realpath (dirname (__FILE__)).'/poiDB.class.php'); + +// ============================================================ +if (isset ($_SERVER['REMOTE_USER'])) + $INFO['userinfo'] = $auth->getUserData ($auth->cleanUser ($_SERVER['REMOTE_USER'])); +$schdplug =& plugin_load ('syntax', 'schedule_block'); +$isAdmin = + isset ($INFO ['userinfo']) && + isset ($INFO ['userinfo']['grps']) && + in_array (trim ($schdplug->getConf ('adminGroup')), $INFO ['userinfo']['grps']); + +$poiDB = new poiDB ($schdplug); +if ('list' == $_REQUEST ['action']) + echo json_encode ($poiDB->getInsee ($_REQUEST['insee'])); +elseif ($isAdmin) { + switch ($_REQUEST ['action']) { + case 'add': + $poiDB->addLine ($_REQUEST ['line']); + break; + case 'remove': + $poiDB->removeLine ($_REQUEST ['line']); + break; + default: + die ('No Ajax Function !'); + } +} else + die ("No Ajax Function !"); + +// ============================================================ diff --git a/conf/default.php b/conf/default.php new file mode 100644 index 0000000..dfe02c7 --- /dev/null +++ b/conf/default.php @@ -0,0 +1,27 @@ + + * + * Options for the Schedule Plugin + */ +$conf['scheduleGroup'] = 'schedule'; +$conf['adminGroup'] = 'admin'; +$conf['dataDir'] = 'schedule'; +$conf['groupsDir'] = 'membres'; +$conf['noSchedule'] = '2010'; +$conf['scheduleDir'] = 'agenda'; +$conf['sharedDir'] = 'commun'; +$conf['scheduleWhat'] = 'A:Manifestation, Vote, Pétition, Action|B:Echange, Forum, Débat, Conférence, Projection, Exposition, Spectacle, Radio, TV, Pique-Nique|C:Assemblée Générale, Réunion, Mensuelle, Hebdomadaire'; +$conf['scheduleAudience'] = 'Ouvert à tous, Membres, Commission, Bureau'; +$conf['iconName'] = 'logo.png'; +$conf['iconWidth'] = 30; +$conf['repeatPosition'] = 'isolated'; +$conf['bubblePosition'] = 'right'; + +$conf['useMap'] = 1; +$conf['defaultCenter'] = "[47.95583, -2.54833]"; +$conf['defaultZoom'] = 14; +$conf['migrate'] = 0; +$conf['showOldValues'] = 0; +?> diff --git a/conf/metadata.php b/conf/metadata.php new file mode 100644 index 0000000..858a976 --- /dev/null +++ b/conf/metadata.php @@ -0,0 +1,28 @@ + + * + * Metadata for configuration manager plugin + * Additions for the schedule plugin + */ +$meta['scheduleGroup'] = array('string'); // who can use form +$meta['adminGroup'] = array('string'); // who can admin data +$meta['dataDir'] = array('string'); // root dir for XML files +$meta['groupsDir'] = array('string'); // dir group in wiki pages to find group names +$meta['noSchedule'] = array('string'); // forbiden name for group (ex skip annual archive dir) +$meta['scheduleDir'] = array('string'); // dir in wiki pages for events (contains subdir for group name or the shared dir) +$meta['sharedDir'] = array('string'); // dir for shared event in wiki pages +$meta['scheduleWhat'] = array('string'); // type event list +$meta['scheduleAudience'] = array('string'); // type audience list +$meta['iconName'] = array('string'); // file name containing logo in group dir +$meta['iconWidth'] = array('numeric'); // default logo width size +$meta['repeatPosition'] = array('multichoice', '_choices' => array('grouped', 'isolated')); // does the repeated events are group on top of form +$meta['bubblePosition'] = array('multichoice', '_choices' => array('left', 'right')); // tooltips position + +$meta['useMap'] = array('onoff'); +$meta['defaultCenter'] = array('string'); +$meta['defaultZoom'] = array('numeric'); +$meta['migrate'] = array('onoff'); // XXX to remove ? +$meta['showOldValues'] = array('onoff'); // XXX to remove ? +?> diff --git a/images/bul_a.png b/images/bul_a.png new file mode 100644 index 0000000000000000000000000000000000000000..541bb87dfef7f6281d38e64381f0f3a9e7ec06ac GIT binary patch literal 1014 zcmVPx#24YJ`L;(K){{a7>y{D4^000SaNLh0L01FcU01FcV0GgZ_00007bV*G`2iXf1 z6#^$@BgcsV00VYOL_t(I%YBqhh?Z3p$A9PC=b87NPiM+zm^9NCen8S-5k<==h?WVp zjv$a$LYp9nTC|EFlNLg>i4=)~3O5r(3lqe0T8xT_B4Rp|IXcbz@!WgQY4JYq+bB(c zxLmk!e)oSq?iGO6(m6(^_po+z5mlu)@Tvj@&;Um2ova}S%PpXtHN<@AO9vR8-ZNkb zwcMSxH=dVrWjO$4sM-w+MbRA|Z#Gjk1#AIk00*vR4HtvuWuRLgU+})M0r1G751IS) z9r@xfDj`|#>|%JQx9#&>c4Pff?NA)dLEydl{tXB>*$O*5yvjepnGtiwW84sy-32$^zLvil zlq1KiKn7aC)%jIUe}9LofV2#cIg_f2n0oM-GO4OO=8TK*-R*qr+dC`(5pb!xzP^e z^zo@6%_yqkt_h)?EiRd^%-^-2_h0tUl5-ZgUB~|W+tlj6=V@RMuy=Qp$DbXQEn7=Q zOYwL!$h3CxHvVpFpS1j{AXq=nybogJ*#U>a#9z3%Cg^1FJw)!+ay)`mbzg k-5QKn4TPx#24YJ`L;(K){{a7>y{D4^000SaNLh0L01FcU01FcV0GgZ_00007bV*G`2iXf1 z6$&@8OJK0hewaZ8+(DXrr;^c1tK+v?o#%{sNY3 zFa4sx3NW!2AA$e|NCA0!&*0TX+2L*0r-V^~U!o~`wbeCzr*Z%BQyL6Y#3 zdV5-Tk2lv!7Qg~kVBP!sM_=5#dH&s=OxIQGn8ZMq8qy$Wsv*WWlf>jb9ob%#Wp=NK zew|1>ue--fa~G`d&0UQb_HVxVVI|SN%p#RUsoM-I4Ybk^l>w7b9r4B=aMNdQr6x=! z5_fnteX1)k>2z`Z1NX=iM_;RaIgrM*Ru@eZkzOL!2-pm8#etqUX)NhA_di>8VTN?{Zq(V{J85LtKV%2V?|G@SwF$}DXsqGfd0Gt-y=^xD-)mbB_ufo)*+$f@ROA+VIgauad~g%Nyl2jr}fJG5x4cvtfA4+}?D z*EcVdV1NL$fa|BP>%`~hs@DmnEHqVbx0Pn0v=mwj%LTNTgDyuPlzen@=;*JL?srxy zu8U<=Eq5#c=JYjh?;EJyKhzg{oCEI^S1NfF+EK_O(rT%EacS`6!S7}d`p9G_Aszqf zLKmoj2U<}nPh9nRB~2S-)oS5W6Js!~9B#F1?&54?@z`bk`M@^|hh5|>u+WLUb8R}d zy3fPF7_e<)KaXu0$wmiz=&@GZT+_^4xY_<=>K~2+7lAn-@2KN$;3_l&8XfQmFwprc zfO%jVm<9d?$_`Bb54iS}Wox`UULA;ABU9G~{|CJF7HvoMGFSir002ovPDHLkV1ipS B1@Zs@ literal 0 HcmV?d00001 diff --git a/images/bul_c.png b/images/bul_c.png new file mode 100644 index 0000000000000000000000000000000000000000..ae391bf28eb6d74bff4183b760d4080c6d7d7da4 GIT binary patch literal 1084 zcmV-C1jGA@P)Px#24YJ`L;(K){{a7>y{D4^000SaNLh0L01FcU01FcV0GgZ_00007bV*G`2iXf1 z6$v;B-xIe000X>9L_t(I%XO4ZY*j@R$A4$$&VBFowNN0olrI5E6(b=jjkHV7v< znPigloB#iunGt|&<`?>gcC$1+i7|#Z|Nhbd3D5xg>J=Bnd#f4HbwT{=5ARoi_R#JX zhERv9^3M6Ur4tvdQFVDN8&)JqrPXYyTL)~;O*ITCaNPy@&0C!Xio;_^v?pu@c=VHZ z`J(z-{`vN%+&Df(U8tL$+c>!ErENp4jZL?G?GC@OF}z(Wvo|+Y;ewpMGbyQ9G!mkQnZ&xcHnp}EF32X}xVJiaXv=}p+yfgN zedUoC){hTP zgoH#K27oAmD1k~mIVE{4$zo0cC?BTyQ*k- zph_E|(4d}3cdC0D&|Bv^6sD|tz%DR*<=9_m0JMzc-s9C{V%@7cO$4uAOD!aXz~#?> zKh`^_h*dB^05ag(t@F1}PklFiO;sf<1??7|#hB5JC0;$57A(e0bMGO-jT2K}%}y+w z2Rxvq7HU_w2QUj~JGOskdt16LZ4(p?AQ~xsB>G4!qr^gG?##l(%7s)46!(!qT@}niec9EG?WEE-o%Eoft2j884n0FfcGOGBP!y95yyK zHlrOlI5Dl@0?CkIF@9^;O@$vEU^78ZZ^Yrxe_4W1k_V)Mp_xSku`T6Kqc;_mM5 z#Sbmid*%OsgaZ;nm`OHo_5pxlVkGwmOgA(9K&Hr^x5F{JVaEh2MMxBS`|4mcThB)Q zeYKQ_NOD!4H(H%;r?o3jb6E&^YKvwxo=T_U?Pc8`gP_G~vpbwFx6A3U+pJ*-JigH} z)&k%obRwc w6x=<11Hv2m#DR))j2B0{Dr>mdKI;Vst073O7$p8QV literal 0 HcmV?d00001 diff --git a/images/cat_ab.png b/images/cat_ab.png new file mode 100644 index 0000000000000000000000000000000000000000..ebf010df793cbbe761821145898957da4521e890 GIT binary patch literal 169 zcmeAS@N?(olHy`uVBq!ia0vp^Od!kwBL7~QRScvUi-X*q7}lMWc?slj7I;Jg1-XSm zn2}-D90{Nxdx@v7EBh@LR$d-c|BtuRfkKict`Q~9`MJ5Nc_j?aMX8A;sVNHOnI#zt z?w-B@;f;LaKt*DnE{-7_*OUL8KX9O+t?}W1$+rJIJPe9@EYp>`sfEf!W@KJE4sEp0#{$r9Iy66gHf+|;}h2Ir#G#FEq$h4Rdj z3&buW+Zta_G!(wB{5ZcTol)}#>${!DUiko3FnGH9xvXmdKI;Vst0C*B6aR2}S literal 0 HcmV?d00001 diff --git a/images/edit.png b/images/edit.png new file mode 100644 index 0000000000000000000000000000000000000000..d79aa515177ef7252c144f87900e3c77b9449d10 GIT binary patch literal 668 zcmV;N0%QG&P)T6hRb*|LUG4ipy2(4j2_h1WCNe;6X2X7X%do zV(?;M)I{Slc*r3}BN&7X2BHMqz+m(uxI|I#Af7zvXpe!w2qw53oWa4dt9#~IujopX zG2mj3(Zjm~2>bxsHxg#NWw7H( z^m+3gm;()ZGC7Eq{$uow{KT3t)-Xa+bDm2FIB5m3W9*bFeDYAMx#N6LP2}2R-;_*%uN6`gR>nQ@P7BM zo#DH;uP8rwfGU*=sZI+3TnylF-|3SRJiWPxRYHi_E$dUCuU24!A&6m0000QL70(Y)*K0-AbW|YuPggqHfAq=$)vfq_MUfw9$oRS{5@!PC{xWt~$(699K- BDF*-m literal 0 HcmV?d00001 diff --git a/images/orange.png b/images/orange.png new file mode 100644 index 0000000000000000000000000000000000000000..aadba80efebcdf9383ddce669b0cbd3a2adfaf52 GIT binary patch literal 1135 zcmV-#1d#iQP)Px#24YJ`L;(K){{a7>y{D4^000SaNLh0L01FcU01FcV0GgZ_00007bV*G`2iXf1 z6$&@8OJK0hewaZ8+(DXrr;^c1tK+v?o#%{sNY3 zFa4sx3NW!2AA$e|NCA0!&*0TX+2L*0r-V^~U!o~`wbeCzr*Z%BQyL6Y#3 zdV5-Tk2lv!7Qg~kVBP!sM_=5#dH&s=OxIQGn8ZMq8qy$Wsv*WWlf>jb9ob%#Wp=NK zew|1>ue--fa~G`d&0UQb_HVxVVI|SN%p#RUsoM-I4Ybk^l>w7b9r4B=aMNdQr6x=! z5_fnteX1)k>2z`Z1NX=iM_;RaIgrM*Ru@eZkzOL!2-pm8#etqUX)NhA_di>8VTN?{Zq(V{J85LtKV%2V?|G@SwF$}DXsqGfd0Gt-y=^xD-)mbB_ufo)*+$f@ROA+VIgauad~g%Nyl2jr}fJG5x4cvtfA4+}?D z*EcVdV1NL$fa|BP>%`~hs@DmnEHqVbx0Pn0v=mwj%LTNTgDyuPlzen@=;*JL?srxy zu8U<=Eq5#c=JYjh?;EJyKhzg{oCEI^S1NfF+EK_O(rT%EacS`6!S7}d`p9G_Aszqf zLKmoj2U<}nPh9nRB~2S-)oS5W6Js!~9B#F1?&54?@z`bk`M@^|hh5|>u+WLUb8R}d zy3fPF7_e<)KaXu0$wmiz=&@GZT+_^4xY_<=>K~2+7lAn-@2KN$;3_l&8XfQmFwprc zfO%jVm<9d?$_`Bb54iS}Wox`UULA;ABU9G~{|CJF7HvoMGFSir002ovPDHLkV1ipS B1@Zs@ literal 0 HcmV?d00001 diff --git a/images/poi.png b/images/poi.png new file mode 100644 index 0000000000000000000000000000000000000000..5ea678c579992271d5395a523a4178e202284ad4 GIT binary patch literal 1232 zcmV;>1TXuEP)P001cn1^@s6z>|W`00006VoOIv0RI60 z0RN!9r;`8x010qNS#tmY4c7nw4c7reD4Tcy000McNliru~ErK^tyb2sbQI8-BEMsz$QDy*D4nmXxuLEu%cny<48HC3u zzGQ@VOOd&gT%ZphlPH?-DuPXXyvPOqtV}Y(%P77_;ip;=B!ONGel1O6@EkCU!spOS zAPD&NZv~`*6kxg#rRijh&AiI?c9?qLwu3+bbwJ2v&q1I8*ws%y8;V-|%meLtt+b39 zxQ3$D;eC@P5oJPDr`Jh`whe=io#8TQ0X9&&ii9?Fsi}rQ1BrCv;EOa6bA&Aov|tdE zS}|-90Mt4OI-5GV_0la8LkZ$TaiW7!uD@`da4>8y3g}&7g)IP#+B><#CeUW>;wd{s z{q6lso}Q#WP=CPrwfGvRZl7XhXC)5|+XclS*utP$@IFryCJtphY(#}geazP?Cf zO=HQF+}plK_jlc-vZxP#B>?DpvWs)a&f#W{0s{0) zOpZG$N*8cA80O58GoE^IsB6gCQ_f0zRsu(?zdkz`y{*0YrMIR(8a_&EU8@};zoW`p zB+%&aLd)UC!&RLq9gQ8h;Q+M~MaIpj##d7nNkEo3s1;F(;*ld!S|YoVsz{=HQQUAa z*_24`7bq;5`+Lqa!B%pMrPvZ~I4FLU2&Sz9&e&co$Cf=4O)O1dD%0LY#!9-v$AV;^ zUmP3Uw~a5(T|||iti;oCuKsovhrMmGfk8s*bAQ89@7e38)~6U>7%xeXRapk72U&}+ z*~*k-$+uXocanA_$59AO-L<+5r#GgF?nUWo?IGk3S=K8%E1aD=%bnFbHe?1!6og80 zp1VYZH4zm*A&@BDOktt-^N^RAKJs&@e_-%T;rJC8ECR<+ zS{HP*X?an42g!o^QVcVi8(h*_X7VXuB6QkPz>doduS6hF2p%E1dGG*;^A+&d0iu`& zFkQftz{4Vf+{(`|>^W&)8h$3j36cm-gASU|g|M%IQu_Cvo*5}kCUl5|Mg1l-RtXdx zWf8srRtkL0TD!_O*?vLO3acLNb?`X`k>VSbEjLJG@?lY6Oaxi|m>M*v%Y~+}Ta%>u zs5otsgPL!E`7&u%1zs1+HO5%4Ok!XHZv)vXFR7cr6}J~Y*X1)j;3RE8w`U5I^Ng_I z_DYs9`vCaIBgH(UOq6;l%Y2_~WAdH{f~*d&s-6U3l&^swDv*3q{HahKNlxA>xULn; zI=hTi0`OI&m?6dq6oP)2kDfus_`MQ@^psY~6%==ir!JS6^k(iw3`{Z%0RLKJHOV_( ut6O^RgWQ-N(A6}+I1l;X#Gt2Ilm7s&%WYc4JE%_p0000P001cn1^@s6z>|W`000xKdQ@0+Qek%> zaB^>EX>4U6ba`-PAZ2)IW&i+q+O3*tk|Vitg#Y6dIs&%9avZE?x`B?b&p}pq&uE@Z zl4)wHSw$u@kr5ugfaT7A{QGtP#(%lS?6TEXdMTd&a?d>uerW#l-{&#-{Qmv?(!Z~T zzdrBc&kK>K5|8QgYdPQJ2hZE@H`M%ne*X1&SJ(GE?R%o{3qL1Jx^m=)?{V#Wq7*;R zhwtz1XF((XaXa7d`*Vx+^YHiIbEO#DIA4k`o)nVj-=?c1eTOu7O#B;D=b(@ZKU>QD zn^OPh+PS{|xp=>R`djV%_&%ThS_sYVg!`?jz52c{=l|N9 zpT;ISXs^tT8ytRqPBAU~##VZLP8?@GJO3GtQ1|x)l!$8^iwTJi_!5$Z5`2xZhCm$~ z{TZx0rW_|72xf5ycj-+^h32%VYxABO-m%f*=V_>gMi_%qX-!XqX60<)r{)HRh9%3+ z%!*ZO)@_tjau9OvX05c^TI+4}*b@|Hz4YpO>%EV`MF*!I zynArO7&Fc^^R!uJoo)6x7Vuel*($59w)z@7?z9>IU3PW5?Y_qe2PmC>}f7>@_`G3{7_^&$W)VlwV z&N;R2+qwVRw}0r`9#2uyYmlZ;HTCI2Y#eafh;2xdSXPA&9Z;vqJ?(S+siWUK_8zUq!!b}M(%#nS`^>%bT%9{?QT<3|ChMooavitt z`;3tvl(tTpE3(BOp4K|?oiY= z<3+i~ooml_Qw6pft9a-rs<_kOzFSFoHn1^c-C#WUHCfN9{ra}IYb)L%=#)7$c5O56 zA#)dP(Ias5e=Xk3))-; z9q6nxu|T@E0u!$F9%!m9YLZZz$+%4`454qGK}l^g?6y-{2Z)4Guw7<%RakIKX=kR1 z9eW`!zzKXf$O70JwFanZR@`FkXP3~eG54jiKxzzE(e@1?MSdA{4X@FtUEe>&1;mZ} zJ_pJHSk2!t*X$?FR0s7nd*vO%C=i|3+X&1WU`P})t}C3DGETi2(zplG$|cTxxgju` z(oJQr!^QgPw^^;6;WR;cJKlBWX)D8B4*cQ4(kgY+@1416<}2SuW#JVl1*C)TK7stD zyhb+wO5oN8%`s3{nCKch}$rS^KRyz zS8Kgxzxtyj5Nk=Y^{(2wb2H(_$<}S~D3zU3u_(o>Q_bLLz!zU%p>dN5@b9t2|(0P=j(RO2eRpex5ZST znK7e~IzdIU;g9LNQPPNEY?nzJDta3zLb*=@7DWzjb6EGH(;c`7!a6sQU}D;=tL!;r z!ay)@j~vPM*lIS%7!}cu9bMtec zQA{h7e;X4Ykn`pU5)c7~BjYu>YhGz@RK~=HE?6gw{R$qrpdP8=K`2ig6Pv&UnFx#o zxr|HJkP@RqBd?G_T`%I$7(-THsV4zo4EW>9ALv?@*1`!W4pfZwlkj0-UZduO_s|UR z!BRvMx=1%VlrymyhXhev#Xt|Jtc)z+V6N(Lv($AAPQa>o!wj8S4?`ZR`JL;59EE}( z0-&x)KSauMhHMO;h$a_vuvcPyQEz&K#99hJ(hbLfcey1LZ!qi3Uw})&y_5u#QRim7 z*GL7Wg)s4JSpOFG8BDEF7JO*hrFWC8C6w_*@f_$bYSP`pEKarNV- z0n%wb=Aoc(0ipm}xFqQv<9lEX01{{%8?d1)*7^9o!%B zC`Now&3Ru<4`t?t3x08JpK%>sEQYPJ0mP0A`N1C2*A2qbQyvCeCg&D_A2t}pODNAR zuynw5U_*J|WGjL}+xjFopKAaNK)W#ADf5%ldH{%i+5!%#P`%&!JvJHL1(KgDX*+0Wu>7*kU@@ zdOl#Xs=Sn`%ogxM?>7fMVitJ@(-8|~2P5Uc#X66j#B(g5t>AB}p<~cJSUW%tQ1sNg5qMr4l2XMa9iO}7t$#$3k#P33=D>*#4N8uqD}6UaB~?E3Uo;) z*Wx95!Ohh9D4FA-bEJN7n=OgaZZr|G?lh%8{hz~M3>iN=zU8yp=ds8mwSx)6B;(+i z=b?~6!t|W5u%HldK1{!5V1IPQNj)R*=7ENd;fWK-yd>%2?*AqMbFF9SsWJTj`||FAOQ?8V}`mf zpgc01CA)IG+^uU^+s5@yVo5Rd$!f)#3@S@P5Ro+oQRp3tU_*?JDPdS>t@c3x9(3p; zE)26usz(Np0F9&=fpmgwAVvAxleT7oU zNgYcC4f6zuawa>%EaM`2werbX4E*{@T$qV~2dZ*c#yOYaq84tiCNIUEoDWh0kgzrd z<8HAWTRs_WZD_<)ZpPY7F&9n}=3_3neeBRiQ6>Qvf1_+J>&KnF-JIx$RGK<9^>V1j{Z^->Nx*{@w`A%)U7 zK{G@(cz{%8+O&5IUgaKz)%%o;HccpKNGf; z!PEfEb>Ull9XMGG9fJ-N$wCvIAc$!a`2$!o*E^_4ZWs!ZAzDOKAh&+1kqV2U;m8IN zh0t(4b57XAeeyeJjR((zA4OM=xlMo~!=;U3swDzW7>m`c%|*FExDJFGS{ z8F;Ik{%|=Q3Xrc3=mJlK21jHBBDfqXT}SQ_M~vO^FnwMInHkMS7PRw-g9Y`Sr=dyh z>o|;KM#326KmlkA^qpq#FZU~?L0vF!(ykV(N}0S(J_$G{Zb}TH{mOJ)6hg_MA8-20 znsOg0qWze*RSLh}cq9)rppyUbc1_8cRF4i+rqeNyi!gdLF~n4$!hmC%DzOK|+cUvu ze`asNMAX?TA_!~XBoFcNDiFGt^dp`V1kWk=bTuX&Qy)!o4;)dLwKepKGx{MnhQ>0t zIQ^@2bu%IaaOOpL91p1tczhN0hcp@j2f1RFan4<7FI7{yKgy^t9&jkEr8zIwMRb5P zmr0;en^Cmaq)i6aljN-j67$Fruhg_06gSdXU!KG<#BR;Ku{fj`29Y$22|}v~q|oEJ zN!y`8z6z(s#NZwp{tYh8(Gy^|&XH*tHjK{feTMjtE z#k5U33l!?e-;<(nr`RpW`YC?IpD&O#dr$Zb44g71&c#n08oDv@HQE{^dT;zOq`S60 z-5@Aq;ytzoK`tKct|fSSzd{gM@TvkbWXm9pC3W2ujF58(trQ0R#Hy<55mxi7lSNL5 zvnAatg(hGnfNLS;-Y<$Xa35TpL4F$@!Y?|&_hkZAx=c2s!*$u_LRVvN8-nLP2WN4_6-=LoO$e(2Rd1MAeK{dcl{m92%MD%JS zwj6`m)+bru?6Y^j4DBVuD$ixlp>IFr+|QnDi(1Pe1<3&_G$BfjbtBdcQ911dA_Pes zbo-?6s7TFRVr&3Q8t?)0f;+%8ylX=V%f~;q<9|Q8Kb}y$=m>#4Eg7G5s@+NgsML)_ zc|2APcV;i?zzX4~aAKM;cY<}16l4Wj!1jWX_;wO} z!-Ig++>fNg?PAZTLa%YdFBo!-rp@`y0}Rx!I^r&Hf7?h09NFoQh#oUX`*nm8HZDn< z#p~!?6chlA@je&@@r-ImmBbmySH-N+Vpxi}3g^S-XSjUh_ABCW@kUps4ay?f7NR)W zvdhXBNGz7mZrGbV?JpvkeWJ4+<`yFs=`p^-Cg6R`fjf_eJt|mbYN7sKIezckRiVM#Qk+K={D5 z5lCd~fElS;?++yg|{s2#zlP^VEbZ`kz@UV#>d*pOPq#jCWjS&Fj|~&AMyjX zc}wuTHrB~!C%`J&m?7N}HgoIH`=2HovLjQ|eOXP10iarx=jbmOU{J3QIuPVy??Y-z zh8GGpaf7Y<|A;SGK_-KsNpMvE6R49Pd-G2gFv)|Rk_N0pd000JJOGiWi z{{a60|De66lK=n!32;bRa{vGh*8l(w*8xH(n|J^K00(qQO+^Rg104=FD?qT2JOBU$ z?MXyIR9M5cmtBZdRUE*7=iIq73$K~X;746)K+@Ky?=jwbwSAQ-Oq1wky9dsXi}6S z13Vl{#4px`40iYS0KbS}1@)FvIF?OVSy)OTB4osnHhGLl6)5YPuJxDuM}1xCw%@OL z+5t`m0ZPGQD?(c+jH+k|2rGiO@3bA- zLy;;#P})NgI&B9_&DpbZu)FuboNx_x_bvlQM6i7iI&FuDwyKPVqakvcLX|%28m{Hi z!La3lWwAtjI#hH=?FyD@s}-IDXkY0NdUG>ucmx(Lf~lKu`V8rVKaol20f=ae_E3am zA?t_DR*Lt4*EN6<*%r*hl2D`sSlZJ`=;U5_a#Iliu%rXFzKnBn4{a-)(%!|PaAnxG z0;r$wdUm;^?Q2Fv`7!n(t;<^o_kRq|9rf$>$`HvHKBAC!0Z10IOnKfY1zFdu)~#0f zDIWmfSmpKzzqNUO0KnQV+F!Yk^7-;`5P^uc9@7PJ9|~5Kt%?N~b!>S2iN?C{#b@U9 zR7J?{*T7x=f40)4a&^{qf@L)ZS+f>rNyIBxML1A28nsLVZNXcVWcAXfMoM&5YuTH* zeHGOr6l$Kbn;c!XuBmpB8`?#ZWko>7XPRP|iP>_(0~6nfjA~1%p|YZkF&D=gCnTqt z8BPTa2Z86cM*ZlgBImlLTTG9p;LkrAgPi^j*E1{#SuuW}NJ!~HJ?j>J3%+QxCXk|o zCqQI=!0j~o!{-T}4;&eZCE_XV&HWKyJ7aRC9g`Qv;OMdX0M~`x`!x#PqRTeLmc2^|g$Pce0AqLc?;uIJKZT!WY6zha5_-NENq4R-fl0`3th>KrS? zopwt@c^8Dqe1??C2LdU(dWQNA1j8jpL{?c)ivmU1x1LS;*~K$iRY-SR($95&5w8VYTzo ztEX=LXNcE3dqp@~p+5dV;+y_P&Nl>!CE{*Q%ywYHe^yf_|3;(btsC9wJ=5F0YTCZ> l%C}SeUozO;yS~{o{{qWxC9q8l%Ps%_002ovPDHLkV1i$LKAQjl literal 0 HcmV?d00001 diff --git a/images/red.png b/images/red.png new file mode 100644 index 0000000000000000000000000000000000000000..541bb87dfef7f6281d38e64381f0f3a9e7ec06ac GIT binary patch literal 1014 zcmVPx#24YJ`L;(K){{a7>y{D4^000SaNLh0L01FcU01FcV0GgZ_00007bV*G`2iXf1 z6#^$@BgcsV00VYOL_t(I%YBqhh?Z3p$A9PC=b87NPiM+zm^9NCen8S-5k<==h?WVp zjv$a$LYp9nTC|EFlNLg>i4=)~3O5r(3lqe0T8xT_B4Rp|IXcbz@!WgQY4JYq+bB(c zxLmk!e)oSq?iGO6(m6(^_po+z5mlu)@Tvj@&;Um2ova}S%PpXtHN<@AO9vR8-ZNkb zwcMSxH=dVrWjO$4sM-w+MbRA|Z#Gjk1#AIk00*vR4HtvuWuRLgU+})M0r1G751IS) z9r@xfDj`|#>|%JQx9#&>c4Pff?NA)dLEydl{tXB>*$O*5yvjepnGtiwW84sy-32$^zLvil zlq1KiKn7aC)%jIUe}9LofV2#cIg_f2n0oM-GO4OO=8TK*-R*qr+dC`(5pb!xzP^e z^zo@6%_yqkt_h)?EiRd^%-^-2_h0tUl5-ZgUB~|W+tlj6=V@RMuy=Qp$DbXQEn7=Q zOYwL!$h3CxHvVpFpS1j{AXq=nybogJ*#U>a#9z3%Cg^1FJw)!+ay)`mbzg k-5QKn4T<>&kwUn}^px?O=iDW(Ec()c~ImSD=)jpddFlHzy|tkWCWUv}x0| zYuBz{zn+(uH(^!C>BrmK=KHnH@#$IUzjNo#J9q9ZUcA^UPuV(8**0I*yI9qyL@95B zRpoATpvKFWFEiLOFxxY50+AB~zY~L?KZB4TgIFMgWB`LqCWA~agM1)^LJ)&OJ%eH} zgGva4Y6pWx1cOE-gEkPPFlzTQ=%tDp_1yMe5rEyX7-pp9PumF zl2&P_pAgO4CzE$UyyUo6{UytWhK8o5rnZ~5?LctTz7q(JdH47CPrev4b?VePbLK2u zxNybO!j;d8SFc{ZZr!?#Pg;Q<+`4t^wr$&h;jm}Vp1phb?t3+L->Yc{4jedi=+NQA zhmRaNa^n5s6Q35J0)ls|&YwSj@#4kHXRlwma^>3NGuMCYym9&VjT<*^emQUp2!8Fq z{qp?nHy3XII&k;y-TOBl-@khw2p;}8`}ppQr%#_g1A<={pFe;8;^DhjuU@@*^XA>V zcOTw=`~U?1AAS7z@e>gIfA;zF=PzHreEs_M+q*yCzW(_3?c4Y7-+%o0@$=`;U!VW| z`t|Gg@85s^{Q3L)@87@w|NZ;-|L_0*|Na9(djCZmV9L-e3GxeO;1N;s^A8A)j){#+ zNK8)2$jUD0?ChF6Yu4&D>mEIR4%7z$SL3#R1!`hU@^*LGwu+tmBaqGI>Eak7A<261 ztQS+D1lxn}bAI|9cNCFU5qHgWoPC7nmixMBm)+Cu`j}r7-7;}ntb>c{w|16Y8(LQe zEH<8e`Qqp6pAFnt&1ZKoFzhf|Fm?6wGwan?&1NXDV?AG5{_5|qZwqW^_H1N1p(|*4 zujuP-5q>TtOxZ>R4|wjM1RrYGl?Elpsa`5!V0YB z{4>+h=uvD8S~evYMAQAbChZ@0-raVezTL(m4BPa<`@!dq@9T~M*jKy0RNZ_}@r}8A zPe9SjU)H>By5{SGvcO-h8_;Gf39P<^{>cS(x;a%BQ1UihE7F(oWg3XMP<(q4>Ks2< zGoa2|(g;65V6YVd`x)qgTTt9hTQvdNyy>pyKM#Z*SUPva2*G}6%dJp6rlJ1=vd5Oy z!vh5L;{AoAMhJD#U$jGhZOQEl$Zl>_^`8blcLI?v1TP&&=*mgjZNSP|DE?EBUUB#T z4LBKJcyU0|Kkz_^0UsH@-Z+Nn^}~eGUS-f%8j*W$mJnq84j@0tIpswwutqaVeWSip z8`Nf{7=!%Iiu_Y<{BK}_HD{h2QuQcbK1fnq4Dn&B5%RRNHAy#BAoGY# zNlQZcSfqYZ1qN~02GL;yWZF3cFmTByAoGwts{*N;Of0Rn0C9n0K#EFN4$>7gLB)P2 zptODh;ysi22e{KP_G84nz1hd)T8i7vQt~!?Tm|;iFY+F?_-zq&hvD;a4F|RoOq+Om6e1btqc|^ z1pSo?Y7#+3f_;epMD$0rq7p0uqkl}upbtX`A}X3irqOMUNE%LXb5pzK2kvfdcfAX* z266Sk!|#26?{m-lz7OvKRaJHAbUJHzo@dkJxrvMGqwk#@fXLANnJjkN^R+R{vJB5= zIS3s2PSf<|C&$1##Zxn*zT;wwH#?LJphJr@w(V&x4%U#shz_mY9iK z2^2TThnibXy4~(GP0MEu=BS;4(r`CbsTk!28>_B`???4 zH&*6{JRX50$$F#Fc%-aTy#ZyjEC3(iMh|}klxTLAydMQ42=eU?Gp5kA@$^S~-!7g% z+>{z`5Cnn7alGH?OvcrVC+?BU#4JWj8QvB&s}0QJ0Xb5#XW`*NE}IGT&(MSI_h zV0Gx(P|)l37MMw5be1BW2e%^3K%3!|p@3{RkTSIrk2f;ARRN3+RQQzF)Eh=3HCe=QmVhxlEeG zR4Itz3N^1F2_r_V$RNIuObmunc+yro)7AILn*Jygh}M9?fawcdQx0ZwAk5PE@TW8SGM+%lpkWr4~ binRVu(0{U`c1tat00000NkvXXu0mjf;LdH@ literal 0 HcmV?d00001 diff --git a/lang/en/lang.php b/lang/en/lang.php new file mode 100644 index 0000000..02ac620 --- /dev/null +++ b/lang/en/lang.php @@ -0,0 +1,121 @@ + + * + * English language file + */ + +// javascript +$lang['js'] = + array ('pleaseWait' => 'Connection to the server in progress ...'); + +// commands +$lang['Valid'] = 'Valid'; +$lang['add'] = 'Add'; +$lang['clear'] = 'Clear cache'; +$lang['clearAll'] = 'Clear all cache'; +$lang['clearCache'] = 'Clear cache'; // XXX +$lang['created'] = 'Created'; +$lang['modify'] = 'Modify'; +$lang['modifyAll'] = 'Modify All'; +$lang['prop'] = 'Suggest'; +$lang['remove'] = 'Remove'; +$lang['selected'] = 'Selected'; +$lang['show'] = 'Show'; +$lang['test'] = 'Test'; + +$lang['repeatType'] = array ('day' => 'days', 'week' => 'weeks', 'dayMonth' => 'months', 'dateMonth' => 'months', 'year' => 'years'); + +// placeholder +$lang['titlePH'] = 'event title'; +$lang['leadPH'] = 'Lead paragraph / organisation team / context / ... (optional)'; +$lang['posterPH'] = 'http://... (optional)'; +$lang['paperPH'] = 'http://... (optional)'; +$lang['remarkPH'] = 'explanation / comments / remarks / ... (optional)'; +$lang['ratePH'] = 'x € / y € (optional)'; +$lang['cityPH'] = 'city (or INSEE code) +!!enter!!'; +$lang['addrPH'] = '1, rue de la Tannerie ~br~ 56 000 +!!enter!!'; +$lang['datePH'] = 'mm/dd/aa'; +$lang['hourPH'] = '--h-- (ex: 09h30-15h)'; +$lang['rootMoveDatePH'] = 'mm/dd/aa (move until this date)'; + +// messages +$lang['days'] = array ('sunday' => 'S', 'monday' => 'M', 'tuesday' => 'T', 'wednesday' => 'W', 'friday' => 'F', 'thursday' => 'T', 'saturday' => 'S'); +$lang['enterTip'] = 'Press \'enter\' to update this field!'; + +$lang['who'] = 'Who'; +$lang['audience'] = 'Audience'; +$lang['shared'] = array ('', 'Shared'); +$lang['proposedBy'] = 'Proposed by'; +$lang['what'] = 'What'; +$lang['title'] = 'Title'; +$lang['lead'] = 'Lead paragraph'; +$lang['posterURL'] = 'Poster'; +$lang['paperURL'] = 'Paper'; +$lang['remark'] = 'Topic'; +$lang['rate'] = 'Rate'; +$lang['where'] = 'Where'; +$lang['city'] = 'Commune'; +$lang['addresse'] = 'Addresse'; +$lang['when'] = 'When'; +$lang['allDates'] = 'All dates'; +$lang['inverted'] = 'Inverted'; +$lang['noEvent'] = 'No event'; +$lang['validation'] = 'Validation'; +$lang['proposition'] = 'Proposition'; +$lang['reactiveMap'] = 'Interactive map!'; +$lang['toComplet'] = 'To complete'; + +$lang['audienceChoice'] = 'For whom ?'; +$lang['memberChoice'] = 'Proposed by ?'; +$lang['eventTypeChoice'] = 'What kind of event ?'; + +$lang['from'] = 'from'; +$lang['to'] = 'to'; +$lang['fromHour'] = 'from'; +$lang['toHour'] = 'to'; +$lang['at'] = 'at'; +$lang['all'] = 'all'; +$lang['each'] = 'each'; + +$lang['startError'] = "Validation couldn't be performed because:"; +$lang['noAudienceError'] = 'audience miss'; +$lang['noMemberError'] = 'member miss'; +$lang['noTypeError'] = 'event type miss'; +$lang['noWhereError'] = 'place miss'; +$lang['noTitleError'] = 'title miss'; +$lang['mktimeError'] = 'unsupported date (OVH server limitation is : 13 decembre 1901 < YYYY-mm-dd < 20 janvier 2038)'; +$lang['pastError'] = 'date out of bounds'; +$lang['pastToError'] = 'end date before start :-D'; +$lang['badDateError'] = 'inappropriate date'; +$lang['notMemberError'] = 'not member'; + +$lang['addEvent'] = 'Add an event'; +$lang['proposedEvent'] = 'Suggest an event'; + +$lang['propSuccess'] = "Votre proposition est enregistrée. Nous allons vérifier qu'elle est conforme à l'esprit du FSL avant de la mettre en ligne. Merci de votre contributon."; + +$lang['notifySubject'] = '[Schedule] New proposal !'; +$lang['notifyContent'] = "A new proposal is done.\n\n"; + +// toolTip +$lang['tipPrevM'] = 'Previous mounth'; +$lang['tipNextM'] = 'Next mounth'; + +// format +$lang['dateFormat'] = 'mdY'; +// $lang['orderedFormat'] = function ($num) { // XXX pb OVH +// if ($num < 1) +// return $num; +// switch ($num) { +// case 1: +// return '1st'; +// default: +// return "{$num}th"; +// } +// }; + +$lang['orderedFormat'] = array (1 => '1st', 2 => '2th', 3 => '3th', 4 => '4th', 5 => '5th', 6 => '6th', 7 => '7th', 8 => '8th', 9 => '9th', 10 => '10th', 11 => '11th', 12 => '12th', 13 => '13th', 14 => '14th', 15 => '15th', 16 => '16th', 17 => '17th', 18 => '18th', 19 => '19th', 20 => '20th', 21 => '21th', 22 => '22th', 23 => '23th', 24 => '24th', 25 => '25th', 26 => '26th', 27 => '27th', 28 => '28th', 29 => '29th', 30 => '30th', 31 => '31th'); + +?> diff --git a/lang/en/settings.php b/lang/en/settings.php new file mode 100644 index 0000000..53695af --- /dev/null +++ b/lang/en/settings.php @@ -0,0 +1,29 @@ + + * + * English language file + */ + +// for the configuration manager +$lang['scheduleGroup'] = 'in this group one can add event'; +$lang['adminGroup'] = 'admin group with all rights on events'; +$lang['dataDir'] = 'directory contains XML file per group'; +$lang['groupsDir'] = 'directory where group names could be find'; +$lang['noSchedule'] = 'forbiden group names'; +$lang['scheduleDir'] = 'wiki page for schedule (contains one dir per group)'; +$lang['sharedDir'] = 'directory for shared events'; +$lang['scheduleWhat'] = 'type of events per categrory (c1:w1,w2|c2:w3,w4|c3:w5,w6,w7)'; +$lang['scheduleAudience'] = 'type of public'; +$lang['iconName'] = 'logo name of the group which define an event (a1,a2,a3)'; +$lang['iconWidth'] = 'logo width of the group which define an event'; +$lang['repeatPosition'] = 'Repeat event position (grouped on top or isolated)'; +$lang['bubblePosition'] = 'tooltips position of calendar'; + +$lang['useMap'] = 'Display map'; +$lang['defaultCenter'] = "Empty map default localization (lat, lon)"; +$lang['defaultZoom'] = "Default zoom"; +$lang['migrate'] = 'Performe data migration'; +$lang['showOldValues'] = 'Highlight non migrate data'; +?> diff --git a/lang/fr/lang.php b/lang/fr/lang.php new file mode 100644 index 0000000..1d6689d --- /dev/null +++ b/lang/fr/lang.php @@ -0,0 +1,121 @@ + + * + * French language file + */ + +// javascript +$lang['js'] = + array ('pleaseWait' => 'Connexion avec le serveur en cours ...'); + +// commands +$lang['add'] = 'Ajouter'; +$lang['clear'] = 'Effacer le cache'; +$lang['clearAll'] = 'Effacer tout les caches'; +$lang['clearCache'] = 'Effacer le cache'; // XXX +$lang['created'] = 'Créer'; +$lang['modify'] = 'Modifier'; +$lang['modifyAll'] = 'Modifier Tout'; +$lang['prop'] = 'Proposer'; +$lang['remove'] = 'Supprimer'; +$lang['selected'] = 'Selectionner'; +$lang['show'] = 'Montrer'; +$lang['test'] = 'Tester'; +$lang['valid'] = 'Valider'; + +$lang['repeatType'] = array ('day' => 'jour', 'week' => 'semaine', 'dayMonth' => 'mois', 'dateMonth' => 'mois', 'year' => 'ans'); + +// placeholder +$lang['titlePH'] = "titre de l'évènement"; +$lang['leadPH'] = 'Chapeau éditorial / organisateur / contexte / ... (facultatif)'; +$lang['posterPH'] = 'http://... (facultatif)'; +$lang['paperPH'] = 'http://... (facultatif)'; +$lang['remarkPH'] = 'texte explicatif / commentaires / remarques / ... (facultatif)'; +$lang['ratePH'] = 'x € / y € (facultatif)'; +$lang['cityPH'] = 'commune (ou code INSEE) +!!entrée!!'; +$lang['addrPH'] = '1, rue de la Tannerie ~br~ 56 000 +!!entrée!!'; +$lang['datePH'] = 'jj/mm/aa'; +$lang['hourPH'] = '--h-- (ex: 09h30-15h)'; +$lang['rootMoveDatePH'] = "jj/mm/aa (déplace jusqu'à cette date)"; + +// messages +$lang['days'] = array ('dimanche' => 'D', 'lundi' => 'L', 'mardi' => 'M', 'mercredi' => 'M', 'jeudi' => 'J', 'vendredi' => 'V', 'samedi' => 'S'); +$lang['enterTip'] = 'Appuyez sur \'entrée\' pour mettre à jour ce champ !'; + +$lang['who'] = 'Qui'; +$lang['audience'] = 'Auditoire'; +$lang['shared'] = array ('', 'Partagé'); +$lang['proposedBy'] = 'Proposé par'; +$lang['what'] = 'Quoi'; +$lang['title'] = 'Titre '; +$lang['lead'] = 'Chapeau éditorial '; +$lang['posterURL'] = 'Affiche '; +$lang['paperURL'] = 'Article '; +$lang['remark'] = 'Sujet '; +$lang['rate'] = 'Tarif '; +$lang['where'] = 'Où '; +$lang['city'] = 'Commune '; +$lang['addresse'] = 'Adresse '; +$lang['when'] = 'Quand '; +$lang['allDates'] = 'Toutes dates'; +$lang['inverted'] = 'Inverser'; +$lang['noEvent'] = "Pas d'évènement"; +$lang['validation'] = 'Validation'; +$lang['proposition'] = 'Proposition'; +$lang['reactiveMap'] = 'Carte interactive !'; +$lang['toComplet'] = 'À compléter'; + +$lang['audienceChoice'] = 'Quelle audience ?'; +$lang['memberChoice'] = 'Proposé par ?'; +$lang['eventTypeChoice'] = "Quelle nature d'évènement ?"; + +$lang['from'] = 'du'; +$lang['to'] = 'au'; +$lang['fromHour'] = 'de'; +$lang['toHour'] = 'à'; +$lang['at'] = 'à'; +$lang['all'] = 'chaque'; +$lang['each'] = 'chaque'; + +$lang['startError'] = 'Certaines informations ne permettent pas la validation :'; +$lang['noAudienceError'] = "manque d'auditoire"; +$lang['noMemberError'] = 'manque de membre'; +$lang['noTypeError'] = "manque de nature d'évènement"; +$lang['noWhereError'] = 'manque de lieu'; +$lang['noTitleError'] = 'manque de titre'; +$lang['mktimeError'] = 'date non gérée (pour OVH : 13 decembre 1901 < YYYY-mm-dd < 20 janvier 2038)'; +$lang['pastError'] = 'début en dehors des limites indiquées'; +$lang['pastToError'] = 'date de fin avant le début :-D'; +$lang['badDateError'] = 'date incorrecte'; +$lang['notMemberError'] = 'non membre'; + +$lang['addEvent'] = 'Ajouter un évènement'; +$lang['proposedEvent'] = 'Proposer un évènement'; + +$lang['propSuccess'] = "Votre proposition est enregistrée. Nous allons vérifier qu'elle est conforme à l'esprit du FSL avant de la mettre en ligne. Merci de votre contributon."; + +$lang['notifySubject'] = '[Agenda] Nouvelle proposition !'; +$lang['notifyContent'] = "Une nouvelle proposition a été faite.\n\n"; + +// toolTip +$lang['tipPrevM'] = 'Mois précédent'; +$lang['tipNextM'] = 'Mois suivant'; + +// format +$lang['dateFormat'] = 'dmY'; +// $lang['orderedFormat'] = function ($num) { // XXX pb OVH +// if ($num < 1) +// return $num; +// switch ($num) { +// case 1: +// return '1er'; +// default: +// return "{$num}e"; +// } +// }; + +$lang['orderedFormat'] = array (1 => '1er', 2 => '2e', 3 => '3e', 4 => '4e', 5 => '5e', 6 => '6e', 7 => '7e', 8 => '8e', 9 => '9e', 10 => '10e', 11 => '11e', 12 => '12e', 13 => '13e', 14 => '14e', 15 => '15e', 16 => '16e', 17 => '17e', 18 => '18e', 19 => '19e', 20 => '20e', 21 => '21e', 22 => '22e', 23 => '23e', 24 => '24e', 25 => '25e', 26 => '26e', 27 => '27e', 28 => '28e', 29 => '29e', 30 => '30e', 31 => '31e'); + +?> diff --git a/lang/fr/settings.php b/lang/fr/settings.php new file mode 100644 index 0000000..9f3180a --- /dev/null +++ b/lang/fr/settings.php @@ -0,0 +1,33 @@ + + * + * French language file + */ + +// for the configuration manager +$lang['scheduleGroup'] = 'en appartenant à ce groupe, on a accès au formulaire pour ajouter des évènements'; +$lang['adminGroup'] = 'le groupe qui à tous les droits sur les évènements'; +$lang['dataDir'] = 'répertoire où sont placés les fichiers XML des évènements par entité'; +$lang['groupsDir'] = 'répertoire où se trouve les entités dans le wiki'; +$lang['noSchedule'] = 'liste des entité (séparé par ",") ne devant pas créer des évènements'; +$lang['scheduleDir'] = 'page wiki de gestion de l\'agenda (contient un répertoire par entité)'; +$lang['sharedDir'] = 'répertoire regroupant les évènements partagés par tous les groupes'; +$lang['scheduleWhat'] = 'liste des types d\'évènement par catégorie (c1:t1,t2|c2:t3,t4|c3:t5,t6,t7)'; +$lang['scheduleAudience'] = 'les différents auditoires attendus pour les évènement (a1,a2,a3)'; +$lang['iconName'] = 'nom du logo du groupe qui à créé l\'évènement'; +$lang['iconWidth'] = 'largeur logo du groupe qui à créé l\'évènement'; +$lang['repeatPosition'] = 'Position des évènements répétés (groupé en haut ou séparé)'; +$lang['repeatPosition_o_grouped'] = 'groupé'; +$lang['repeatPosition_o_isolated'] = 'séparé'; +$lang['bubblePosition'] = 'Position des info-bulles du calendrier'; +$lang['bubblePosition_o_left'] = 'gauche'; +$lang['bubblePosition_o_right'] = 'droite'; + +$lang['useMap'] = 'Affiche une carte de localisation'; +$lang['defaultCenter'] = "Position par défaut d'une carte vide (lat, lon)"; +$lang['defaultZoom'] = "Zoom par défaut"; +$lang['migrate'] = 'Effectue une migration des données'; +$lang['showOldValues'] = 'Souligne les données non migrées'; +?> diff --git a/lib/scheduleCitiesMap.js b/lib/scheduleCitiesMap.js new file mode 100644 index 0000000..d0782b0 --- /dev/null +++ b/lib/scheduleCitiesMap.js @@ -0,0 +1,600 @@ +/* + All scheduleMap behaviors managed by schedule plugin + + scheduleMapList: Map of + mapId => scheduleMap + + scheduleMap + mapId: div map ID + map: map + poi: Point Of Interest + poiSize: # of number + clusterLayer: clusterLayer +*/ +var scheduleMapList = {}; + +var scheduleUseMap = JSINFO['schedule']['useMap']; +var scheduleZoom = JSINFO['schedule']['defaultZoom']; +var scheduleCenter = JSINFO['schedule']['defaultCenter']; +var schedulePoiUri = DOKU_BASE+'lib/plugins/schedule/images/poi.png'; +var scheduleSelectedPoiUri = DOKU_BASE+'lib/plugins/schedule/images/poiRed.png'; +var scheduleEmptyUri = DOKU_BASE+'lib/plugins/schedule/images/empty.png'; +var scheduleIconCalendarUri = DOKU_BASE+'lib/plugins/schedule/images/calendar.png'; +var scheduleAjaxPoiUri = DOKU_BASE+"lib/plugins/schedule/ajaxPOI.php"; +var schedulePrecision = 5; + +var locationFormatRE = new RegExp ("^\\s*\\(\\s*([\\-0-9.]+)\\s*\\|\\s*([\\-0-9.]+)\\s*\\)\\s*(.*)$"); + +var iconStyle; +var iconSelectedStyle; + +/* selected LI on city UL */ +var scheduleSelectedCity = null; + +// ======================================== +/* format location */ +function scheduleGetLocation (lat, lon) { + return "("+ + parseFloat (parseFloat (lat).toFixed (schedulePrecision))+"|"+ + parseFloat (parseFloat (lon).toFixed (schedulePrecision))+")"; +} + +// ======================================== +/* convert and reverse location */ +function scheduleGetCoordFromLocation (latLonArray) { + return ol.proj.transform ([latLonArray [1], latLonArray [0]], 'EPSG:4326', 'EPSG:3857'); +} + +// ======================================== +/* convert and reverse location */ +function scheduleGetLocationFromCoord (coord) { + var lonLat = ol.proj.transform (coord, 'EPSG:3857', 'EPSG:4326'); + return [parseFloat (lonLat[1]).toFixed (schedulePrecision), parseFloat (lonLat [0]).toFixed (schedulePrecision)]; +} + +// ======================================== +/* center scheduleMap according POI markers */ +function scheduleCenterMap (scheduleMap) { + if (!scheduleUseMap) + return; + if (scheduleMap.poiSize) { + scheduleMap.map.getView ().fit (scheduleMap.poi.getExtent (), scheduleMap.map.getSize ()); + scheduleMap.map.getView ().setZoom (Math.min (scheduleMap.map.getView ().getZoom ()*.9, + scheduleZoom)); + return; + } + scheduleMap.map.getView ().setCenter (scheduleGetCoordFromLocation (scheduleCenter)); + scheduleMap.map.getView ().setZoom (6); //scheduleZoom); +} + +// ======================================== +/* remove all POI markers */ +function scheduleClearMarkers (scheduleMap) { + if (!scheduleUseMap) + return; + scheduleMap.poiSize = 0; + if (scheduleMap.poi !== undefined) + scheduleMap.poi.clear (); +} + +// ======================================== +/* add a wellknown city marker */ +function scheduleAddCityMarker (scheduleMap, cityCode) { + if (! cityCode) + return; + var inseeLocation = inseeCityNameLatLon[cityCode]; + if (!style) { + // XXX not found + return; + } + scheduleAddLatLonCityMarker (scheduleMap, cityCode, + inseeCityNameLatLon[cityCode][1], + inseeCityNameLatLon[cityCode][2]); +} + +// ======================================== +/* add a positioned city marker */ +function scheduleAddLatLonCityMarker (scheduleMap, cityCode, lat, lon) { + if (!scheduleUseMap) + return; + scheduleMap.poiSize++; + if (scheduleMap.poi === undefined) + return; + scheduleMap.poi.addFeature (new ol.Feature({ + geometry: new ol.geom.Point (scheduleGetCoordFromLocation ([lat, lon])), + cityCode: cityCode, + location: scheduleGetLocation (lat, lon), + lat: lat, + lon: lon, + selected: false + })); +} + +// ======================================== +/* highlight all cities on the list of adress */ +function scheduleHighlightAddress (locations) { + if (!locations) { + jQuery ('div.scheduleAddresse').removeClass("poiAdrLight"); + return; + } + jQuery ('div.scheduleAddresse').each (function () { + var adrBlock = jQuery (this); + var location = adrBlock.attr ('location'); + if (location && (','+locations).indexOf (location) >= 0) + adrBlock.addClass("poiAdrLight"); + else + adrBlock.removeClass("poiAdrLight"); + }); +} + +function scheduleHighlightLocation (location) { + scheduleHighlightAddress (location); + scheduleHighlightPOI (location); +} + +// ======================================== +/* display cities from a UL */ +function scheduleAddAllCityFromUl (scheduleMap, ul) { + scheduleClearMarkers (scheduleMap); + ul.find ('li').each (function (index) { + var li = jQuery (this); + scheduleAddLatLonCityMarker (scheduleMap, li.attr ('insee'), + li.attr ('lat'), li.attr ('lon')); + }); + if (!scheduleUseMap) + return; + if (scheduleMap.poi !== undefined){ + scheduleMap.poi.changed (); + scheduleMap.map.render (); + } +} + +// ======================================== +function scheduleAddCityToUl (ul, cityName) { + var cityCode = /[0-9AB]{5,}/.exec (cityName); + var lat = ""; + var lon = ""; + if (cityCode != null) + cityCode = cityCode[0]; + if (!cityCode) + cityCode = inseeCityNameInsee [cityName.toLowerCase ()]; + if (!cityCode) + cityName = cityCode = cityName.replace(/[^A-ZÀÂÆÇÉÈÊËÎÏÑÔŒÙÛÜÿ a-zàâæçéèêëîïñôœùûüÿ'\-]/gi,''); + else { + cityName = inseeCityNameLatLon[cityCode][0]; + lat = inseeCityNameLatLon[cityCode][1]; + lon = inseeCityNameLatLon[cityCode][2]; + } + var recorded = false; + ul.find ('li[insee="'+cityCode+'"][lat="'+lat+'"][lon="'+lon+'"]').each (function (index) { + recorded = true; + scheduleSetSelectCity (jQuery (this)); + }); + if (recorded) + return; + var lonLat = + (lat && lon) ? + ' lat="'+lat+'" lon="'+lon+'"' : + ' class="unknown"'; + ul.append ('
  • '+ + ''+ + ' '+ + ''+cityName+'
  • '); + scheduleSetSelectCity (ul.find ("li").last ()); +} + +// ======================================== +/* Sort UL by city name (XXX what about addresse ?) */ +function scheduleSortUlCities (ul) { + ul.children ('li').sort (function (a, b) { + var upa = jQuery (a).find ('span.city').text ().toUpperCase (); + var upb = jQuery (b).find ('span.city').text ().toUpperCase (); + return (upa < upb) ? -1 : (upa > upb) ? 1 : 0; + }).appendTo (ul[0]); +} + +function scheduleAjaxPOILine (action) { + if (!scheduleSelectedCity) + // XXX dire quelque chose + return; + var insee = scheduleSelectedCity.attr ('insee'); + var lat = scheduleSelectedCity.attr ('lat'); + var lon = scheduleSelectedCity.attr ('lon'); + var addr = scheduleSelectedCity.find ('span.addresse').html ().replace (/
    /gi, '~br~'); + line = new Array (); + line.push (insee); + line.push (lat); + line.push (lon); + line.push (addr); + jQuery.ajax ({ + type: "POST", + url: scheduleAjaxPoiUri, + cache: true, + async: true, + data: { action: action, line: line.join ('|') }, + success: function (response) { alert (response); } + }); +} + +function scheduleAddInsee () { + scheduleAjaxPOILine ("add"); +} +function scheduleRemoveInsee () { + scheduleAjaxPOILine ("remove"); +} +function scheduleTestInsee () { +} + +function scheduleSetSelectCity (li) { + scheduleSelectedCity = li; + // change autocomplete + var cityCode = scheduleSelectedCity.attr ('insee'); + var form = scheduleSelectedCity.closest ('.scheduleCitiesForm'); + var input = form.find ('input[name="addr"]'); + input. val (scheduleSelectedCity.find ('span.addresse').html ().replace (/
    /gi, '~br~')); + jQuery.ajax ({ + type: "POST", + url: scheduleAjaxPoiUri, + cache: true, + async: true, + data: { action: "list", insee: cityCode }, + success: function (response) { + var db = jQuery.parseJSON (response); + source = new Array (); + for (var i = 0; i < db.length; i++) { + var vals = db[i].split ("|"); + source.push ("("+vals[0]+"|"+vals[1]+") "+vals[2]); + } + input.autocomplete ({ source: source }); + } + }); +} + +function scheduleFocusCity () { + if (!scheduleUseMap) + return; + var form = scheduleSelectedCity.closest ('.scheduleCitiesForm'); + // focus on map + form.find ('.scheduleMap').each (function () { + var scheduleMap = scheduleMapList [jQuery (this).attr ('id')]; + scheduleMap.map.getView ().setCenter (scheduleGetCoordFromLocation ([scheduleSelectedCity.attr ('lat'), scheduleSelectedCity.attr ('lon')])); + scheduleMap.map.getView ().setZoom (Math.min (scheduleMap.map.getView ().getZoom (), scheduleZoom)); + }); +} + +// ======================================== +/* User LI selection and focus on map */ +function scheduleSelectCity (item) { + scheduleSetSelectCity (jQuery (item).closest ('li')); + scheduleFocusCity (); +} + +// ======================================== +/* User remove LI */ +function scheduleRemoveCity (item) { + var li = jQuery (item).closest ('li'); + var ul = li.closest ('ul'); + li.remove (); + scheduleUpdateUlCity (ul); +} + +function scheduleUpdateUlCity (ul) { + ul.closest ('.scheduleCitiesForm'). find ('.scheduleMap').each (function () { + var scheduleMap = scheduleMapList [jQuery (this).attr ('id')]; + scheduleAddAllCityFromUl (scheduleMap, ul); + scheduleCenterMap (scheduleMap); + }); + scheduleCheckInputs (); +} + +// ======================================== +/* Find all initial values */ +function scheduleInitPOI () { + jQuery ('.schedulePOI').each (function () { + var poiDiv = jQuery (this); + var scheduleMap = scheduleMapList [poiDiv.find ('.scheduleMap').attr ('id')]; + scheduleClearMarkers (scheduleMap); + poiDiv.find ('span.poiLocations').each (function () { + var poiLocations = jQuery (this).text ().split (','); + for (var i = 0; i < poiLocations.length; i++) { + var latLon = poiLocations[i]; + var match = latLon.match (locationFormatRE); + if (match) { + var lat = parseFloat (match [1]).toFixed (schedulePrecision); + var lon = parseFloat (match [2]).toFixed (schedulePrecision); + scheduleAddLatLonCityMarker (scheduleMap, "", lat, lon); + } else + // cityCode = latLon + scheduleAddCityMarker (scheduleMap, latLon); + } + scheduleCenterMap (scheduleMap); + }); + poiDiv.find ('ul.poiLatLon').each (function () { + jQuery (this).find ('li').each (function (index) { + var li = jQuery (this); + scheduleAddLatLonCityMarker (scheduleMap, li.text (), + li.attr ('lat'), li.attr ('lon')); + }); + scheduleCenterMap (scheduleMap); + }); + }); + jQuery ('.scheduleCitiesForm').each (function () { + var scheduleCitiesForm = jQuery (this); + var scheduleMapDiv = scheduleCitiesForm.find ('.scheduleMap'); + if (scheduleMapDiv[0] === undefined) + return; + var scheduleMap = scheduleMapList [scheduleMapDiv.attr ('id')]; + scheduleAddAllCityFromUl (scheduleMap, jQuery (this).find ('.cities')); + scheduleCenterMap (scheduleMap); + }); +} + +// ======================================== +/* Find all maps */ +function scheduleInitMaps () { + if (!scheduleUseMap) + return; + + jQuery ('.scheduleMap').each (function () { + var mapDiv = jQuery (this).find ('div'); + if (mapDiv !== undefined && mapDiv && mapDiv.length) + return; + var mapId = jQuery (this).attr ('id'); + var osm = new ol.layer.Tile ({ + source: new ol.source.OSM () + }); + var poi = new ol.source.Vector ({ + features: [] + }); + iconStyle = new ol.style.Style ({ + image: new ol.style.Icon (/** @type {olx.style.IconOptions} */ ({ + anchor: [.5, 1], + anchorXUnits: 'fraction', + anchorYUnits: 'fraction', + opacity: 0.75, + src: schedulePoiUri + })) + }); + iconSelectedStyle = new ol.style.Style ({ + image: new ol.style.Icon (/** @type {olx.style.IconOptions} */ ({ + anchor: [.5, 1], + anchorXUnits: 'fraction', + anchorYUnits: 'fraction', + opacity: 0.75, + src: scheduleSelectedPoiUri + })) + }); + var clusters = new ol.source.Cluster({ + distance: 10, + source: poi, + }); + var styleCache = {false: {}, true: {}}; + var clusterLayer = new ol.layer.Vector({ + source: clusters, + style: function (feature) { + var features = feature.get ('features'); + var size = features.length; + if (size < 2) + return features [0].get ('selected') ? iconSelectedStyle : iconStyle; + var selected = false; + features.forEach (function (item) { + if (item.get ('selected')) + selected = true; + }); + + var style = styleCache[selected][size]; + if (!style) { + style = [new ol.style.Style({ + image: new ol.style.Icon (/** @type {olx.style.IconOptions} */ ({ + anchor: [.5, 1], + anchorXUnits: 'fraction', + anchorYUnits: 'fraction', + opacity: 0.75, + src: selected ? scheduleSelectedPoiUri : schedulePoiUri + })), + text: new ol.style.Text({ + text: size.toString (), + offsetY: -24 // XXX textpos conf ? + }) + })]; + styleCache[selected][size] = style; + } + return style; + } + }); + var map = new ol.Map ({ + target: mapId, + layers: [osm, clusterLayer], + //logo: false, + controls: ol.control.defaults ({ + zoom: true, + attribution: false, + rotate: false + }), + view: new ol.View ({ + center: ol.proj.fromLonLat (scheduleCenter), + zoom: scheduleZoom + }) + }); + if (jQuery (this).hasClass ("scheduleMapDisplay")) + map.set ("mapType", "display"); + if (jQuery (this).hasClass ("scheduleMapForm")) + map.set ("mapType", "form"); + if (jQuery (this).hasClass ("scheduleMapCalendar")) + map.set ("mapType", "calendar"); + + map.on ("singleclick", function (evt) { + var f; + map.forEachFeatureAtPixel (evt.pixel, function (feature) { + f = feature; + return true; + }); + switch (map.get ("mapType")) { + case "display": + if (f) { + if (f.get ('features').length) + f = f.get ('features')[0]; + window.open ("https://www.openstreetmap.org/?mlat="+f.get ('lat')+"&mlon="+f.get ('lon')+"&zoom="+scheduleZoom, "_self"); + } + break; + case "form": + var location = scheduleGetLocationFromCoord (evt.coordinate); + scheduleSelectedCity.attr ('lat', location[0]); + scheduleSelectedCity.attr ('lon', location[1]); + scheduleAddAllCityFromUl (scheduleMap, scheduleSelectedCity.closest ('ul')); + break; + } + }); + map.on ("pointermove", function (evt) { + var locations = new Set (); + map.forEachFeatureAtPixel (evt.pixel, function (feature) { + feature.get ('features').forEach (function (item) { + locations.add (item.get ('location')); + }); + }); + locations = Array.from (locations); + scheduleHighlightDays (locations); + scheduleHighlightLocation (locations.join (',')); + }); + var scheduleMap = { + mapId: mapId, + map: map, + poiSize: 0, + poi: poi, + clusterLayer: clusterLayer + }; + + scheduleMapList[mapId] = scheduleMap; + scheduleCenterMap (scheduleMap); + }); +} + +// ======================================== +/* Initialisation and attach function */ +jQuery (function () { + jQuery (function () { + // create tabs before OpenLayers stuff (important !) + jQuery (".scheduleTabForm").tabs ({ + active: 0 + }); + jQuery ("#scheduleHelp").accordion ({ + collapsible: true, + animated: false, + active: false + }); + jQuery.datepicker.setDefaults ({ + showOn: "both", + buttonImageOnly: true, + buttonImage: scheduleIconCalendarUri, + buttonText: "", + firstDay: 0 + }); + jQuery.datepicker.formatDate ("yy-mm-dd"); + jQuery (".scheduleTabForm .date").datepicker (); + + divError = jQuery ("div.schedule").prev ("div.error"); + if (divError !== undefined && divError && divError.length) + // XXX a tester (avant divError.size ()) + scheduleForceCheckInputs (); + }); + + // check and format form request + jQuery ('.scheduleFinalForm').submit (function () { + var scheduleFinalForm = jQuery (this); + if (!scheduleForceCheckInputs ()) + return false; + var scheduleForm = scheduleFinalForm.closest ('.scheduleTabForm'); + var scheduleCitiesForm = scheduleForm.find ('.scheduleCitiesForm'); + var cities = new Array (); + var lats = new Array (); + var lons = new Array (); + var addrs = new Array (); + scheduleCitiesForm.find ('li').each (function (index) { + var li = jQuery (this); + cities.push (li.attr ('insee')); + lats.push (li.attr ('lat')); + lons.push (li.attr ('lon')); + addrs.push (li.find ('span.addresse').html ()); + }); + scheduleFinalForm.append (''); + scheduleFinalForm.append (''); + scheduleFinalForm.append (''); + scheduleFinalForm.append ('/gi, '~br~')+'"/>'); + + var scheduleMiscForm = scheduleForm.find ('.scheduleMiscForm'); + scheduleMiscForm.find ('input[type="text"]').each (function (index) { + if (this.value) + scheduleFinalForm.append (''); + }); + scheduleMiscForm.find ('select:not([class="members"])').each (function (index) { + scheduleFinalForm.append (''); + }); + var members = new Array (); + scheduleMiscForm.find ('select[class="members"]').each (function (index) { + jQuery (this).find ('option:selected').each (function (index) { + members.push (jQuery (this).val ()); + }); + }); + if (members.length > 0) + scheduleFinalForm.append (''); + scheduleMiscForm.find ('textarea').each (function (index) { + var val = jQuery (this).val ().replace(/"/gi, "''"); + var name = jQuery (this).attr ('name'); + scheduleFinalForm.append (''); + }); + return true; + }); + + // city validation + jQuery ('.scheduleCitiesForm input[name="city"]').keypress (function (e) { + if (e.which != 13) + return; + var input = jQuery (this); + var form = input.closest ('.scheduleCitiesForm'); + var cityName = input.val (); + if (!cityName) + return false; + input.val (""); + form.find ('input[name="addr"]').val (""); + var ul = form.find ('ul'); + scheduleAddCityToUl (ul, cityName); + scheduleSortUlCities (ul); + scheduleUpdateUlCity (ul); + return false; + }); + + // full adress validation + jQuery ('.scheduleCitiesForm input[name="addr"]').keypress (function (e) { + if (e.which != 13) + return; + if (!scheduleSelectedCity) + return; + var input = jQuery (this); + var form = input.closest ('.scheduleCitiesForm'); + var addr = input.val (); + var match = addr.match (locationFormatRE); + if (match) { + addr = match [3]; + scheduleSelectedCity.attr ('lat', parseFloat (match [1]).toFixed (schedulePrecision)); + scheduleSelectedCity.attr ('lon', parseFloat (match [2]).toFixed (schedulePrecision)); + var scheduleMap = scheduleMapList [form.find ('.scheduleMap').attr ('id')]; + scheduleAddAllCityFromUl (scheduleMap, scheduleSelectedCity.closest ('ul')); + scheduleFocusCity (); + } + input.val (addr); + scheduleSelectedCity.find ('span.addresse').html (addr.replace (/~br~/gi, '
    ')); + }); + + // default form validation for each text field + jQuery ('.scheduleMiscForm input').keypress (function (e) { + if (e.which != 13) + return; + jQuery (this).closest ('.scheduleTabForm').find ('input[type="submit"]').first ().trigger ('click'); + }); + + // init maps + scheduleInitMaps (); + scheduleInitPOI (); +}); + +// ======================================== diff --git a/lib/scheduleForm.js b/lib/scheduleForm.js new file mode 100644 index 0000000..58adaed --- /dev/null +++ b/lib/scheduleForm.js @@ -0,0 +1,145 @@ +// ======================================== + +var scheduleDoCkeck = false; + +function scheduleForceCheckInputs () { + scheduleDoCkeck = true; + return scheduleCheckInputs (); +} + +function scheduleCheckSelect (select) { + var warning = select.find ('option:selected').val () == ""; + select.prev (".warningPlace").toggleClass ("warning", warning); + return warning; +} +function scheduleCheckInput (input) { + var warning = input.val () == ""; + input.toggleClass ("warning", warning); + return warning; +} +function scheduleCheckUL (ul) { + var warning = ul.children ().length == 0; + ul.toggleClass ("warning", warning); + return warning; +} +/* + * highlight missing input fields + */ +function scheduleCheckInputs () { + if (!scheduleDoCkeck) + return false; + var tab = jQuery (".scheduleTabForm"); + var tabs = tab.find ("li a.warningPlace"); + var checked = true; + var warning = false; + warning = scheduleCheckSelect (tab.find ('select[name="schd[audience]"]')); + warning = scheduleCheckSelect (tab.find ('select.members')) || warning; + tabs.slice (0,1).toggleClass ("warning", warning); + if (warning) + checked = false; + warning = scheduleCheckSelect (tab.find ('select[name="schd[what]"]')); + warning = scheduleCheckInput (tab.find ('input[name="schd[title]"]')) || warning; + tabs.slice (1,2).toggleClass ("warning", warning); + if (warning) + checked = false; + warning = scheduleCheckUL (tab.find ('ul.cities')); + tabs.slice (2,3).toggleClass ("warning", warning); + if (warning) + checked = false; + warning = scheduleCheckInput (tab.find ('input[name="schd[from]"]')); + tabs.slice (3,4).toggleClass ("warning", warning); + if (warning) + checked = false; + warning = scheduleCheckInput (tab.find ('input.edit')); + tabs.slice (4,5).toggleClass ("warning", warning); + if (warning) + checked = false; + return checked; +} + +/** + * switch all selected events for delete. + */ +function scheduleSwitchSelection (form) { + for (var i = 0; i < form.elements.length; i++) + form.elements[i].checked = !form.elements[i].checked; +} + +/* + * change member selection mode according share status + */ +function scheduleSharedEvent (form) { + var form = jQuery (form); + var shared = null; + + form.find ('select[name="schd[shared]"]').each (function () { + if (this.options [this.selectedIndex].text != "") + shared = "true"; + }); + form.find ('select[name="schd[member]"]').each (function () { + this.multiple = shared; + this.size = shared ? 9 : 1; + }); +} + +/* + * display repeat input according selection + */ +function scheduleUpdateRepeatType (repeatSelection) { + var selectedValue = repeatSelection.options [repeatSelection.selectedIndex].value; + var inputs = repeatSelection.form.elements; + var disabledWeek = (selectedValue != "week"); + var disabledDay = (selectedValue != "dayMonth"); + var disabledDate = (selectedValue != "dateMonth"); + var checkedDay = 0; + // XXX jquery ? + for (var i = 0; i < inputs.length; i++) { + var name = inputs[i].name; + if (name == "schd[weekDays][]") { + inputs[i].disabled = disabledWeek; + if (inputs[i].checked) + checkedDay++; + } else if (name == "schd[weekRank]") + inputs[i].disabled = disabledDay; + else if (name == "schd[dayInWeek]") + inputs[i].disabled = disabledDay; + else if (name == "schd[dayRank]") + inputs[i].disabled = disabledDate; + } + if (!disabledWeek && checkedDay == 0) + scheduleForceDay (inputs); + // XXX forcer le Ie jour du mois +} + +/* + * display repeat input according selection + */ +function scheduleUpdateWeekDays (DaySelection) { + var inputs = DaySelection.form.elements; + var checkedDay = 0; + // XXX jquery + for (var i = 0; i < inputs.length; i++) { + if (inputs[i].name == "schd[weekDays][]") + if (inputs[i].checked) + checkedDay++; + } + if (checkedDay == 0) + scheduleForceDay (inputs); +} + +function scheduleForceDay (inputs) { + var date = document.getElementById ("scheduleFrom").value; + if (!date) + date = new Date (); + else { + date = date.split ('/'); // XXX bug format FR seulement + date [1] = parseInt (date [1]) - 1; + if (date[2] < 100) + date [2] = parseInt (date [2]) + 2000; + date = new Date (date[2], date [1], date [0]); + } + var rankDay = date.getDay(); + for (var i = 0; i < inputs.length; i++) + if (inputs[i].name == "schd[weekDays][]" && inputs[i].value == rankDay) + inputs[i].checked = true; +} diff --git a/lib/scheduleTable.js b/lib/scheduleTable.js new file mode 100644 index 0000000..1e6d698 --- /dev/null +++ b/lib/scheduleTable.js @@ -0,0 +1,93 @@ +// ======================================== +/* highlight day and cities in table according the event under the mouse */ +function scheduleHighlightEvent (day, locations) { + jQuery ('div.cityBubble div.bubble').each (function () { + jQuery (this).hide (); + }); + scheduleHighlightPOI (locations); + jQuery ('div.schedule table.short div.mapSelect').each (function () { + var showDay = jQuery (this); + var today = showDay.attr ('day'); + if (day && today == day) + showDay.show (); + else + showDay.hide (); + }); +} + +// ======================================== +/* highlight all days in table according the city under the mouse */ +function scheduleHighlightDays (targetLocations) { + jQuery ('div.cityBubble div.bubble').each (function () { + jQuery (this).hide (); + }); + if (!targetLocations || !targetLocations.length) { + jQuery ('div.schedule table.short div.mapSelect').hide (); + return; + } + targetLocations.forEach (function (location) { + jQuery ('div.cityBubble[location="'+location+'"] div.bubble').each (function () { + jQuery (this).show (); + }); + }); + jQuery ('div.schedule table.short div.mapSelect').each (function () { + var showDay = jQuery (this); + var eventLocations = showDay.attr ('locations'); + if (!eventLocations) { + showDay.hide (); + return; + } + var doShow = false; + targetLocations.forEach (function (location) { + if ((','+eventLocations+',').indexOf (location) >= 0) { + doShow = true; + return; + } + }); + if (doShow) + showDay.show (); + else + showDay.hide (); + }); +} + +var scheduleMapList; + +// ======================================== +/* highlight all cities on the map according the day under the mouse */ +function scheduleHighlightPOI (locations) { + if (!locations) { + jQuery ('div.schedule table.short div.mapSelect').hide (); + jQuery.each (scheduleMapList, function (mapId, scheduleMap) { + scheduleMap.poi.forEachFeature (function (item) { + item.set ('selected', false); + }); + scheduleMap.poi.changed (); + scheduleMap.map.render (); + }); + return; + } + jQuery.each (scheduleMapList, function (mapId, scheduleMap) { + var changed = false; + scheduleMap.poi.forEachFeature (function (item) { + var location = item.get ('location'); + item.set ('selected', (location && (','+locations).indexOf (location) >= 0)); + }); + scheduleMap.poi.changed (); + scheduleMap.map.render (); + }); +} +// ======================================== +function scheduleChangeDate (obj, ns, mapId, date) { + var table = jQuery (obj).closest ("table")[0]; + scheduleSend (table, + DOKU_BASE+"lib/plugins/schedule/ajax.php", + "schd[ns]="+ns+"&schd[mapId]="+mapId+"&schd[action]=changeDate&schd[date]="+date); +} + +// ======================================== +function scheduleSelectDate (date) { + jQuery ("#scheduleFrom").val (date); +} + +// ======================================== diff --git a/plugin.info.txt b/plugin.info.txt new file mode 100644 index 0000000..39e052e --- /dev/null +++ b/plugin.info.txt @@ -0,0 +1,7 @@ +base schedule +author Francois Merciol +email dokuplugin@merciol.fr +date 2021-03-31 +name Schedule Plugin +desc Includes and manage shcedule into a page (V4) +url http://www.dokuwiki.org/plugin:schedule diff --git a/poiDB.class.php b/poiDB.class.php new file mode 100644 index 0000000..8ac33bc --- /dev/null +++ b/poiDB.class.php @@ -0,0 +1,113 @@ + + * + * Plugin Schedule: manage events per wiki @groups + + */ + +// ============================================================ +class poiDB { + var $modify = false; + var $dbFileName; + var $dbByInsee; + var $allLines; + + // ============================================================ + function __construct ($plugin) { + global $conf; + $this->dbFileName = + ((!$conf['savedir'] || strpos ($conf['savedir'], '.') === 0) ? DOKU_INC : ""). + $conf['savedir'].'/'.trim ($plugin->getConf ('dataDir').'/poiDB.txt'); + $this->read (); + } + + // ============================================================ + function getInsee ($insee) { + if (!isset ($this->dbByInsee [$insee])) + return array (); + return $this->dbByInsee [$insee]; + } + + // ============================================================ + function addLine ($line) { + $this->cleanDB (); + $line = $this->cleanLine ($line); + if (!$this->allLines || !in_array ($line, $this->allLines)) { + $this->allLines [] = $line; + sort ($this->allLines); + $this->modify = true; + } + $this->write (); + echo "record".NL; + } + + // ============================================================ + function removeLine ($line) { + $this->cleanDB (); + $line = $this->cleanLine ($line); + + if (($key = array_search ($line, $this->allLines)) !== false) { + unset ($this->allLines [$key]); + $this->modify = true; + } + $this->write (); + } + + // ============================================================ + function cleanLine ($line) { + // XXX vérifier nombre + $data = explode ("|", $line); + foreach ($data as $key => $val) + $data[$key] = trim ($val); + $data [1] = number_format ((float)$data[1], 4); + $data [2] = number_format ((float)$data[2], 4); + return implode ("|", $data); + } + + // ============================================================ + function cleanDB () { + if ($this->allLines) + foreach ($this->allLines as $key => $line) { + $lineCleaned = $this->cleanLine ($line); + if ($lineCleaned != $line) + $this->modify = true; + $this->allLines [$key] = $lineCleaned; + } + } + + // ============================================================ + function read () { + $this->dbByInsee = array (); + $handle = @fopen ($this->dbFileName, "r"); + if ($handle) { + while (($line = fgets ($handle)) !== false) { + $line = trim (preg_replace ("/#.*$/", "", str_replace ('\\\\', '~br~', $line))); + if (!$line) + continue; + $this->allLines [] = $line; + list ($insee, $end) = explode ('|', $line, 2); + $this->dbByInsee [$insee][] = $end; + } + fclose ($handle); + } + } + + // ============================================================ + function write () { + if (!$this->modify) { + echo "POI DB unchanged !".NL; + return; + } + $handle = @fopen ($this->dbFileName, "w"); + if ($handle == false) + die("unable to create file: ".$this->dbFileName); // XXX + foreach ($this->allLines as $line) + fputs ($handle, $line.NL); + fclose ($handle); + echo "POI DB updated !".NL; + } + + // ============================================================ +} diff --git a/schedule.class.php b/schedule.class.php new file mode 100644 index 0000000..f8d819c --- /dev/null +++ b/schedule.class.php @@ -0,0 +1,64 @@ + + * + * Plugin Schedule: manage events per wiki @groups + */ +if (!defined ('DOKU_INC')) + define ('DOKU_INC', realpath (dirname (__FILE__).'/../../../').'/'); +if (!defined ('DOKU_PLUGIN')) + define ('DOKU_PLUGIN', DOKU_INC.'lib/plugins/'); + +// ============================================================ +// event class +class schedule { + var $id; // event id + var $member; // event owner + var $from; // event begining date + var $to; // event ending date + var $at; // event begining hour + var $what; // event type + var $where; // event place insee code or city name + var $lon; // event place latitude + var $lat; // event place longitude + var $addr; // event place addresse + var $audience; // event expected audience + var $shared; // shared event + var $title; // event page title + var $lead; // event lead + var $posterURL; // event poster URL + var $paperURL; // event paper URL + var $remark; // event remark + var $rate; // event rate + + var $repeatFlag; // event repeat or not + var $repeat; // event step of repeat + var $repeatType; // event type of repeat + var $repeatGrouped; // repeat event grouped on top + var $weekDays; // event array days concern by the repeating week + var $weekRank; // event week concern by the repeating month + var $dayInWeek; // event day in the week concern by the repeating month + var $dayRank; // event day in the month concern by the repeating month + + var $requestMembers; // all members proposed during the request +} + +// ============================================================ +// sort event by event date, then creation date +function cmpSchedule ($a, $b) { + if ($a->from == $b->from) { + if ($a->at == $b->at) + return ($a->id < $b->id) ? -1 : 1; + return ($a->at < $b->at) ? -1 : 1; + } + return ($a->from < $b->from) ? -1 : 1; +} +// sort info bubbles by date +function cmpScheduleInDay ($a, $b) { + if ($a['at'] == $b['at']) + return ($a['id'] < $b['id']) ? -1 : 1; + return ($a['at'] < $b['at']) ? -1 : 1; +} + +// ============================================================ diff --git a/scheduleInseeCities.php b/scheduleInseeCities.php new file mode 100644 index 0000000..cc3b80a --- /dev/null +++ b/scheduleInseeCities.php @@ -0,0 +1,2830 @@ + array ("Ablon", 49.39425, 0.28676), + 14002 => array ("Acqueville", 48.97077, -0.37056), + 14003 => array ("Agy", 49.23767, -0.77919), + 14004 => array ("Aignerville", 49.32618, -0.91846), + 14005 => array ("Airan", 49.09531, -0.14544), + 14006 => array ("Amayé-sur-Orne", 49.09112, -0.43422), + 14007 => array ("Amayé-sur-Seulles", 49.08206, -0.71769), + 14008 => array ("Amblie", 49.28488, -0.50146), + 14009 => array ("Amfreville", 49.25014, -0.24157), + 14011 => array ("Anctoville", 49.11298, -0.69082), + 14012 => array ("Angerville", 49.24430, -0.02755), + 14013 => array ("Angoville", 48.94377, -0.37343), + 14014 => array ("Anguerny", 49.26535, -0.39693), + 14015 => array ("Anisy", 49.25086, -0.39879), + 14016 => array ("Annebault", 49.24896, 0.05404), + 14019 => array ("Arganchy", 49.22998, -0.73162), + 14020 => array ("Argences", 49.13397, -0.15851), + 14021 => array ("Arromanches-les-Bains", 49.33579, -0.62061), + 14022 => array ("Asnelles", 49.33365, -0.58463), + 14023 => array ("Asnières-en-Bessin", 49.36007, -0.93825), + 14024 => array ("Auberville", 49.31109, -0.02689), + 14025 => array ("Aubigny", 48.91457, -0.21554), + 14026 => array ("Audrieu", 49.20465, -0.60423), + 14027 => array ("Aunay-sur-Odon", 49.01447, -0.63392), + 14028 => array ("Auquainville", 49.05548, 0.24035), + 14029 => array ("Les Autels-Saint-Bazile", 48.94698, 0.10280), + 14030 => array ("Authie", 49.20581, -0.43065), + 14031 => array ("Les Authieux-Papion", 49.06373, 0.05312), + 14032 => array ("Les Authieux-sur-Calonne", 49.28411, 0.27554), + 14033 => array ("Auvillars", 49.19740, 0.07577), + 14034 => array ("Avenay", 49.09574, -0.46440), + 14035 => array ("Balleroy", 49.18016, -0.83927), + 14036 => array ("Banneville-la-Campagne", 49.16868, -0.22432), + 14037 => array ("Banneville-sur-Ajon", 49.06159, -0.56799), + 14038 => array ("Banville", 49.30916, -0.48595), + 14039 => array ("Barbery", 49.01422, -0.34531), + 14040 => array ("Barbeville", 49.27385, -0.75503), + 14041 => array ("Barneville-la-Bertran", 49.39136, 0.18216), + 14042 => array ("Baron-sur-Odon", 49.12808, -0.48192), + 14043 => array ("Barou-en-Auge", 48.92975, -0.03861), + 14044 => array ("Basly", 49.27590, -0.42610), + 14045 => array ("Basseneville", 49.20674, -0.13500), + 14046 => array ("Bavent", 49.22602, -0.18266), + 14047 => array ("Bayeux", 49.27765, -0.70462), + 14049 => array ("Bazenville", 49.30060, -0.58455), + 14050 => array ("La Bazoque", 49.15350, -0.87759), + 14052 => array ("Beaulieu", 48.91754, -0.81789), + 14053 => array ("Beaumais", 48.89922, -0.07108), + 14054 => array ("Beaumesnil", 48.89676, -0.96836), + 14055 => array ("Beaumont-en-Auge", 49.26737, 0.11018), + 14056 => array ("Bauquay", 49.03388, -0.62021), + 14057 => array ("Bellengreville", 49.12026, -0.23340), + 14058 => array ("Bellou", 48.98772, 0.22340), + 14059 => array ("Benerville-sur-Mer", 49.33901, 0.04779), + 14060 => array ("Bénouville", 49.24370, -0.28785), + 14061 => array ("Le Bény-Bocage", 48.93110, -0.82970), + 14062 => array ("Bény-sur-Mer", 49.29065, -0.43623), + 14063 => array ("Bernesq", 49.27377, -0.93144), + 14064 => array ("Bernières-d'Ailly", 48.94746, -0.10228), + 14065 => array ("Bernières-le-Patry", 48.80894, -0.74601), + 14066 => array ("Bernières-sur-Mer", 49.31946, -0.41774), + 14068 => array ("Biéville-Beuville", 49.23948, -0.33689), + 14069 => array ("Beuvillers", 49.12628, 0.24974), + 14070 => array ("Beuvron-en-Auge", 49.19600, -0.04245), + 14073 => array ("La Bigne", 49.01131, -0.71799), + 14074 => array ("Billy", 49.08882, -0.19495), + 14075 => array ("Bissières", 49.11366, -0.08969), + 14076 => array ("Blainville-sur-Orne", 49.22815, -0.30432), + 14077 => array ("Blangy-le-Château", 49.24673, 0.28321), + 14078 => array ("Blay", 49.28043, -0.83613), + 14079 => array ("Blonville-sur-Mer", 49.32226, 0.03223), + 14080 => array ("Le Bô", 48.90635, -0.44166), + 14081 => array ("Boissey", 49.01975, 0.05006), + 14082 => array ("La Boissière", 49.12995, 0.13202), + 14083 => array ("Bonnebosq", 49.21831, 0.09060), + 14084 => array ("Bonnemaison", 49.00364, -0.58574), + 14085 => array ("Bonneville-la-Louvet", 49.27308, 0.34197), + 14086 => array ("Bonneville-sur-Touques", 49.32993, 0.11921), + 14087 => array ("Bonnoeil", 48.92250, -0.36568), + 14088 => array ("Bons-Tassilly", 48.95924, -0.23041), + 14089 => array ("Bougy", 49.10860, -0.52237), + 14090 => array ("Boulon", 49.04752, -0.37826), + 14091 => array ("Bourgeauville", 49.26814, 0.05838), + 14092 => array ("Bourguébus", 49.11597, -0.28948), + 14093 => array ("Branville", 49.26918, 0.02336), + 14096 => array ("Brémoy", 48.99843, -0.78084), + 14097 => array ("Bretteville-le-Rabet", 49.02971, -0.25828), + 14098 => array ("Bretteville-l'Orgueilleuse", 49.21515, -0.51673), + 14099 => array ("Bretteville-sur-Dives", 49.03616, 0.00163), + 14100 => array ("Bretteville-sur-Laize", 49.04983, -0.32604), + 14101 => array ("Bretteville-sur-Odon", 49.17316, -0.42222), + 14102 => array ("Le Breuil-en-Auge", 49.22781, 0.21876), + 14103 => array ("Le Breuil-en-Bessin", 49.25461, -0.85545), + 14104 => array ("Le Brévedent", 49.22698, 0.29388), + 14105 => array ("La Brévière", 48.97605, 0.16731), + 14106 => array ("Bréville-les-Monts", 49.23608, -0.22104), + 14107 => array ("Bricqueville", 49.28931, -0.95691), + 14109 => array ("Brouay", 49.20970, -0.56294), + 14110 => array ("Brucourt", 49.24680, -0.10382), + 14111 => array ("Bucéels", 49.18694, -0.63961), + 14113 => array ("Burcy", 48.86940, -0.80525), + 14115 => array ("Bures-les-Monts", 48.95175, -0.96781), + 14116 => array ("Le Bû-sur-Rouvres", 49.03245, -0.17672), + 14117 => array ("Cabourg", 49.28348, -0.12551), + 14118 => array ("Caen", 49.18479, -0.36980), + 14119 => array ("Cagny", 49.15420, -0.26067), + 14120 => array ("Cahagnes", 49.06712, -0.76279), + 14121 => array ("Cahagnolles", 49.16335, -0.77615), + 14122 => array ("La Caine", 49.03523, -0.51576), + 14123 => array ("Cairon", 49.23412, -0.43789), + 14124 => array ("La Cambe", 49.34470, -1.00930), + 14125 => array ("Cambes-en-Plaine", 49.23148, -0.38346), + 14126 => array ("Cambremer", 49.14660, 0.05390), + 14127 => array ("Campagnolles", 48.88930, -0.93528), + 14128 => array ("Campandré-Valcongrain", 48.98135, -0.58496), + 14129 => array ("Campeaux", 48.95212, -0.92601), + 14130 => array ("Campigny", 49.24921, -0.80055), + 14131 => array ("Canapville", 49.31709, 0.13608), + 14132 => array ("Canchy", 49.33085, -0.98489), + 14134 => array ("Canteloup", 49.13661, -0.12131), + 14135 => array ("Carcagny", 49.23919, -0.61396), + 14136 => array ("Cardonville", 49.34597, -1.05341), + 14137 => array ("Carpiquet", 49.18661, -0.45112), + 14138 => array ("Cartigny-l'épinay", 49.23927, -1.00695), + 14139 => array ("Carville", 48.93494, -0.87170), + 14140 => array ("Castillon", 49.19539, -0.79756), + 14141 => array ("Castillon-en-Auge", 49.03463, 0.09193), + 14142 => array ("Castilly", 49.27382, -1.01618), + 14143 => array ("Caumont-l'éventé", 49.09643, -0.81243), + 14144 => array ("Caumont-sur-Orne", 48.95103, -0.47081), + 14145 => array ("Cauvicourt", 49.04751, -0.25989), + 14146 => array ("Cauville", 48.95185, -0.56415), + 14147 => array ("Cernay", 49.01638, 0.32390), + 14148 => array ("Cerqueux", 48.99700, 0.34767), + 14149 => array ("Cesny-aux-Vignes", 49.08629, -0.12097), + 14150 => array ("Cesny-Bois-Halbout", 48.98532, -0.39446), + 14151 => array ("Champ-du-Boult", 48.79005, -0.99994), + 14152 => array ("La Chapelle-Engerbold", 48.88893, -0.60122), + 14153 => array ("La Chapelle-Haute-Grue", 48.96403, 0.14599), + 14154 => array ("La Chapelle-Yvon", 49.06384, 0.35284), + 14155 => array ("Cheffreville-Tonnencourt", 49.02809, 0.23315), + 14156 => array ("Chênedollé", 48.84714, -0.76487), + 14157 => array ("Cheux", 49.16923, -0.52937), + 14158 => array ("Chicheboville", 49.10513, -0.21993), + 14159 => array ("Chouain", 49.20971, -0.64073), + 14160 => array ("Cintheaux", 49.06577, -0.28369), + 14161 => array ("Clarbec", 49.24117, 0.13457), + 14162 => array ("Clécy", 48.90255, -0.49358), + 14163 => array ("Cléville", 49.14405, -0.09443), + 14164 => array ("Clinchamps-sur-Orne", 49.08279, -0.40397), + 14165 => array ("Colleville-sur-Mer", 49.35032, -0.84846), + 14166 => array ("Colleville-Montgomery", 49.27451, -0.30139), + 14167 => array ("Colombelles", 49.19824, -0.29550), + 14168 => array ("Colombières", 49.30316, -0.98845), + 14169 => array ("Colombiers-sur-Seulles", 49.29923, -0.50600), + 14170 => array ("Colomby-sur-Thaon", 49.26344, -0.42068), + 14171 => array ("Combray", 48.94614, -0.44569), + 14172 => array ("Commes", 49.33499, -0.73901), + 14173 => array ("Condé-sur-Ifs", 49.03650, -0.13968), + 14174 => array ("Condé-sur-Noireau", 48.84969, -0.56250), + 14175 => array ("Condé-sur-Seulles", 49.22113, -0.64127), + 14176 => array ("Conteville", 49.08909, -0.24518), + 14177 => array ("Coquainvilliers", 49.20246, 0.19731), + 14178 => array ("Corbon", 49.14543, -0.02795), + 14179 => array ("Cordebugle", 49.11342, 0.37983), + 14180 => array ("Cordey", 48.85165, -0.22272), + 14181 => array ("Cormelles-le-Royal", 49.15477, -0.32741), + 14182 => array ("Cormolain", 49.12936, -0.86304), + 14183 => array ("Cossesseville", 48.89463, -0.42518), + 14184 => array ("Cottun", 49.26794, -0.78575), + 14185 => array ("Coudray-Rabut", 49.30368, 0.18059), + 14186 => array ("Coulombs", 49.24870, -0.56800), + 14187 => array ("Coulonces", 48.86494, -0.91991), + 14188 => array ("Coulvain", 49.05363, -0.72137), + 14189 => array ("Coupesarte", 49.05528, 0.10376), + 14190 => array ("Courcy", 48.96958, -0.03376), + 14191 => array ("Courseulles-sur-Mer", 49.32056, -0.44837), + 14192 => array ("Courson", 48.85274, -1.08893), + 14193 => array ("Courtonne-la-Meurdrac", 49.12024, 0.32905), + 14194 => array ("Courtonne-les-Deux-églises", 49.08883, 0.37808), + 14195 => array ("Courvaudon", 49.02684, -0.57384), + 14196 => array ("Crépon", 49.31451, -0.54565), + 14197 => array ("Cresserons", 49.28763, -0.35425), + 14198 => array ("Cresseveuille", 49.23874, 0.01175), + 14200 => array ("Creully", 49.28645, -0.54332), + 14201 => array ("Crèvecoeur-en-Auge", 49.12355, 0.02056), + 14202 => array ("Cricqueboeuf", 49.39653, 0.13909), + 14203 => array ("Cricqueville-en-Auge", 49.23712, -0.07328), + 14204 => array ("Cricqueville-en-Bessin", 49.37933, -1.00061), + 14205 => array ("Cristot", 49.19418, -0.57085), + 14206 => array ("Crocy", 48.87708, -0.05176), + 14207 => array ("Croisilles", 49.00341, -0.45208), + 14208 => array ("Croissanville", 49.11199, -0.10865), + 14209 => array ("Crouay", 49.26903, -0.81737), + 14210 => array ("La Croupte", 49.02393, 0.27919), + 14211 => array ("Culey-le-Patry", 48.95635, -0.53115), + 14212 => array ("Cully", 49.25046, -0.52791), + 14213 => array ("Curcy-sur-Orne", 49.00759, -0.50688), + 14214 => array ("Cussy", 49.28374, -0.76215), + 14215 => array ("Cuverville", 49.19178, -0.26143), + 14216 => array ("Damblainville", 48.92193, -0.12687), + 14217 => array ("Dampierre", 49.04149, -0.86631), + 14218 => array ("Danestal", 49.24358, 0.03296), + 14219 => array ("Danvou-la-Ferrière", 48.96390, -0.67974), + 14220 => array ("Deauville", 49.35438, 0.07446), + 14221 => array ("Démouville", 49.17780, -0.26313), + 14222 => array ("Le Désert", 48.89911, -0.81038), + 14223 => array ("Le Détroit", 48.87385, -0.34505), + 14224 => array ("Deux-Jumeaux", 49.35479, -0.96879), + 14225 => array ("Dives-sur-Mer", 49.28295, -0.08917), + 14226 => array ("Donnay", 48.94693, -0.41732), + 14227 => array ("Douville-en-Auge", 49.26398, -0.02126), + 14228 => array ("Douvres-la-Délivrande", 49.28959, -0.39232), + 14229 => array ("Dozulé", 49.22903, -0.04489), + 14230 => array ("Drubec", 49.24968, 0.10404), + 14231 => array ("Beaufour-Druval", 49.21417, 0.02348), + 14232 => array ("Ducy-Sainte-Marguerite", 49.22582, -0.61113), + 14235 => array ("écrammeville", 49.32165, -0.94234), + 14236 => array ("Ellon", 49.22764, -0.68136), + 14237 => array ("émiéville", 49.15360, -0.22404), + 14238 => array ("Englesqueville-en-Auge", 49.33185, 0.14988), + 14239 => array ("Englesqueville-la-Percée", 49.37940, -0.95137), + 14240 => array ("épaney", 48.94548, -0.15942), + 14241 => array ("épinay-sur-Odon", 49.06883, -0.61907), + 14242 => array ("épron", 49.22002, -0.36458), + 14243 => array ("équemauville", 49.40315, 0.20991), + 14244 => array ("Eraines", 48.90485, -0.15710), + 14245 => array ("Ernes", 49.01143, -0.12522), + 14246 => array ("Escoville", 49.20575, -0.24562), + 14248 => array ("Espins", 48.99967, -0.41466), + 14249 => array ("Esquay-Notre-Dame", 49.11280, -0.47110), + 14250 => array ("Esquay-sur-Seulles", 49.27165, -0.62241), + 14251 => array ("Esson", 48.96905, -0.45950), + 14252 => array ("Estrées-la-Campagne", 49.00799, -0.24305), + 14253 => array ("Estry", 48.90157, -0.73024), + 14254 => array ("éterville", 49.14787, -0.42881), + 14255 => array ("étouvy", 48.89006, -0.89917), + 14256 => array ("étréham", 49.32080, -0.79327), + 14257 => array ("évrecy", 49.09400, -0.50137), + 14258 => array ("Falaise", 48.89578, -0.19340), + 14259 => array ("Familly", 48.96077, 0.34718), + 14260 => array ("Fauguernon", 49.18902, 0.26885), + 14261 => array ("Le Faulq", 49.23872, 0.31792), + 14264 => array ("La Ferrière-Harang", 48.97006, -0.88672), + 14265 => array ("Fervaques", 49.03711, 0.27006), + 14266 => array ("Feuguerolles-Bully", 49.11193, -0.40758), + 14268 => array ("Fierville-Bray", 49.06471, -0.17959), + 14269 => array ("Fierville-les-Parcs", 49.24388, 0.23071), + 14270 => array ("Firfol", 49.15206, 0.32164), + 14271 => array ("Fleury-sur-Orne", 49.14442, -0.37761), + 14272 => array ("La Folie", 49.26274, -0.97034), + 14273 => array ("La Folletière-Abenon", 48.97385, 0.40800), + 14274 => array ("Fontaine-étoupefour", 49.13799, -0.45420), + 14275 => array ("Fontaine-Henry", 49.27778, -0.46584), + 14276 => array ("Fontaine-le-Pin", 48.97514, -0.29137), + 14277 => array ("Fontenay-le-Marmion", 49.08701, -0.34496), + 14278 => array ("Fontenay-le-Pesnel", 49.17360, -0.58043), + 14279 => array ("Fontenermont", 48.82115, -1.10018), + 14280 => array ("Formentin", 49.20307, 0.13581), + 14281 => array ("Formigny", 49.33584, -0.89641), + 14282 => array ("Foulognes", 49.13984, -0.80595), + 14283 => array ("Fourches", 48.85852, -0.07424), + 14284 => array ("Fourneaux-le-Val", 48.86254, -0.26982), + 14285 => array ("Le Fournet", 49.20028, 0.11172), + 14286 => array ("Fourneville", 49.35750, 0.23248), + 14287 => array ("Frénouville", 49.13274, -0.24902), + 14288 => array ("Le Fresne-Camilly", 49.25623, -0.48766), + 14289 => array ("Fresné-la-Mère", 48.88804, -0.12505), + 14290 => array ("Fresney-le-Puceux", 49.06609, -0.35307), + 14291 => array ("Fresney-le-Vieux", 49.00510, -0.38627), + 14292 => array ("Friardel", 48.99873, 0.38823), + 14293 => array ("Fumichon", 49.17024, 0.37668), + 14294 => array ("Garcelles-Secqueville", 49.09671, -0.28075), + 14296 => array ("Le Gast", 48.79181, -1.06068), + 14297 => array ("Gavrus", 49.11672, -0.50796), + 14298 => array ("Géfosse-Fontenay", 49.36141, -1.08505), + 14299 => array ("Genneville", 49.35986, 0.27371), + 14300 => array ("Gerrots", 49.19354, 0.00245), + 14301 => array ("Giberville", 49.17815, -0.28516), + 14302 => array ("Glanville", 49.27677, 0.07501), + 14303 => array ("Glos", 49.12478, 0.28133), + 14304 => array ("Gonneville-sur-Honfleur", 49.38148, 0.23637), + 14305 => array ("Gonneville-sur-Mer", 49.28954, -0.03631), + 14306 => array ("Gonneville-en-Auge", 49.25924, -0.18835), + 14307 => array ("Goupillières", 49.03239, -0.47937), + 14308 => array ("Goustranville", 49.22278, -0.10838), + 14309 => array ("Gouvix", 49.03655, -0.30897), + 14310 => array ("Grainville-Langannerie", 49.00903, -0.27186), + 14311 => array ("Grainville-sur-Odon", 49.13862, -0.53167), + 14312 => array ("Grandcamp-Maisy", 49.37543, -1.04464), + 14313 => array ("Grandchamp-le-Château", 49.08437, 0.07536), + 14316 => array ("Grangues", 49.26206, -0.05819), + 14317 => array ("La Graverie", 48.89655, -0.86000), + 14318 => array ("Graye-sur-Mer", 49.32936, -0.48668), + 14319 => array ("Grentheville", 49.14601, -0.29357), + 14320 => array ("Grimbosq", 49.04467, -0.44362), + 14322 => array ("Guéron", 49.24766, -0.70984), + 14324 => array ("Hamars", 49.00592, -0.54734), + 14325 => array ("Hermanville-sur-Mer", 49.28324, -0.32297), + 14326 => array ("Hermival-les-Vaux", 49.16620, 0.29127), + 14327 => array ("Hérouville-Saint-Clair", 49.20735, -0.33102), + 14328 => array ("Hérouvillette", 49.21404, -0.26017), + 14329 => array ("Heuland", 49.26480, -0.00106), + 14330 => array ("Heurtevent", 48.98206, 0.13149), + 14331 => array ("Hiéville", 49.02222, -0.00304), + 14332 => array ("La Hoguette", 48.85306, -0.16067), + 14333 => array ("Honfleur", 49.41294, 0.23757), + 14334 => array ("L'Hôtellerie", 49.14098, 0.40712), + 14335 => array ("Hotot-en-Auge", 49.17742, -0.07637), + 14336 => array ("Hottot-les-Bagues", 49.14608, -0.65354), + 14337 => array ("La Houblonnière", 49.12330, 0.10246), + 14338 => array ("Houlgate", 49.29740, -0.06775), + 14339 => array ("Hubert-Folie", 49.12845, -0.31604), + 14341 => array ("Ifs", 49.14057, -0.34269), + 14342 => array ("Isigny-sur-Mer", 49.30576, -1.10256), + 14343 => array ("Les Isles-Bardel", 48.83742, -0.34905), + 14344 => array ("Janville", 49.16379, -0.16374), + 14345 => array ("Jort", 48.97333, -0.07288), + 14346 => array ("Juaye-Mondaye", 49.20492, -0.69055), + 14347 => array ("Jurques", 49.02119, -0.74576), + 14348 => array ("Juvigny-sur-Seulles", 49.15565, -0.60264), + 14349 => array ("Laize-la-Ville", 49.08011, -0.38062), + 14350 => array ("La Lande-sur-Drôme", 49.07325, -0.86001), + 14352 => array ("Landelles-et-Coupigny", 48.89839, -1.00295), + 14353 => array ("Landes-sur-Ajon", 49.07649, -0.57202), + 14354 => array ("Langrune-sur-Mer", 49.31298, -0.37826), + 14355 => array ("Lantheuil", 49.26718, -0.51610), + 14356 => array ("Lasson", 49.23175, -0.47343), + 14357 => array ("Lassy", 48.91364, -0.68414), + 14358 => array ("Léaupartie", 49.18022, 0.05276), + 14359 => array ("Lécaude", 49.10385, 0.08463), + 14360 => array ("Leffard", 48.91637, -0.29723), + 14361 => array ("Lénault", 48.92969, -0.62294), + 14362 => array ("Lessard-et-le-Chêne", 49.07091, 0.13127), + 14364 => array ("Lingèvres", 49.17326, -0.67450), + 14365 => array ("Lion-sur-Mer", 49.30076, -0.33395), + 14366 => array ("Lisieux", 49.14666, 0.23827), + 14367 => array ("Lison", 49.24161, -1.05227), + 14368 => array ("Lisores", 48.95634, 0.21197), + 14369 => array ("Litteau", 49.15307, -0.90989), + 14370 => array ("Le Molay-Littry", 49.23440, -0.88924), + 14371 => array ("Livarot", 49.00515, 0.16598), + 14372 => array ("Livry", 49.11348, -0.76837), + 14373 => array ("Le Locheur", 49.10523, -0.55608), + 14374 => array ("Les Loges", 49.04440, -0.80124), + 14375 => array ("Les Loges-Saulces", 48.86469, -0.30340), + 14376 => array ("Longraye", 49.15536, -0.70086), + 14377 => array ("Longues-sur-Mer", 49.33167, -0.69423), + 14378 => array ("Longueville", 49.33549, -0.95730), + 14379 => array ("Longvillers", 49.04646, -0.64415), + 14380 => array ("Loucelles", 49.22573, -0.57801), + 14381 => array ("Louvagny", 48.95014, -0.04656), + 14382 => array ("Louvières", 49.37164, -0.92252), + 14383 => array ("Louvigny", 49.15527, -0.39670), + 14384 => array ("Luc-sur-Mer", 49.30853, -0.35746), + 14385 => array ("Magny-en-Bessin", 49.31164, -0.66647), + 14386 => array ("Magny-la-Campagne", 49.04943, -0.09871), + 14387 => array ("Magny-le-Freule", 49.10378, -0.07454), + 14388 => array ("Maisoncelles-la-Jourdan", 48.79254, -0.84754), + 14389 => array ("Maisoncelles-Pelvey", 49.05928, -0.67263), + 14390 => array ("Maisoncelles-sur-Ajon", 49.05955, -0.53996), + 14391 => array ("Maisons", 49.31618, -0.75292), + 14393 => array ("Maizet", 49.07543, -0.45417), + 14394 => array ("Maizières", 49.01417, -0.16697), + 14395 => array ("Malloué", 48.94439, -0.95457), + 14396 => array ("Maltot", 49.13159, -0.42298), + 14397 => array ("Mandeville-en-Bessin", 49.30699, -0.86491), + 14398 => array ("Manerbe", 49.17840, 0.14388), + 14399 => array ("Manneville-la-Pipard", 49.26529, 0.22836), + 14400 => array ("Le Manoir", 49.28889, -0.59927), + 14401 => array ("Manvieux", 49.33772, -0.65866), + 14402 => array ("Le Marais-la-Chapelle", 48.88202, -0.02250), + 14403 => array ("Marolles", 49.13785, 0.36902), + 14404 => array ("Martainville", 48.94746, -0.35026), + 14405 => array ("Martigny-sur-l'Ante", 48.89027, -0.28704), + 14406 => array ("Martragny", 49.24863, -0.60137), + 14407 => array ("Mathieu", 49.25623, -0.36673), + 14408 => array ("May-sur-Orne", 49.09669, -0.37927), + 14409 => array ("Merville-Franceville-Plage", 49.27233, -0.20198), + 14410 => array ("Méry-Corbon", 49.13155, -0.07464), + 14411 => array ("Meslay", 48.95394, -0.39245), + 14412 => array ("Le Mesnil-au-Grain", 49.04679, -0.60612), + 14413 => array ("Le Mesnil-Auzouf", 48.97757, -0.73588), + 14414 => array ("Le Mesnil-Bacley", 48.99819, 0.12960), + 14415 => array ("Le Mesnil-Benoist", 48.86808, -0.98143), + 14416 => array ("Le Mesnil-Caussois", 48.85508, -1.01089), + 14417 => array ("Mesnil-Clinchamps", 48.84592, -0.98234), + 14418 => array ("Le Mesnil-Durand", 49.04568, 0.15226), + 14419 => array ("Le Mesnil-Eudes", 49.09312, 0.17472), + 14420 => array ("Le Mesnil-Germain", 49.04554, 0.18869), + 14421 => array ("Le Mesnil-Guillaume", 49.10488, 0.28256), + 14422 => array ("Le Mesnil-Mauger", 49.07679, 0.01088), + 14423 => array ("Le Mesnil-Patry", 49.19248, -0.54333), + 14424 => array ("Le Mesnil-Robert", 48.88010, -0.97291), + 14425 => array ("Le Mesnil-Simon", 49.08898, 0.11264), + 14426 => array ("Le Mesnil-sur-Blangy", 49.25952, 0.26107), + 14427 => array ("Le Mesnil-Villement", 48.85638, -0.37657), + 14429 => array ("Meulles", 48.97258, 0.31832), + 14430 => array ("Meuvaines", 49.32564, -0.57244), + 14431 => array ("Mézidon-Canon", 49.07844, -0.07402), + 14432 => array ("Missy", 49.12253, -0.54268), + 14433 => array ("Mittois", 49.00578, 0.03311), + 14435 => array ("Les Monceaux", 49.11071, 0.13023), + 14436 => array ("Monceaux-en-Bessin", 49.25033, -0.68932), + 14437 => array ("Mondeville", 49.16936, -0.31069), + 14438 => array ("Mondrainville", 49.14199, -0.51229), + 14439 => array ("Monfréville", 49.31207, -1.03506), + 14440 => array ("Montamy", 48.96961, -0.75674), + 14441 => array ("Mont-Bertrand", 48.97281, -0.91951), + 14442 => array ("Montchamp", 48.91514, -0.75481), + 14443 => array ("Montchauvet", 48.94685, -0.73504), + 14444 => array ("Monteille", 49.10677, 0.04421), + 14445 => array ("Montfiquet", 49.18506, -0.88886), + 14446 => array ("Montigny", 49.04082, -0.53445), + 14448 => array ("Montreuil-en-Auge", 49.16744, 0.07064), + 14449 => array ("Monts-en-Bessin", 49.12190, -0.61143), + 14450 => array ("Montviette", 48.99032, 0.09669), + 14452 => array ("Morteaux-Couliboeuf", 48.92398, -0.08138), + 14453 => array ("Mosles", 49.30545, -0.82052), + 14454 => array ("Mouen", 49.14984, -0.48840), + 14455 => array ("Moulines", 48.99211, -0.34745), + 14456 => array ("Moult", 49.11271, -0.16529), + 14457 => array ("Les Moutiers-en-Auge", 48.89835, 0.00771), + 14458 => array ("Les Moutiers-en-Cinglais", 49.02310, -0.43770), + 14459 => array ("Les Moutiers-Hubert", 48.97220, 0.25827), + 14460 => array ("Moyaux", 49.19500, 0.35123), + 14461 => array ("Mutrécy", 49.06437, -0.42248), + 14462 => array ("Neuilly-la-Forêt", 49.27144, -1.08918), + 14465 => array ("Nonant", 49.23684, -0.64772), + 14466 => array ("Norolles", 49.20324, 0.23652), + 14467 => array ("Noron-l'Abbaye", 48.89603, -0.24765), + 14468 => array ("Noron-la-Poterie", 49.22394, -0.77533), + 14469 => array ("Norrey-en-Auge", 48.91349, -0.01514), + 14471 => array ("Notre-Dame-de-Courson", 49.00087, 0.26837), + 14473 => array ("Notre-Dame-de-Livaye", 49.11788, 0.04128), + 14474 => array ("Notre-Dame-d'Estrées", 49.13545, -0.00148), + 14475 => array ("Noyers-Bocage", 49.12416, -0.57535), + 14476 => array ("Olendon", 48.96857, -0.17314), + 14477 => array ("Ondefontaine", 48.98840, -0.68757), + 14478 => array ("Orbec", 49.02366, 0.38495), + 14480 => array ("Osmanville", 49.33398, -1.08866), + 14481 => array ("Les Oubeaux", 49.28467, -1.05251), + 14482 => array ("Ouézy", 49.08527, -0.10396), + 14483 => array ("Ouffières", 49.02485, -0.48898), + 14484 => array ("Ouilly-du-Houley", 49.16547, 0.34044), + 14486 => array ("Ouilly-le-Tesson", 48.99090, -0.21312), + 14487 => array ("Ouilly-le-Vicomte", 49.17784, 0.21531), + 14488 => array ("Ouistreham", 49.27452, -0.25771), + 14489 => array ("Ouville-la-Bien-Tournée", 49.05249, -0.03024), + 14491 => array ("Parfouru-sur-Odon", 49.08898, -0.60882), + 14492 => array ("Pennedepie", 49.40165, 0.16404), + 14493 => array ("Percy-en-Auge", 49.05987, -0.06005), + 14494 => array ("Périers-en-Auge", 49.26456, -0.09414), + 14495 => array ("Périers-sur-le-Dan", 49.25729, -0.33742), + 14496 => array ("Périgny", 48.91889, -0.60505), + 14497 => array ("Perrières", 48.95970, -0.12580), + 14498 => array ("Pertheville-Ners", 48.87582, -0.10935), + 14499 => array ("Petiville", 49.24234, -0.16846), + 14500 => array ("Pierrefitte-en-Auge", 49.25491, 0.20027), + 14501 => array ("Pierrefitte-en-Cinglais", 48.90471, -0.38866), + 14502 => array ("Pierrepont", 48.88682, -0.32133), + 14503 => array ("Pierres", 48.86223, -0.73595), + 14504 => array ("Le Pin", 49.21392, 0.33560), + 14505 => array ("Placy", 48.97878, -0.42659), + 14506 => array ("Planquery", 49.16240, -0.82646), + 14508 => array ("Le Plessis-Grimoult", 48.96061, -0.61166), + 14509 => array ("Plumetot", 49.27755, -0.36146), + 14510 => array ("La Pommeraye", 48.90940, -0.41548), + 14511 => array ("Pont-Bellanger", 48.93515, -0.98280), + 14512 => array ("Pontécoulant", 48.89187, -0.58373), + 14513 => array ("Pont-Farcy", 48.94369, -1.03671), + 14514 => array ("Pont-l'évêque", 49.28056, 0.18766), + 14515 => array ("Port-en-Bessin-Huppain", 49.33960, -0.77270), + 14516 => array ("Potigny", 48.97063, -0.24735), + 14517 => array ("Poussy-la-Campagne", 49.07889, -0.23113), + 14518 => array ("Préaux-Saint-Sébastien", 48.99326, 0.30701), + 14519 => array ("Préaux-Bocage", 49.05263, -0.50920), + 14520 => array ("Le Pré-d'Auge", 49.14965, 0.14575), + 14521 => array ("Presles", 48.87951, -0.77427), + 14522 => array ("Prêtreville", 49.07221, 0.25860), + 14523 => array ("Proussy", 48.87953, -0.55285), + 14524 => array ("Putot-en-Auge", 49.21310, -0.06642), + 14525 => array ("Putot-en-Bessin", 49.21388, -0.54402), + 14527 => array ("Biéville-Quétiéville", 49.11612, -0.03570), + 14528 => array ("Quetteville", 49.34199, 0.30013), + 14529 => array ("Ranchy", 49.25527, -0.76371), + 14530 => array ("Ranville", 49.22882, -0.26353), + 14531 => array ("Rapilly", 48.85697, -0.34380), + 14532 => array ("Le Reculey", 48.90815, -0.84152), + 14533 => array ("Repentigny", 49.19900, 0.04268), + 14534 => array ("Reux", 49.27648, 0.15345), + 14535 => array ("Reviers", 49.29846, -0.46569), + 14536 => array ("La Rivière-Saint-Sauveur", 49.40803, 0.26800), + 14538 => array ("Rocquancourt", 49.09575, -0.31725), + 14539 => array ("La Rocque", 48.88673, -0.67454), + 14540 => array ("Rocques", 49.16869, 0.24941), + 14541 => array ("La Roque-Baignard", 49.17791, 0.09158), + 14542 => array ("Rosel", 49.22165, -0.45002), + 14543 => array ("Rots", 49.20605, -0.47472), + 14544 => array ("Roucamps", 48.98662, -0.63142), + 14545 => array ("Roullours", 48.82676, -0.83696), + 14546 => array ("Rouvres", 49.00365, -0.18670), + 14547 => array ("Rubercy", 49.28529, -0.88632), + 14548 => array ("Rucqueville", 49.25805, -0.58610), + 14549 => array ("Rully", 48.82851, -0.71535), + 14550 => array ("Rumesnil", 49.18620, 0.02207), + 14551 => array ("Russy", 49.32815, -0.81895), + 14552 => array ("Ryes", 49.31389, -0.62790), + 14553 => array ("Saint-Agnan-le-Malherbe", 49.04023, -0.58191), + 14554 => array ("Saint-Aignan-de-Cramesnil", 49.08161, -0.28602), + 14555 => array ("Saint-André-d'Hébertot", 49.30322, 0.29191), + 14556 => array ("Saint-André-sur-Orne", 49.12440, -0.38840), + 14557 => array ("Saint-Arnoult", 49.33163, 0.08957), + 14558 => array ("Saint-Aubin-d'Arquenay", 49.26100, -0.28386), + 14559 => array ("Saint-Aubin-des-Bois", 48.83240, -1.13116), + 14562 => array ("Saint-Aubin-sur-Mer", 49.32243, -0.39282), + 14563 => array ("Saint-Benoît-d'Hébertot", 49.32499, 0.27275), + 14564 => array ("Saint-Charles-de-Percy", 48.91904, -0.79113), + 14565 => array ("Saint-Côme-de-Fresné", 49.32653, -0.60221), + 14566 => array ("Saint-Contest", 49.21545, -0.40091), + 14568 => array ("Sainte-Croix-Grand-Tonne", 49.23370, -0.55603), + 14569 => array ("Sainte-Croix-sur-Mer", 49.31601, -0.51000), + 14570 => array ("Saint-Cyr-du-Ronceray", 49.05083, 0.30184), + 14571 => array ("Saint-Denis-de-Mailloc", 49.09827, 0.31413), + 14572 => array ("Saint-Denis-de-Méré", 48.86481, -0.50313), + 14573 => array ("Saint-Denis-Maisoncelles", 48.97909, -0.86480), + 14574 => array ("Saint-Désir", 49.14415, 0.19072), + 14575 => array ("Saint-étienne-la-Thillaye", 49.29169, 0.11782), + 14576 => array ("Sainte-Foy-de-Montgommery", 48.95870, 0.16363), + 14577 => array ("Saint-Gabriel-Brécy", 49.27078, -0.57093), + 14578 => array ("Saint-Gatien-des-Bois", 49.35086, 0.18684), + 14579 => array ("Saint-Georges-d'Aunay", 49.02677, -0.68862), + 14580 => array ("Saint-Georges-en-Auge", 48.98846, 0.07087), + 14581 => array ("Saint-Germain-d'Ectot", 49.11924, -0.71556), + 14582 => array ("Saint-Germain-de-Livet", 49.07682, 0.19753), + 14583 => array ("Saint-Germain-de-Montgommery", 48.94084, 0.16621), + 14584 => array ("Saint-Germain-de-Tallevende-la-Lande-Vaumont", 48.79373, -0.90633), + 14585 => array ("Saint-Germain-du-Crioult", 48.85097, -0.60914), + 14586 => array ("Saint-Germain-du-Pert", 49.33030, -1.04253), + 14587 => array ("Saint-Germain-la-Blanche-Herbe", 49.19559, -0.42159), + 14588 => array ("Saint-Germain-Langot", 48.92232, -0.32729), + 14589 => array ("Saint-Germain-le-Vasson", 48.99994, -0.30142), + 14590 => array ("Sainte-Honorine-de-Ducy", 49.13730, -0.78551), + 14591 => array ("Sainte-Honorine-des-Pertes", 49.34678, -0.81043), + 14592 => array ("Sainte-Honorine-du-Fay", 49.07006, -0.49109), + 14593 => array ("Saint-Hymer", 49.24329, 0.16739), + 14595 => array ("Saint-Jean-de-Livet", 49.09338, 0.24234), + 14596 => array ("Saint-Jean-des-Essartiers", 49.04820, -0.81861), + 14597 => array ("Saint-Jean-le-Blanc", 48.93915, -0.65777), + 14598 => array ("Saint-Jouin", 49.21755, -0.01297), + 14599 => array ("Saint-Julien-de-Mailloc", 49.08186, 0.33576), + 14600 => array ("Saint-Julien-le-Faucon", 49.06710, 0.08127), + 14601 => array ("Saint-Julien-sur-Calonne", 49.28755, 0.23051), + 14602 => array ("Saint-Lambert", 48.93531, -0.53799), + 14603 => array ("Saint-Laurent-de-Condel", 49.02613, -0.39482), + 14604 => array ("Saint-Laurent-du-Mont", 49.14043, 0.02725), + 14605 => array ("Saint-Laurent-sur-Mer", 49.35770, -0.87601), + 14606 => array ("Saint-Léger-Dubosq", 49.23020, -0.01475), + 14607 => array ("Saint-Louet-sur-Seulles", 49.09574, -0.67799), + 14608 => array ("Saint-Loup-de-Fribois", 49.10873, 0.01021), + 14609 => array ("Saint-Loup-Hors", 49.26020, -0.73045), + 14610 => array ("Saint-Manvieu-Norrey", 49.18661, -0.50064), + 14611 => array ("Saint-Manvieu-Bocage", 48.82168, -0.96491), + 14613 => array ("Saint-Marcouf", 49.25710, -1.00212), + 14614 => array ("Sainte-Marguerite-d'Elle", 49.21574, -0.98686), + 14615 => array ("Sainte-Marguerite-des-Loges", 49.01607, 0.19951), + 14616 => array ("Sainte-Marguerite-de-Viette", 49.01199, 0.08813), + 14618 => array ("Sainte-Marie-Laumont", 48.92017, -0.90876), + 14619 => array ("Sainte-Marie-Outre-l'Eau", 48.93016, -1.02129), + 14620 => array ("Saint-Martin-aux-Chartrains", 49.30841, 0.15439), + 14621 => array ("Saint-Martin-de-Bienfaite-la-Cressonnière", 49.03289, 0.35277), + 14622 => array ("Saint-Martin-de-Blagny", 49.24912, -0.94294), + 14623 => array ("Saint-Martin-de-Fontenay", 49.11461, -0.35179), + 14625 => array ("Saint-Martin-de-la-Lieue", 49.11027, 0.22439), + 14626 => array ("Saint-Martin-de-Mailloc", 49.08840, 0.28644), + 14627 => array ("Saint-Martin-de-Mieux", 48.87351, -0.24062), + 14628 => array ("Saint-Martin-de-Sallen", 48.97512, -0.52075), + 14629 => array ("Saint-Martin-des-Besaces", 49.00877, -0.84369), + 14630 => array ("Saint-Martin-des-Entrées", 49.25745, -0.66494), + 14632 => array ("Saint-Martin-Don", 48.92558, -0.94649), + 14633 => array ("Saint-Martin-du-Mesnil-Oury", 49.03171, 0.12416), + 14634 => array ("Saint-Michel-de-Livet", 49.01559, 0.13205), + 14635 => array ("Saint-Omer", 48.92835, -0.44369), + 14636 => array ("Saint-Ouen-des-Besaces", 49.02191, -0.85043), + 14637 => array ("Saint-Ouen-du-Mesnil-Oger", 49.15758, -0.11526), + 14638 => array ("Saint-Ouen-le-Houx", 48.98159, 0.19273), + 14639 => array ("Saint-Ouen-le-Pin", 49.15083, 0.10365), + 14640 => array ("Saint-Pair", 49.16553, -0.18671), + 14643 => array ("Saint-Paul-du-Vernay", 49.19676, -0.75663), + 14644 => array ("Saint-Philbert-des-Champs", 49.21195, 0.27554), + 14645 => array ("Saint-Pierre-Azif", 49.29277, 0.03725), + 14646 => array ("Saint-Pierre-Canivet", 48.92112, -0.23001), + 14647 => array ("Saint-Pierre-de-Mailloc", 49.06944, 0.30910), + 14648 => array ("Saint-Pierre-des-Ifs", 49.11461, 0.16187), + 14649 => array ("Saint-Pierre-du-Bû", 48.86460, -0.20418), + 14650 => array ("Saint-Pierre-du-Fresne", 49.03412, -0.76592), + 14651 => array ("Saint-Pierre-du-Jonquet", 49.16581, -0.13947), + 14652 => array ("Saint-Pierre-du-Mont", 49.38112, -0.97519), + 14653 => array ("Saint-Pierre-la-Vieille", 48.92041, -0.58071), + 14654 => array ("Saint-Pierre-sur-Dives", 49.01585, -0.04077), + 14655 => array ("Saint-Pierre-Tarentaine", 48.96152, -0.78600), + 14656 => array ("Saint-Rémy", 48.93986, -0.49006), + 14657 => array ("Saint-Samson", 49.19049, -0.15245), + 14658 => array ("Saint-Sever-Calvados", 48.82175, -1.04661), + 14659 => array ("Saint-Sylvain", 49.05315, -0.21559), + 14660 => array ("Saint-Vaast-en-Auge", 49.29058, 0.00230), + 14661 => array ("Saint-Vaast-sur-Seulles", 49.13619, -0.63017), + 14662 => array ("Saint-Vigor-des-Mézerets", 48.90336, -0.63343), + 14663 => array ("Saint-Vigor-le-Grand", 49.28771, -0.67685), + 14664 => array ("Sallen", 49.11694, -0.83988), + 14665 => array ("Sallenelles", 49.26370, -0.23342), + 14666 => array ("Sannerville", 49.18393, -0.22288), + 14667 => array ("Saon", 49.27137, -0.86300), + 14668 => array ("Saonnet", 49.27219, -0.88098), + 14669 => array ("Sassy", 48.98570, -0.13927), + 14670 => array ("Secqueville-en-Bessin", 49.23445, -0.51111), + 14671 => array ("Sept-Frères", 48.86420, -1.04020), + 14672 => array ("Sept-Vents", 49.06627, -0.83777), + 14674 => array ("Soignolles", 49.02935, -0.21661), + 14675 => array ("Soliers", 49.13152, -0.28762), + 14676 => array ("Sommervieu", 49.29263, -0.64587), + 14677 => array ("Soulangy", 48.94048, -0.21399), + 14678 => array ("Soumont-Saint-Quentin", 48.97923, -0.23472), + 14679 => array ("Subles", 49.23919, -0.74992), + 14680 => array ("Sully", 49.30039, -0.74173), + 14681 => array ("Surrain", 49.32844, -0.85485), + 14682 => array ("Surville", 49.30289, 0.22336), + 14684 => array ("Tessel", 49.15095, -0.56804), + 14685 => array ("Thaon", 49.25496, -0.44917), + 14686 => array ("Le Theil-Bocage", 48.88211, -0.70909), + 14687 => array ("Le Theil-en-Auge", 49.34681, 0.25098), + 14688 => array ("Thiéville", 49.03671, -0.04423), + 14689 => array ("Thury-Harcourt", 48.98645, -0.48510), + 14690 => array ("Tierceville", 49.29845, -0.52740), + 14691 => array ("Tilly-la-Campagne", 49.11027, -0.31076), + 14692 => array ("Tilly-sur-Seulles", 49.17186, -0.62341), + 14693 => array ("Tordouet", 49.04265, 0.32192), + 14694 => array ("Le Torquesne", 49.21812, 0.16934), + 14695 => array ("Torteval-Quesnay", 49.14981, -0.73003), + 14696 => array ("Tortisambert", 48.96418, 0.11493), + 14697 => array ("L'Oudon", 48.96261, 0.03294), + 14698 => array ("Touffréville", 49.19936, -0.22502), + 14699 => array ("Touques", 49.35360, 0.11266), + 14700 => array ("Tour-en-Bessin", 49.29391, -0.78376), + 14701 => array ("Tourgéville", 49.31790, 0.07981), + 14702 => array ("Tournay-sur-Odon", 49.09612, -0.58292), + 14703 => array ("Tournebu", 48.96333, -0.32629), + 14704 => array ("Le Tourneur", 48.97128, -0.83205), + 14705 => array ("Tournières", 49.23046, -0.93785), + 14706 => array ("Tourville-en-Auge", 49.31972, 0.19513), + 14707 => array ("Tourville-sur-Odon", 49.13706, -0.50068), + 14708 => array ("Tracy-Bocage", 49.07263, -0.68942), + 14709 => array ("Tracy-sur-Mer", 49.33116, -0.64463), + 14710 => array ("Tréprel", 48.89860, -0.34680), + 14711 => array ("Trévières", 49.29923, -0.91609), + 14712 => array ("Troarn", 49.19153, -0.18368), + 14713 => array ("Trois-Monts", 49.04884, -0.47903), + 14714 => array ("Le Tronquay", 49.22711, -0.81979), + 14715 => array ("Trouville-sur-Mer", 49.37212, 0.10212), + 14716 => array ("Trungy", 49.19368, -0.72239), + 14717 => array ("Truttemer-le-Grand", 48.79867, -0.80630), + 14718 => array ("Truttemer-le-Petit", 48.76934, -0.80978), + 14719 => array ("Urville", 49.02515, -0.29441), + 14720 => array ("Ussy", 48.94606, -0.29000), + 14721 => array ("Vacognes-Neuilly", 49.08543, -0.53423), + 14722 => array ("La Vacquerie", 49.09208, -0.85571), + 14723 => array ("Valsemé", 49.23769, 0.09920), + 14724 => array ("Varaville", 49.26315, -0.14592), + 14726 => array ("Vassy", 48.86360, -0.65830), + 14727 => array ("Vaubadon", 49.20375, -0.83614), + 14728 => array ("Vaucelles", 49.28389, -0.73545), + 14729 => array ("Vaudeloges", 48.94635, 0.00065), + 14730 => array ("Vaudry", 48.85396, -0.84423), + 14731 => array ("Vauville", 49.30899, 0.05853), + 14732 => array ("Vaux-sur-Aure", 49.30706, -0.71005), + 14733 => array ("Vaux-sur-Seulles", 49.26155, -0.62765), + 14734 => array ("Vendes", 49.14202, -0.60098), + 14735 => array ("Vendeuvre", 49.00675, -0.08289), + 14737 => array ("Versainville", 48.91977, -0.17152), + 14738 => array ("Verson", 49.16376, -0.46330), + 14739 => array ("Ver-sur-Mer", 49.33385, -0.52881), + 14740 => array ("La Vespière", 49.01119, 0.42474), + 14741 => array ("Le Vey", 48.91660, -0.46191), + 14742 => array ("Vicques", 48.95316, -0.07424), + 14743 => array ("Victot-Pontfol", 49.16802, -0.00948), + 14744 => array ("Vienne-en-Bessin", 49.28120, -0.62776), + 14745 => array ("Vierville-sur-Mer", 49.36698, -0.90154), + 14746 => array ("Viessoix", 48.83293, -0.79018), + 14747 => array ("Vieux", 49.11281, -0.44294), + 14748 => array ("Vieux-Bourg", 49.31284, 0.24545), + 14749 => array ("Vieux-Fumé", 49.06111, -0.12942), + 14750 => array ("Vieux-Pont-en-Auge", 49.04229, 0.05297), + 14751 => array ("Vignats", 48.85400, -0.10240), + 14752 => array ("Villers-Bocage", 49.07824, -0.65266), + 14753 => array ("Villers-Canivet", 48.93360, -0.26131), + 14754 => array ("Villers-sur-Mer", 49.31107, 0.00599), + 14755 => array ("Villerville", 49.39087, 0.12314), + 14756 => array ("La Villette", 48.90904, -0.54320), + 14757 => array ("Villiers-le-Sec", 49.29294, -0.56561), + 14758 => array ("Villons-les-Buissons", 49.23619, -0.41126), + 14759 => array ("Villy-lez-Falaise", 48.90033, -0.13614), + 14760 => array ("Villy-Bocage", 49.10392, -0.64197), + 14761 => array ("Vimont", 49.14162, -0.19470), + 14762 => array ("Vire", 48.85124, -0.88960), + 14763 => array ("Vouilly", 49.29589, -1.03229), + 14764 => array ("Pont-d'Ouilly", 48.87686, -0.40903), + 22001 => array ("Allineuc", 48.31083, -2.8725), + 22002 => array ("Andel", 48.49, -2.56778), + 22003 => array ("Aucaleuc", 48.45611, -2.12972), + 22004 => array ("Bégard", 48.62806, -3.30083), + 22005 => array ("Belle-Isle-en-Terre", 48.54472, -3.39444), + 22006 => array ("Berhet", 48.69639, -3.30417), + 22007 => array ("Binic", 48.60194, -2.82528), + 22008 => array ("Bobital", 48.41333, -2.10306), + 22009 => array ("Le Bodéo", 48.32222, -2.93333), + 22011 => array ("Boqueho", 48.48306, -2.96111), + 22012 => array ("La Bouillie", 48.57389, -2.43528), + 22013 => array ("Bourbriac", 48.4731, -3.1879), + 22014 => array ("Bourseul", 48.48722, -2.25972), + 22015 => array ("Bréhand", 48.40278, -2.57417), + 22016 => array ("Île-de-Bréhat", 48.8466, -2.9989), + 22018 => array ("Brélidy", 48.65944, -3.21806), + 22019 => array ("Bringolo", 48.57611, -3.0025), + 22020 => array ("Broons", 48.31722, -2.26083), + 22021 => array ("Brusvily", 48.39083, -2.12722), + 22023 => array ("Bulat-Pestivien", 48.42861, -3.33056), + 22024 => array ("Calanhel", 48.43598, -3.47975), + 22025 => array ("Callac", 48.40417, -3.42778), + 22026 => array ("Calorguen", 48.4101, -2.0278), + 22027 => array ("Le Cambout", 48.05889, -2.61056), + 22028 => array ("Camlez", 48.77778, -3.30472), + 22029 => array ("Canihuel", 48.33806, -3.10528), + 22030 => array ("Caouënnec-Lanvézéac", 48.7035, -3.3751), + 22031 => array ("Carnoët", 48.36778, -3.52139), + 22032 => array ("Caulnes", 48.28889, -2.15472), + 22033 => array ("Caurel", 48.21611, -3.03833), + 22034 => array ("Cavan", 48.67194, -3.34472), + 22035 => array ("Les Champs-Géraux", 48.41667, -1.97056), + 22036 => array ("La Chapelle-Blanche", 48.26583, -2.14472), + 22037 => array ("La Chapelle-Neuve", 48.4623, -3.4201), + 22038 => array ("Châtelaudren", 48.54222, -2.97111), + 22039 => array ("La Chèze", 48.13139, -2.65611), + 22040 => array ("Coadout", 48.51722, -3.18806), + 22041 => array ("Coatascorn", 48.67389, -3.25028), + 22042 => array ("Coatréven", 48.76833, -3.34278), + 22043 => array ("Coëtlogon", 48.14167, -2.54417), + 22044 => array ("Coëtmieux", 48.49194, -2.60056), + 22045 => array ("Cohiniac", 48.46139, -2.94861), + 22046 => array ("Collinée", 48.30083, -2.52), + 22047 => array ("Corlay", 48.31694, -3.05722), + 22048 => array ("Corseul", 48.48194, -2.17), + 22049 => array ("Créhen", 48.54556, -2.21306), + 22050 => array ("Dinan", 48.45556, -2.05028), + 22051 => array ("Dolo", 48.38389, -2.32944), + 22052 => array ("Duault", 48.36111, -3.43528), + 22053 => array ("Éréac", 48.27389, -2.3475), + 22054 => array ("Erquy", 48.63167, -2.46417), + 22055 => array ("Étables-sur-Mer", 48.6261, -2.8342), + 22056 => array ("Évran", 48.3825, -1.98083), + 22057 => array ("Le Faouët", 48.6828, -3.0727), + 22058 => array ("La Ferrière", 48.14194, -2.60667), + 22059 => array ("Le Fœil", 48.42861, -2.915), + 22060 => array ("Gausson", 48.29944, -2.75472), + 22061 => array ("Glomel", 48.22278, -3.39639), + 22062 => array ("Gomené", 48.17389, -2.48722), + 22063 => array ("Gommenec'h", 48.6396, -3.0484), + 22064 => array ("Gouarec", 48.22667, -3.18083), + 22065 => array ("Goudelin", 48.60306, -3.01722), + 22066 => array ("Le Gouray", 48.32722, -2.48861), + 22067 => array ("Grâces", 48.5559, -3.1843), + 22068 => array ("Grâce-Uzel", 48.24139, -2.80083), + 22069 => array ("Guenroc", 48.31722, -2.075), + 22070 => array ("Guingamp", 48.5622, -3.1501), + 22071 => array ("Guitté", 48.29639, -2.09444), + 22072 => array ("Gurunhuel", 48.51667, -3.3), + 22073 => array ("La Harmoye", 48.33556, -2.96139), + 22074 => array ("Le Haut-Corlay", 48.32139, -3.05694), + 22075 => array ("Hémonstoir", 48.15861, -2.83083), + 22076 => array ("Hénanbihen", 48.56056, -2.37694), + 22077 => array ("Hénansal", 48.54167, -2.43472), + 22078 => array ("Hengoat", 48.74306, -3.19833), + 22079 => array ("Hénon", 48.38472, -2.68361), + 22080 => array ("L'Hermitage-Lorge", 48.33167, -2.82889), + 22081 => array ("Hillion", 48.51361, -2.66889), + 22082 => array ("Le Hinglé", 48.39278, -2.07833), + 22083 => array ("Illifaut", 48.14611, -2.34833), + 22084 => array ("Jugon-les-Lacs", 48.4092, -2.3212), + 22085 => array ("Kerbors", 48.82778, -3.18306), + 22086 => array ("Kerfot", 48.73639, -3.03), + 22087 => array ("Kergrist-Moëlou", 48.30944, -3.3175), + 22088 => array ("Kerien", 48.39, -3.21222), + 22090 => array ("Kermaria-Sulard", 48.77167, -3.37083), + 22091 => array ("Kermoroc'h", 48.62167, -3.20667), + 22092 => array ("Kerpert", 48.37639, -3.13389), + 22093 => array ("Lamballe", 48.46861, -2.51778), + 22094 => array ("Lancieux", 48.60778, -2.15), + 22095 => array ("Landebaëron", 48.63444, -3.20917), + 22096 => array ("Landébia", 48.51417, -2.33639), + 22097 => array ("La Landec", 48.43556, -2.17972), + 22098 => array ("Landéhen", 48.42833, -2.54083), + 22099 => array ("Lanfains", 48.35306, -2.91417), + 22100 => array ("Langast", 48.28, -2.66389), + 22101 => array ("Langoat", 48.75056, -3.28083), + 22102 => array ("Langourla", 48.285, -2.41583), + 22103 => array ("Langrolay-sur-Rance", 48.5541, -2.0017), + 22104 => array ("Languédias", 48.38806, -2.21306), + 22105 => array ("Languenan", 48.51056, -2.12722), + 22106 => array ("Langueux", 48.495, -2.7175), + 22107 => array ("Laniscat", 48.24234, -3.12372), + 22108 => array ("Lanleff", 48.69278, -3.04444), + 22109 => array ("Lanloup", 48.71278, -2.96556), + 22110 => array ("Lanmérin", 48.74139, -3.34972), + 22111 => array ("Lanmodez", 48.84111, -3.10583), + 22112 => array ("Lannebert", 48.65806, -3.00917), + 22113 => array ("Lannion", 48.7325, -3.45528), + 22114 => array ("Lanrelas", 48.25194, -2.29389), + 22115 => array ("Lanrivain", 48.34694, -3.21417), + 22116 => array ("Lanrodec", 48.51611, -3.03083), + 22117 => array ("Lantic", 48.59889, -2.89806), + 22118 => array ("Lanvallay", 48.45528, -2.02833), + 22119 => array ("Lanvellec", 48.61889, -3.53611), + 22121 => array ("Lanvollon", 48.63, -2.985), + 22122 => array ("Laurenan", 48.19917, -2.53556), + 22123 => array ("Léhon", 48.44194, -2.03944), + 22124 => array ("Lescouët-Gouarec", 48.16, -3.24472), + 22126 => array ("Le Leslay", 48.42944, -2.96583), + 22127 => array ("Lézardrieux", 48.7854, -3.106), + 22128 => array ("Locarn", 48.31972, -3.42222), + 22129 => array ("Loc-Envel", 48.51639, -3.40917), + 22131 => array ("Loguivy-Plougras", 48.52194, -3.48778), + 22132 => array ("Lohuec", 48.46028, -3.52167), + 22133 => array ("Loscouët-sur-Meu", 48.17778, -2.24194), + 22134 => array ("Louannec", 48.79389, -3.41056), + 22135 => array ("Louargat", 48.56556, -3.33889), + 22136 => array ("Loudéac", 48.1779, -2.7523), + 22137 => array ("Maël-Carhaix", 48.28333, -3.42444), + 22138 => array ("Maël-Pestivien", 48.3948, -3.2959), + 22139 => array ("Magoar", 48.3966, -3.1861), + 22140 => array ("La Malhoure", 48.39722, -2.49444), + 22141 => array ("Mantallot", 48.70722, -3.29889), + 22143 => array ("Matignon", 48.59583, -2.29167), + 22144 => array ("La Méaugon", 48.49861, -2.83833), + 22145 => array ("Mégrit", 48.37472, -2.24861), + 22146 => array ("Mellionnec", 48.17472, -3.29639), + 22147 => array ("Merdrignac", 48.1925, -2.41444), + 22148 => array ("Mérillac", 48.25583, -2.39417), + 22149 => array ("Merléac", 48.27722, -2.89889), + 22150 => array ("Le Merzer", 48.57583, -3.06833), + 22151 => array ("Meslin", 48.44472, -2.56778), + 22152 => array ("Minihy-Tréguier", 48.77528, -3.22806), + 22153 => array ("Moncontour", 48.35917, -2.63306), + 22154 => array ("Morieux", 48.52167, -2.60889), + 22155 => array ("La Motte", 48.235, -2.73222), + 22156 => array ("Moustéru", 48.51722, -3.23889), + 22157 => array ("Le Moustoir", 48.26556, -3.50222), + 22158 => array ("Mûr-de-Bretagne", 48.20028, -2.98611), + 22160 => array ("Noyal", 48.4475, -2.48722), + 22161 => array ("Pabu", 48.5875, -3.13611), + 22162 => array ("Paimpol", 48.7782, -3.0457), + 22163 => array ("Paule", 48.23611, -3.44528), + 22164 => array ("Pédernec", 48.59694, -3.27), + 22165 => array ("Penguily", 48.3723, -2.494), + 22166 => array ("Penvénan", 48.8115, -3.2941), + 22167 => array ("Perret", 48.17639, -3.15972), + 22168 => array ("Perros-Guirec", 48.81333, -3.44333), + 22169 => array ("Peumerit-Quintin", 48.36083, -3.27278), + 22170 => array ("Plaine-Haute", 48.44528, -2.85361), + 22171 => array ("Plaintel", 48.40806, -2.81833), + 22172 => array ("Plancoët", 48.52306, -2.23417), + 22173 => array ("Planguenoual", 48.53278, -2.57778), + 22174 => array ("Pléboulle", 48.60861, -2.33833), + 22175 => array ("Plédéliac", 48.44917, -2.38778), + 22176 => array ("Plédran", 48.44583, -2.74611), + 22177 => array ("Pléguien", 48.63472, -2.94), + 22178 => array ("Pléhédel", 48.69444, -3.00833), + 22179 => array ("Fréhel", 48.6279, -2.3653), + 22180 => array ("Plélan-le-Petit", 48.43417, -2.22), + 22181 => array ("Plélauff", 48.20639, -3.20972), + 22182 => array ("Plélo", 48.55639, -2.94667), + 22183 => array ("Plémet", 48.17694, -2.595), + 22184 => array ("Plémy", 48.33639, -2.68333), + 22185 => array ("Plénée-Jugon", 48.36417, -2.40056), + 22186 => array ("Pléneuf-Val-André", 48.59083, -2.54806), + 22187 => array ("Plérin", 48.53444, -2.77083), + 22188 => array ("Plerneuf", 48.51472, -2.885), + 22189 => array ("Plésidy", 48.4475, -3.12167), + 22190 => array ("Pleslin-Trigavou", 48.5352, -2.0539), + 22191 => array ("Plessala", 48.27583, -2.61944), + 22192 => array ("Plessix-Balisson", 48.5375, -2.1425), + 22193 => array ("Plestan", 48.42389, -2.44694), + 22194 => array ("Plestin-les-Grèves", 48.65694, -3.63111), + 22195 => array ("Pleubian", 48.84167, -3.13972), + 22196 => array ("Pleudaniel", 48.76611, -3.14444), + 22197 => array ("Pleudihen-sur-Rance", 48.5109, -1.9513), + 22198 => array ("Pleumeur-Bodou", 48.77306, -3.51778), + 22199 => array ("Pleumeur-Gautier", 48.80222, -3.15722), + 22200 => array ("Pléven", 48.49, -2.32028), + 22201 => array ("Plévenon", 48.65417, -2.33139), + 22202 => array ("Plévin", 48.22639, -3.50528), + 22203 => array ("Plœuc-sur-Lié", 48.3462, -2.7568), + 22204 => array ("Ploëzal", 48.71639, -3.20278), + 22205 => array ("Plorec-sur-Arguenon", 48.47972, -2.29722), + 22206 => array ("Plouagat", 48.53667, -2.99889), + 22207 => array ("Plouaret", 48.61194, -3.4725), + 22208 => array ("Plouasne", 48.30111, -2.00722), + 22209 => array ("Ploubalay", 48.58028, -2.14028), + 22210 => array ("Ploubazlanec", 48.8, -3.03333), + 22211 => array ("Ploubezre", 48.70444, -3.44889), + 22212 => array ("Plouëc-du-Trieux", 48.6738, -3.1906), + 22213 => array ("Plouër-sur-Rance", 48.52778, -2.00333), + 22214 => array ("Plouézec", 48.74833, -2.985), + 22215 => array ("Ploufragan", 48.48944, -2.79583), + 22216 => array ("Plougonver", 48.48472, -3.37861), + 22217 => array ("Plougras", 48.51083, -3.56139), + 22218 => array ("Plougrescant", 48.84028, -3.22861), + 22219 => array ("Plouguenast", 48.28139, -2.70444), + 22220 => array ("Plouguernével", 48.24028, -3.25611), + 22221 => array ("Plouguiel", 48.79722, -3.24083), + 22222 => array ("Plouha", 48.67556, -2.92944), + 22223 => array ("Plouisy", 48.57778, -3.18389), + 22224 => array ("Ploulec'h", 48.7179, -3.5038), + 22225 => array ("Ploumagoar", 48.54528, -3.1325), + 22226 => array ("Ploumilliau", 48.68, -3.52389), + 22227 => array ("Plounérin", 48.56667, -3.54111), + 22228 => array ("Plounévez-Moëdec", 48.55639, -3.44472), + 22229 => array ("Plounévez-Quintin", 48.29, -3.23167), + 22231 => array ("Plourac'h", 48.41611, -3.54722), + 22232 => array ("Plourhan", 48.6306, -2.8711), + 22233 => array ("Plourivo", 48.74389, -3.07278), + 22234 => array ("Plouvara", 48.50694, -2.91528), + 22235 => array ("Plouzélambre", 48.6425, -3.54194), + 22236 => array ("Pludual", 48.66444, -2.98417), + 22237 => array ("Pluduno", 48.53083, -2.26806), + 22238 => array ("Plufur", 48.60778, -3.57389), + 22239 => array ("Plumaudan", 48.35778, -2.12472), + 22240 => array ("Plumaugat", 48.255, -2.23889), + 22241 => array ("Plumieux", 48.10306, -2.58417), + 22242 => array ("Plurien", 48.62583, -2.40417), + 22243 => array ("Plusquellec", 48.38583, -3.48528), + 22244 => array ("Plussulien", 48.28278, -3.07056), + 22245 => array ("Pluzunet", 48.64139, -3.36972), + 22246 => array ("Pommeret", 48.4625, -2.62639), + 22247 => array ("Pommerit-Jaudy", 48.73194, -3.24222), + 22248 => array ("Pommerit-le-Vicomte", 48.61917, -3.08833), + 22249 => array ("Pont-Melvez", 48.4605, -3.3049), + 22250 => array ("Pontrieux", 48.69833, -3.15944), + 22251 => array ("Pordic", 48.5703, -2.8171), + 22253 => array ("Pouldouran", 48.76444, -3.19917), + 22254 => array ("Prat", 48.67694, -3.2975), + 22255 => array ("La Prénessaye", 48.18306, -2.63389), + 22256 => array ("Quemper-Guézennec", 48.70139, -3.10556), + 22257 => array ("Quemperven", 48.725, -3.32194), + 22258 => array ("Quessoy", 48.42111, -2.65833), + 22259 => array ("Quévert", 48.46361, -2.08722), + 22260 => array ("Le Quillio", 48.24111, -2.88333), + 22261 => array ("Quintenic", 48.51528, -2.42833), + 22262 => array ("Quintin", 48.40361, -2.90944), + 22263 => array ("Le Quiou", 48.35111, -2.00611), + 22264 => array ("La Roche-Derrien", 48.74639, -3.26), + 22265 => array ("Rospez", 48.72944, -3.38389), + 22266 => array ("Rostrenen", 48.23639, -3.31694), + 22267 => array ("Rouillac", 48.30944, -2.36611), + 22268 => array ("Ruca", 48.56722, -2.33944), + 22269 => array ("Runan", 48.69333, -3.21167), + 22271 => array ("Saint-Adrien", 48.48917, -3.13028), + 22272 => array ("Saint-Agathon", 48.55917, -3.10472), + 22273 => array ("Saint-Alban", 48.55722, -2.535), + 22274 => array ("Saint-André-des-Eaux", 48.37167, -2.01), + 22275 => array ("Saint-Barnabé", 48.1375, -2.70278), + 22276 => array ("Saint-Bihy", 48.37889, -2.97056), + 22277 => array ("Saint-Brandan", 48.38944, -2.87), + 22278 => array ("Saint-Brieuc", 48.51361, -2.76028), + 22279 => array ("Saint-Caradec", 48.19083, -2.84917), + 22280 => array ("Saint-Carné", 48.41583, -2.06556), + 22281 => array ("Saint-Carreuc", 48.39833, -2.73139), + 22282 => array ("Saint-Cast-le-Guildo", 48.62944, -2.25778), + 22283 => array ("Saint-Clet", 48.6625, -3.1325), + 22284 => array ("Saint-Connan", 48.41806, -3.06389), + 22285 => array ("Saint-Connec", 48.1775, -2.92139), + 22286 => array ("Saint-Denoual", 48.52722, -2.40056), + 22287 => array ("Saint-Donan", 48.46972, -2.88528), + 22288 => array ("Saint-Étienne-du-Gué-de-l'Isle", 48.10361, -2.64778), + 22289 => array ("Saint-Fiacre", 48.46139, -3.06083), + 22290 => array ("Saint-Gelven", 48.22696, -3.09711), + 22291 => array ("Saint-Gildas", 48.42222, -3.00306), + 22292 => array ("Saint-Gilles-du-Mené", 48.24806, -2.54806), + 22293 => array ("Saint-Gilles-les-Bois", 48.64972, -3.10278), + 22294 => array ("Saint-Gilles-Pligeaux", 48.38, -3.09472), + 22295 => array ("Saint-Gilles-Vieux-Marché", 48.2436, -2.9737), + 22296 => array ("Saint-Glen", 48.35944, -2.52333), + 22297 => array ("Saint-Gouéno", 48.2675, -2.56861), + 22298 => array ("Saint-Guen", 48.21722, -2.93667), + 22299 => array ("Saint-Hélen", 48.47111, -1.95889), + 22300 => array ("Saint-Hervé", 48.27694, -2.8275), + 22302 => array ("Saint-Jacut-de-la-Mer", 48.59722, -2.19028), + 22303 => array ("Saint-Jacut-du-Mené", 48.28361, -2.48167), + 22304 => array ("Saint-Jean-Kerdaniel", 48.56528, -3.02139), + 22305 => array ("Saint-Jouan-de-l'Isle", 48.2675, -2.15917), + 22306 => array ("Saint-Judoce", 48.36333, -1.95417), + 22307 => array ("Saint-Julien", 48.4525, -2.81639), + 22308 => array ("Saint-Juvat", 48.35333, -2.04389), + 22309 => array ("Saint-Launeuc", 48.23361, -2.37556), + 22310 => array ("Saint-Laurent", 48.6189, -3.232), + 22311 => array ("Saint-Lormel", 48.54611, -2.22944), + 22312 => array ("Saint-Maden", 48.33056, -2.07722), + 22313 => array ("Saint-Martin-des-Prés", 48.30694, -2.95444), + 22314 => array ("Saint-Maudan", 48.11361, -2.77417), + 22315 => array ("Saint-Maudez", 48.45278, -2.18222), + 22316 => array ("Saint-Mayeux", 48.25583, -3.00639), + 22317 => array ("Saint-Méloir-des-Bois", 48.4578, -2.2501), + 22318 => array ("Saint-Michel-de-Plélan", 48.46611, -2.21528), + 22319 => array ("Saint-Michel-en-Grève", 48.68333, -3.56389), + 22320 => array ("Saint-Nicodème", 48.3426, -3.3382), + 22321 => array ("Saint-Nicolas-du-Pélem", 48.31306, -3.16444), + 22322 => array ("Saint-Péver", 48.48111, -3.10222), + 22323 => array ("Saint-Pôtan", 48.55778, -2.29139), + 22324 => array ("Saint-Quay-Perros", 48.7925, -3.44778), + 22325 => array ("Saint-Quay-Portrieux", 48.65194, -2.83111), + 22326 => array ("Saint-Rieul", 48.44222, -2.41944), + 22327 => array ("Saint-Samson-sur-Rance", 48.49139, -2.03083), + 22328 => array ("Saint-Servais", 48.3867, -3.3868), + 22330 => array ("Saint-Thélo", 48.22833, -2.85417), + 22331 => array ("Sainte-Tréphine", 48.27, -3.15417), + 22332 => array ("Saint-Trimoël", 48.38611, -2.54889), + 22333 => array ("Saint-Vran", 48.23778, -2.44222), + 22334 => array ("Saint-Igeaux", 48.27194, -3.10528), + 22335 => array ("Senven-Léhart", 48.425, -3.06917), + 22337 => array ("Sévignac", 48.33306, -2.33889), + 22338 => array ("Squiffiec", 48.62722, -3.15417), + 22339 => array ("Taden", 48.47556, -2.01778), + 22340 => array ("Tonquédec", 48.66917, -3.39528), + 22341 => array ("Tramain", 48.40111, -2.40194), + 22342 => array ("Trébédan", 48.39972, -2.17139), + 22343 => array ("Trébeurden", 48.76944, -3.56806), + 22344 => array ("Trébrivan", 48.30889, -3.47528), + 22345 => array ("Trébry", 48.35528, -2.55222), + 22346 => array ("Trédaniel", 48.35806, -2.61889), + 22347 => array ("Trédarzec", 48.78694, -3.20028), + 22348 => array ("Trédias", 48.35778, -2.23694), + 22349 => array ("Trédrez-Locquémeau", 48.69778, -3.56583), + 22350 => array ("Tréduder", 48.65056, -3.56222), + 22351 => array ("Treffrin", 48.29889, -3.51694), + 22352 => array ("Tréfumel", 48.33806, -2.02611), + 22353 => array ("Trégastel", 48.81694, -3.51361), + 22354 => array ("Tréglamus", 48.55694, -3.27472), + 22356 => array ("Trégomeur", 48.56556, -2.88194), + 22357 => array ("Trégon", 48.56917, -2.18361), + 22358 => array ("Trégonneau", 48.61167, -3.16389), + 22359 => array ("Trégrom", 48.60083, -3.40528), + 22360 => array ("Trégueux", 48.4911, -2.737), + 22361 => array ("Tréguidel", 48.60167, -2.94361), + 22362 => array ("Tréguier", 48.785, -3.2325), + 22363 => array ("Trélévern", 48.80833, -3.37167), + 22364 => array ("Trélivan", 48.43333, -2.11667), + 22365 => array ("Trémargat", 48.33222, -3.2675), + 22366 => array ("Trémel", 48.60306, -3.61139), + 22367 => array ("Tréméloir", 48.55556, -2.85833), + 22368 => array ("Tréméreuc", 48.55833, -2.06556), + 22369 => array ("Trémeur", 48.3475, -2.26389), + 22370 => array ("Tréméven", 48.67278, -3.02889), + 22371 => array ("Trémorel", 48.19917, -2.28889), + 22372 => array ("Trémuson", 48.52389, -2.84944), + 22373 => array ("Tréogan", 48.18917, -3.52), + 22375 => array ("Tressignaux", 48.61667, -2.98333), + 22376 => array ("Trévé", 48.21333, -2.79556), + 22377 => array ("Tréveneuc", 48.66389, -2.87), + 22378 => array ("Trévérec", 48.65472, -3.05917), + 22379 => array ("Trévou-Tréguignec", 48.815, -3.35861), + 22380 => array ("Trévron", 48.39139, -2.06306), + 22381 => array ("Trézény", 48.7575, -3.36611), + 22383 => array ("Troguéry", 48.75389, -3.22583), + 22384 => array ("Uzel", 48.27944, -2.84139), + 22385 => array ("La Vicomté-sur-Rance", 48.4884, -1.981), + 22386 => array ("Le Vieux-Bourg", 48.38861, -3.00056), + 22387 => array ("Le Vieux-Marché", 48.60694, -3.44806), + 22388 => array ("Vildé-Guingalan", 48.43778, -2.15861), + 22389 => array ("Yffiniac", 48.485, -2.6775), + 22390 => array ("Yvias", 48.71333, -3.0525), + 22391 => array ("Yvignac-la-Tour", 48.34833, -2.17583), + 29001 => array ("Argol", 48.24639, -4.31639), + 29002 => array ("Arzano", 47.90111, -3.44028), + 29003 => array ("Audierne", 48.02417, -4.54167), + 29004 => array ("Bannalec", 47.9325, -3.69694), + 29005 => array ("Baye", 47.8575, -3.60472), + 29006 => array ("Bénodet", 47.87667, -4.11278), + 29007 => array ("Berrien", 48.40417, -3.75194), + 29008 => array ("Beuzec-Cap-Sizun", 48.07528, -4.51167), + 29010 => array ("Bodilis", 48.53056, -4.11694), + 29011 => array ("Bohars", 48.42944, -4.51361), + 29012 => array ("Bolazec", 48.44444, -3.58389), + 29013 => array ("Botmeur", 48.38361, -3.91528), + 29014 => array ("Botsorhel", 48.52694, -3.64139), + 29015 => array ("Bourg-Blanc", 48.49944, -4.505), + 29016 => array ("Brasparts", 48.30056, -3.95556), + 29017 => array ("Brélès", 48.47806, -4.71361), + 29018 => array ("Brennilis", 48.3575, -3.85139), + 29019 => array ("Brest", 48.39, -4.48694), + 29020 => array ("Briec", 48.10167, -4.00167), + 29021 => array ("Brignogan-Plage", 48.66444, -4.32611), + 29022 => array ("Camaret-sur-Mer", 48.2752, -4.59718), + 29023 => array ("Carantec", 48.66778, -3.91361), + 29024 => array ("Carhaix-Plouguer", 48.27594, -3.57326), + 29025 => array ("Cast", 48.15722, -4.13889), + 29026 => array ("Châteaulin", 48.19667, -4.09), + 29027 => array ("Châteauneuf-du-Faou", 48.18611, -3.81278), + 29028 => array ("Cléden-Cap-Sizun", 48.04811, -4.64756), + 29029 => array ("Cléden-Poher", 48.23528, -3.66861), + 29030 => array ("Cléder", 48.66333, -4.10361), + 29031 => array ("Clohars-Carnoët", 47.79583, -3.58583), + 29032 => array ("Clohars-Fouesnant", 47.89778, -4.06806), + 29033 => array ("Le Cloître-Pleyben", 48.25694, -3.89056), + 29034 => array ("Le Cloître-Saint-Thégonnec", 48.48, -3.79472), + 29035 => array ("Coat-Méal", 48.5075, -4.54389), + 29036 => array ("Collorec", 48.28639, -3.775), + 29037 => array ("Combrit", 47.88694, -4.16), + 29038 => array ("Commana", 48.41389, -3.95472), + 29039 => array ("Concarneau", 47.87528, -3.91889), + 29040 => array ("Le Conquet", 48.36063, -4.77083), + 29041 => array ("Coray", 48.06111, -3.82972), + 29042 => array ("Crozon", 48.24568, -4.48929), + 29043 => array ("Daoulas", 48.36083, -4.25972), + 29044 => array ("Dinéault", 48.21889, -4.16556), + 29045 => array ("Dirinon", 48.39722, -4.26972), + 29046 => array ("Douarnenez", 48.09222, -4.33028), + 29047 => array ("Le Drennec", 48.53389, -4.37222), + 29048 => array ("Edern", 48.10306, -3.97694), + 29049 => array ("Elliant", 47.995, -3.89), + 29051 => array ("Ergué-Gabéric", 47.99611, -4.0225), + 29052 => array ("Esquibien", 48.025, -4.56278), + 29053 => array ("Le Faou", 48.29417, -4.1775), + 29054 => array ("La Feuillée", 48.39167, -3.85361), + 29055 => array ("Le Folgoët", 48.56222, -4.33444), + 29056 => array ("La Forest-Landerneau", 48.4275, -4.315), + 29057 => array ("La Forêt-Fouesnant", 47.90917, -3.97917), + 29058 => array ("Fouesnant", 47.89333, -4.01222), + 29059 => array ("Garlan", 48.60111, -3.75722), + 29060 => array ("Gouesnach", 47.91111, -4.115), + 29061 => array ("Gouesnou", 48.45361, -4.46444), + 29062 => array ("Gouézec", 48.16972, -3.97278), + 29063 => array ("Goulien", 48.05667, -4.5925), + 29064 => array ("Goulven", 48.62833, -4.30111), + 29065 => array ("Gourlizon", 48.01972, -4.26694), + 29066 => array ("Guengat", 48.04167, -4.20528), + 29067 => array ("Guerlesquin", 48.5175, -3.58861), + 29068 => array ("Guiclan", 48.55, -3.96194), + 29069 => array ("Guilers", 48.42444, -4.55806), + 29070 => array ("Guiler-sur-Goyen", 48.01583, -4.35944), + 29071 => array ("Guilligomarc'h", 47.93611, -3.41583), + 29072 => array ("Guilvinec", 47.7954, -4.2832), + 29073 => array ("Guimaëc", 48.66778, -3.70806), + 29074 => array ("Guimiliau", 48.48778, -3.99889), + 29075 => array ("Guipavas", 48.43333, -4.4), + 29076 => array ("Guipronvel", 48.49083, -4.57444), + 29077 => array ("Guissény", 48.63361, -4.41139), + 29078 => array ("Hanvec", 48.32806, -4.16111), + 29079 => array ("Henvic", 48.63222, -3.92778), + 29080 => array ("Hôpital-Camfrout", 48.3275, -4.24222), + 29081 => array ("Huelgoat", 48.36444, -3.74472), + 29082 => array ("Île-de-Batz", 48.7449, -4.0105), + 29083 => array ("Île-de-Sein", 48.0366, -4.8502), + 29084 => array ("Île-Molène", 48.3975, -4.95694), + 29085 => array ("Île-Tudy", 47.8425, -4.16806), + 29086 => array ("Irvillac", 48.37111, -4.21306), + 29087 => array ("Le Juch", 48.06611, -4.25444), + 29089 => array ("Kergloff", 48.27444, -3.61944), + 29090 => array ("Kerlaz", 48.09222, -4.27389), + 29091 => array ("Kerlouan", 48.64444, -4.36722), + 29093 => array ("Kernilis", 48.57111, -4.42), + 29094 => array ("Kernouës", 48.59083, -4.34861), + 29095 => array ("Kersaint-Plabennec", 48.47222, -4.37361), + 29097 => array ("Lampaul-Guimiliau", 48.49306, -4.04139), + 29098 => array ("Lampaul-Plouarzel", 48.44833, -4.76194), + 29099 => array ("Lampaul-Ploudalmézeau", 48.56139, -4.65583), + 29100 => array ("Lanarvily", 48.55194, -4.38833), + 29101 => array ("Landéda", 48.58694, -4.57167), + 29102 => array ("Landeleau", 48.22722, -3.72944), + 29103 => array ("Landerneau", 48.45083, -4.24944), + 29104 => array ("Landévennec", 48.29222, -4.26778), + 29105 => array ("Landivisiau", 48.50917, -4.06833), + 29106 => array ("Landrévarzec", 48.08944, -4.06056), + 29107 => array ("Landudal", 48.06278, -3.97833), + 29108 => array ("Landudec", 48.00056, -4.33639), + 29109 => array ("Landunvez", 48.5325, -4.72694), + 29110 => array ("Langolen", 48.06694, -3.91278), + 29111 => array ("Lanhouarneau", 48.57972, -4.21083), + 29112 => array ("Lanildut", 48.47333, -4.74944), + 29113 => array ("Lanmeur", 48.6477, -3.7144), + 29114 => array ("Lannéanou", 48.48917, -3.67194), + 29115 => array ("Lannédern", 48.30056, -3.89722), + 29116 => array ("Lanneuffret", 48.4975, -4.20306), + 29117 => array ("Lannilis", 48.56972, -4.51944), + 29119 => array ("Lanrivoaré", 48.4731, -4.6387), + 29120 => array ("Lanvéoc", 48.28583, -4.46222), + 29122 => array ("Laz", 48.1375, -3.83528), + 29123 => array ("Lennon", 48.19278, -3.8975), + 29124 => array ("Lesneven", 48.57194, -4.32222), + 29125 => array ("Leuhan", 48.09917, -3.78444), + 29126 => array ("Loc-Brévalaire", 48.55472, -4.40556), + 29127 => array ("Loc-Eguiner-Saint-Thégonnec", 48.46278, -3.96972), + 29128 => array ("Loc-Eguiner", 48.47306, -4.09444), + 29129 => array ("Locmaria-Berrien", 48.35389, -3.69472), + 29130 => array ("Locmaria-Plouzané", 48.37472, -4.64361), + 29131 => array ("Locmélar", 48.45278, -4.065), + 29132 => array ("Locquénolé", 48.62444, -3.86083), + 29133 => array ("Locquirec", 48.69083, -3.64833), + 29134 => array ("Locronan", 48.09861, -4.20722), + 29135 => array ("Loctudy", 47.83361, -4.16917), + 29136 => array ("Locunolé", 47.93611, -3.47833), + 29137 => array ("Logonna-Daoulas", 48.32056, -4.29861), + 29139 => array ("Lopérec", 48.27694, -4.04806), + 29140 => array ("Loperhet", 48.37472, -4.30528), + 29141 => array ("Loqueffret", 48.31944, -3.85833), + 29142 => array ("Lothey", 48.1824, -4.0267), + 29143 => array ("Mahalon", 48.03472, -4.43639), + 29144 => array ("La Martyre", 48.44833, -4.15917), + 29145 => array ("Confort-Meilars", 48.05028, -4.42639), + 29146 => array ("Melgven", 47.90583, -3.83611), + 29147 => array ("Mellac", 47.90389, -3.57806), + 29148 => array ("Mespaul", 48.61833, -4.02472), + 29149 => array ("Milizac", 48.4675, -4.56556), + 29150 => array ("Moëlan-sur-Mer", 47.81417, -3.62806), + 29151 => array ("Morlaix", 48.5775, -3.82778), + 29152 => array ("Motreff", 48.20167, -3.55528), + 29153 => array ("Névez", 47.81944, -3.7925), + 29155 => array ("Ouessant", 48.45528, -5.0975), + 29156 => array ("Pencran", 48.43778, -4.23528), + 29158 => array ("Penmarch", 47.8125, -4.3372), + 29159 => array ("Peumerit", 47.93861, -4.30917), + 29160 => array ("Plabennec", 48.50194, -4.42611), + 29161 => array ("Pleuven", 47.90611, -4.04333), + 29162 => array ("Pleyben", 48.22611, -3.96944), + 29163 => array ("Pleyber-Christ", 48.50472, -3.87528), + 29165 => array ("Plobannalec-Lesconil", 47.82333, -4.22972), + 29166 => array ("Ploéven", 48.15722, -4.2325), + 29167 => array ("Plogastel-Saint-Germain", 47.98361, -4.27139), + 29168 => array ("Plogoff", 48.03654, -4.6655), + 29169 => array ("Plogonnec", 48.07778, -4.19444), + 29170 => array ("Plomelin", 47.93556, -4.15306), + 29171 => array ("Plomeur", 47.84028, -4.28472), + 29172 => array ("Plomodiern", 48.18111, -4.23167), + 29173 => array ("Plonéis", 48.01694, -4.21056), + 29174 => array ("Plonéour-Lanvern", 47.90306, -4.28389), + 29175 => array ("Plonévez-du-Faou", 48.25222, -3.82472), + 29176 => array ("Plonévez-Porzay", 48.12556, -4.22194), + 29177 => array ("Plouarzel", 48.43333, -4.73333), + 29178 => array ("Ploudalmézeau", 48.54028, -4.6575), + 29179 => array ("Ploudaniel", 48.53694, -4.31306), + 29180 => array ("Ploudiry", 48.45333, -4.14278), + 29181 => array ("Plouédern", 48.48528, -4.24556), + 29182 => array ("Plouégat-Guérand", 48.62111, -3.67528), + 29183 => array ("Plouégat-Moysan", 48.57194, -3.61028), + 29184 => array ("Plouénan", 48.62833, -3.9925), + 29185 => array ("Plouescat", 48.6575, -4.17417), + 29186 => array ("Plouezoc'h", 48.6405, -3.8215), + 29187 => array ("Plougar", 48.56306, -4.14167), + 29188 => array ("Plougasnou", 48.69556, -3.78944), + 29189 => array ("Plougastel-Daoulas", 48.3725, -4.37056), + 29190 => array ("Plougonvelin", 48.34278, -4.71139), + 29191 => array ("Plougonven", 48.52083, -3.71306), + 29192 => array ("Plougoulm", 48.66694, -4.04444), + 29193 => array ("Plougourvest", 48.55333, -4.08472), + 29195 => array ("Plouguerneau", 48.60583, -4.50417), + 29196 => array ("Plouguin", 48.52444, -4.60111), + 29197 => array ("Plouhinec", 48.015, -4.48611), + 29198 => array ("Plouider", 48.60806, -4.29778), + 29199 => array ("Plouigneau", 48.5675, -3.70139), + 29201 => array ("Ploumoguer", 48.40361, -4.72278), + 29202 => array ("Plounéour-Ménez", 48.4389, -3.8899), + 29203 => array ("Plounéour-Trez", 48.65056, -4.31806), + 29204 => array ("Plounéventer", 48.51472, -4.2125), + 29205 => array ("Plounévézel", 48.29889, -3.58972), + 29206 => array ("Plounévez-Lochrist", 48.61694, -4.21194), + 29207 => array ("Plourin-lès-Morlaix", 48.5343, -3.79), + 29208 => array ("Plourin", 48.50944, -4.68694), + 29209 => array ("Plouvien", 48.52944, -4.45278), + 29210 => array ("Plouvorn", 48.57944, -4.03806), + 29211 => array ("Plouyé", 48.31417, -3.73556), + 29212 => array ("Plouzané", 48.38222, -4.62028), + 29213 => array ("Plouzévédé", 48.595, -4.1125), + 29214 => array ("Plovan", 47.91583, -4.36278), + 29215 => array ("Plozévet", 47.98667, -4.42528), + 29216 => array ("Pluguffan", 47.98083, -4.17889), + 29217 => array ("Pont-Aven", 47.85556, -3.74722), + 29218 => array ("Pont-Croix", 48.04194, -4.48917), + 29219 => array ("Le Ponthou", 48.56528, -3.63806), + 29220 => array ("Pont-l'Abbé", 47.86722, -4.22306), + 29221 => array ("Porspoder", 48.50861, -4.76556), + 29222 => array ("Port-Launay", 48.21528, -4.07278), + 29224 => array ("Pouldergat", 48.04333, -4.3275), + 29225 => array ("Pouldreuzic", 47.955, -4.36056), + 29226 => array ("Poullan-sur-Mer", 48.08083, -4.41333), + 29227 => array ("Poullaouen", 48.34, -3.6425), + 29228 => array ("Primelin", 48.02556, -4.61139), + 29229 => array ("Quéménéven", 48.11611, -4.12), + 29230 => array ("Querrien", 47.95972, -3.53694), + 29232 => array ("Quimper", 47.99583, -4.09778), + 29233 => array ("Quimperlé", 47.87278, -3.54972), + 29234 => array ("Rédené", 47.8602, -3.4606), + 29235 => array ("Le Relecq-Kerhuon", 48.40861, -4.39694), + 29236 => array ("Riec-sur-Belon", 47.844, -3.6937), + 29237 => array ("La Roche-Maurice", 48.47222, -4.20333), + 29238 => array ("Roscanvel", 48.315, -4.54694), + 29239 => array ("Roscoff", 48.72667, -3.98583), + 29240 => array ("Rosnoën", 48.26361, -4.19472), + 29241 => array ("Rosporden", 47.96056, -3.83472), + 29243 => array ("Saint-Coulitz", 48.19083, -4.05972), + 29244 => array ("Saint-Derrien", 48.5475, -4.1825), + 29245 => array ("Saint-Divy", 48.45389, -4.33528), + 29246 => array ("Saint-Eloy", 48.36194, -4.12306), + 29247 => array ("Saint-Évarzec", 47.93694, -4.02028), + 29248 => array ("Saint-Frégant", 48.60222, -4.3675), + 29249 => array ("Saint-Goazec", 48.1625, -3.78417), + 29250 => array ("Saint-Hernin", 48.21806, -3.63472), + 29251 => array ("Saint-Jean-du-Doigt", 48.69444, -3.7725), + 29252 => array ("Saint-Jean-Trolimon", 47.86611, -4.28056), + 29254 => array ("Saint-Martin-des-Champs", 48.57611, -3.84361), + 29255 => array ("Saint-Méen", 48.56111, -4.26556), + 29256 => array ("Saint-Nic", 48.2025, -4.28167), + 29257 => array ("Saint-Pabu", 48.56583, -4.60278), + 29259 => array ("Saint-Pol-de-Léon", 48.68528, -3.98639), + 29260 => array ("Saint-Renan", 48.43389, -4.62139), + 29261 => array ("Saint-Rivoal", 48.34917, -3.99667), + 29262 => array ("Saint-Sauveur", 48.44833, -4.00333), + 29263 => array ("Saint-Ségal", 48.24111, -4.06722), + 29264 => array ("Saint-Servais", 48.51111, -4.155), + 29265 => array ("Sainte-Sève", 48.55806, -3.87472), + 29266 => array ("Saint-Thégonnec", 48.52111, -3.94667), + 29267 => array ("Saint-Thois", 48.16528, -3.88444), + 29268 => array ("Saint-Thonan", 48.47889, -4.33583), + 29269 => array ("Saint-Thurien", 47.95917, -3.62361), + 29270 => array ("Saint-Urbain", 48.40028, -4.22639), + 29271 => array ("Saint-Vougay", 48.59444, -4.13861), + 29272 => array ("Saint-Yvi", 47.9675, -3.93472), + 29273 => array ("Santec", 48.70306, -4.02917), + 29274 => array ("Scaër", 48.02722, -3.70222), + 29275 => array ("Scrignac", 48.43366, -3.67847), + 29276 => array ("Sibiril", 48.665, -4.06389), + 29277 => array ("Sizun", 48.405, -4.0775), + 29278 => array ("Spézet", 48.1925, -3.71667), + 29279 => array ("Taulé", 48.60361, -3.90028), + 29280 => array ("Telgruc-sur-Mer", 48.23139, -4.35639), + 29281 => array ("Tourch", 48.02444, -3.82583), + 29282 => array ("Trébabu", 48.37056, -4.73528), + 29284 => array ("Treffiagat", 47.8035, -4.2625), + 29285 => array ("Tréflaouénan", 48.62778, -4.09639), + 29286 => array ("Tréflévénez", 48.415, -4.17), + 29287 => array ("Tréflez", 48.62278, -4.26139), + 29288 => array ("Trégarantec", 48.55111, -4.29417), + 29289 => array ("Trégarvan", 48.25139, -4.22528), + 29290 => array ("Tréglonou", 48.55056, -4.53944), + 29291 => array ("Trégourez", 48.10611, -3.86306), + 29292 => array ("Tréguennec", 47.89167, -4.33333), + 29293 => array ("Trégunc", 47.85583, -3.85194), + 29294 => array ("Le Tréhou", 48.39389, -4.13139), + 29295 => array ("Trémaouézan", 48.505, -4.255), + 29296 => array ("Tréméoc", 47.90528, -4.21194), + 29297 => array ("Tréméven", 47.89833, -3.5325), + 29298 => array ("Tréogat", 47.91944, -4.32333), + 29299 => array ("Tréouergat", 48.49667, -4.6025), + 29300 => array ("Le Trévoux", 47.89444, -3.64139), + 29301 => array ("Trézilidé", 48.60917, -4.0875), + 29302 => array ("Pont-de-Buis-lès-Quimerch", 48.2544, -4.0888), + 35001 => array ("Acigné", 48.13417, -1.53667), + 35002 => array ("Amanlis", 48.00639, -1.47611), + 35003 => array ("Andouillé-Neuville", 48.29361, -1.59), + 35004 => array ("Antrain", 48.4605, -1.4842), + 35005 => array ("Arbrissel", 47.92667, -1.30444), + 35006 => array ("Argentré-du-Plessis", 48.05722, -1.15389), + 35007 => array ("Aubigné", 48.29444, -1.635), + 35008 => array ("Availles-sur-Seiche", 47.96111, -1.19583), + 35009 => array ("Baguer-Morvan", 48.525, -1.77389), + 35010 => array ("Baguer-Pican", 48.5525, -1.69917), + 35011 => array ("Baillé", 48.35944, -1.38167), + 35012 => array ("Bain-de-Bretagne", 47.84222, -1.68194), + 35013 => array ("Bains-sur-Oust", 47.70361, -2.07139), + 35014 => array ("Bais", 48.01056, -1.29056), + 35015 => array ("Balazé", 48.16889, -1.19167), + 35016 => array ("Baulon", 47.98528, -1.93167), + 35017 => array ("La Baussaine", 48.31333, -1.89806), + 35018 => array ("La Bazouge-du-Désert", 48.4434, -1.1044), + 35019 => array ("Bazouges-la-Pérouse", 48.42611, -1.57417), + 35021 => array ("Beaucé", 48.33806, -1.15694), + 35022 => array ("Bécherel", 48.29528, -1.945), + 35023 => array ("Bédée", 48.17944, -1.94417), + 35024 => array ("Betton", 48.1825, -1.64389), + 35025 => array ("Billé", 48.28778, -1.24639), + 35026 => array ("Bléruais", 48.11111, -2.12472), + 35027 => array ("Boisgervilly", 48.16694, -2.06389), + 35028 => array ("Boistrudan", 47.96944, -1.40111), + 35029 => array ("Bonnemain", 48.46667, -1.76667), + 35030 => array ("La Bosse-de-Bretagne", 47.86944, -1.59889), + 35031 => array ("La Bouëxière", 48.18389, -1.43806), + 35032 => array ("Bourgbarré", 47.99444, -1.61472), + 35033 => array ("Bourg-des-Comptes", 47.92917, -1.7425), + 35034 => array ("La Boussac", 48.51278, -1.66056), + 35035 => array ("Bovel", 47.95028, -1.97694), + 35037 => array ("Bréal-sous-Montfort", 48.04778, -1.86667), + 35038 => array ("Bréal-sous-Vitré", 48.10306, -1.06111), + 35039 => array ("Brécé", 48.10889, -1.4825), + 35040 => array ("Breteil", 48.14528, -1.89861), + 35041 => array ("Brie", 47.95194, -1.53722), + 35042 => array ("Brielles", 48.00861, -1.09056), + 35044 => array ("Broualan", 48.47361, -1.65), + 35045 => array ("Bruc-sur-Aff", 47.81472, -2.01861), + 35046 => array ("Les Brulais", 47.88889, -2.04306), + 35047 => array ("Bruz", 48.02472, -1.74583), + 35048 => array ("Campel", 47.93639, -2.00972), + 35049 => array ("Cancale", 48.67667, -1.85194), + 35050 => array ("Cardroc", 48.28528, -1.88972), + 35051 => array ("Cesson-Sévigné", 48.1212, -1.603), + 35052 => array ("Champeaux", 48.14722, -1.31111), + 35053 => array ("Chancé", 48.03583, -1.38), + 35054 => array ("Chanteloup", 47.96583, -1.61556), + 35055 => array ("Chantepie", 48.08861, -1.61639), + 35056 => array ("La Chapelle-aux-Filtzméens", 48.384, -1.8244), + 35057 => array ("La Chapelle-Bouëxic", 47.92917, -1.94083), + 35058 => array ("La Chapelle-Chaussée", 48.27167, -1.85472), + 35059 => array ("La Chapelle-des-Fougeretz", 48.17722, -1.73278), + 35060 => array ("La Chapelle-du-Lou", 48.21278, -1.99222), + 35061 => array ("La Chapelle-Erbrée", 48.14111, -1.10056), + 35062 => array ("La Chapelle-Janson", 48.3475, -1.10194), + 35063 => array ("La Chapelle-Saint-Aubert", 48.31361, -1.30806), + 35064 => array ("La Chapelle-de-Brain", 47.70028, -1.93472), + 35065 => array ("La Chapelle-Thouarault", 48.12444, -1.86556), + 35066 => array ("Chartres-de-Bretagne", 48.0393, -1.7029), + 35067 => array ("Chasné-sur-Illet", 48.24167, -1.56167), + 35068 => array ("Châteaubourg", 48.11111, -1.40333), + 35069 => array ("Châteaugiron", 48.04806, -1.50306), + 35070 => array ("Châteauneuf-d'Ille-et-Vilaine", 48.56111, -1.92972), + 35071 => array ("Le Châtellier", 48.41583, -1.25417), + 35072 => array ("Châtillon-en-Vendelais", 48.225, -1.17861), + 35075 => array ("Chauvigné", 48.37583, -1.46028), + 35076 => array ("Chavagne", 48.05417, -1.78444), + 35077 => array ("Chelun", 47.85458, -1.22317), + 35078 => array ("Cherrueix", 48.605, -1.71083), + 35079 => array ("Chevaigné", 48.21194, -1.62972), + 35080 => array ("Cintré", 48.10556, -1.87222), + 35081 => array ("Clayes", 48.17667, -1.8525), + 35082 => array ("Coësmes", 47.88306, -1.44083), + 35083 => array ("Coglès", 48.45972, -1.36417), + 35084 => array ("Comblessac", 47.87639, -2.08389), + 35085 => array ("Combourg", 48.40861, -1.75167), + 35086 => array ("Combourtillé", 48.27194, -1.24556), + 35087 => array ("Cornillé", 48.08028, -1.30694), + 35088 => array ("Corps-Nuds", 47.97806, -1.58639), + 35089 => array ("La Couyère", 47.8875, -1.50528), + 35090 => array ("Crevin", 47.93722, -1.66167), + 35091 => array ("Le Crouais", 48.20722, -2.13861), + 35092 => array ("Cuguen", 48.4497, -1.6624), + 35093 => array ("Dinard", 48.6325, -2.06167), + 35094 => array ("Dingé", 48.3575, -1.71611), + 35095 => array ("Dol-de-Bretagne", 48.55, -1.75), + 35096 => array ("Domagné", 48.0708, -1.3906), + 35097 => array ("Domalain", 47.99611, -1.24167), + 35098 => array ("La Dominelais", 47.7625, -1.68833), + 35099 => array ("Domloup", 48.0618, -1.522), + 35100 => array ("Dompierre-du-Chemin", 48.26694, -1.14361), + 35101 => array ("Dourdain", 48.19306, -1.37), + 35102 => array ("Drouges", 47.90167, -1.265), + 35103 => array ("Eancé", 47.82194, -1.24278), + 35104 => array ("Epiniac", 48.51, -1.69611), + 35105 => array ("Erbrée", 48.09889, -1.12444), + 35106 => array ("Ercé-en-Lamée", 47.83028, -1.55917), + 35107 => array ("Ercé-près-Liffré", 48.25556, -1.51778), + 35108 => array ("Essé", 47.9575, -1.42444), + 35109 => array ("Étrelles", 48.06028, -1.19361), + 35110 => array ("Feins", 48.32889, -1.64028), + 35111 => array ("Le Ferré", 48.4925, -1.29389), + 35112 => array ("Fleurigné", 48.33556, -1.12083), + 35113 => array ("La Fontenelle", 48.46778, -1.50278), + 35114 => array ("Forges-la-Forêt", 47.85944, -1.28139), + 35115 => array ("Fougères", 48.35167, -1.2), + 35116 => array ("La Fresnais", 48.59556, -1.84333), + 35117 => array ("Gaël", 48.13222, -2.22139), + 35118 => array ("Gahard", 48.29694, -1.51917), + 35119 => array ("Gennes-sur-Seiche", 47.98861, -1.12361), + 35120 => array ("Gévezé", 48.21917, -1.78806), + 35121 => array ("Gosné", 48.24611, -1.46472), + 35122 => array ("La Gouesnière", 48.6054, -1.8937), + 35123 => array ("Goven", 48.00611, -1.8475), + 35124 => array ("Grand-Fougeray", 47.72306, -1.73306), + 35125 => array ("La Guerche-de-Bretagne", 47.94139, -1.22972), + 35126 => array ("Guichen", 47.9675, -1.795), + 35127 => array ("Guignen", 47.92056, -1.86167), + 35128 => array ("Guipel", 48.29833, -1.72139), + 35129 => array ("Guipry", 47.82472, -1.84278), + 35130 => array ("Hédé-Bazouges", 48.29472, -1.8025), + 35131 => array ("L'Hermitage", 48.12583, -1.81583), + 35132 => array ("Hirel", 48.60556, -1.80167), + 35133 => array ("Iffendic", 48.13028, -2.03361), + 35134 => array ("Les Iffs", 48.28778, -1.86667), + 35135 => array ("Irodouër", 48.25, -1.95), + 35136 => array ("Janzé", 47.96056, -1.49917), + 35137 => array ("Javené", 48.31917, -1.21583), + 35138 => array ("Laignelet", 48.37083, -1.15028), + 35139 => array ("Laillé", 47.97778, -1.71833), + 35140 => array ("Lalleu", 47.85528, -1.51028), + 35141 => array ("Landavran", 48.15889, -1.28944), + 35142 => array ("Landéan", 48.41306, -1.1525), + 35143 => array ("Landujan", 48.25028, -1.99639), + 35144 => array ("Langan", 48.24444, -1.85361), + 35145 => array ("Langon", 47.71972, -1.84806), + 35146 => array ("Langouet", 48.24972, -1.82306), + 35147 => array ("Lanhélin", 48.45861, -1.82806), + 35148 => array ("Lanrigan", 48.39639, -1.69889), + 35149 => array ("Lassy", 47.97889, -1.87), + 35150 => array ("Lécousse", 48.3675, -1.21778), + 35151 => array ("Lieuron", 47.85111, -1.94444), + 35152 => array ("Liffré", 48.21361, -1.50806), + 35153 => array ("Lillemer", 48.56472, -1.86028), + 35154 => array ("Livré-sur-Changeon", 48.21889, -1.34333), + 35155 => array ("Lohéac", 47.86556, -1.88333), + 35156 => array ("Longaulnay", 48.3102, -1.9387), + 35157 => array ("Le Loroux", 48.39472, -1.06417), + 35158 => array ("Le Lou-du-Lac", 48.20944, -1.99278), + 35159 => array ("Lourmais", 48.45, -1.7275), + 35160 => array ("Loutehel", 47.93611, -2.08028), + 35161 => array ("Louvigné-de-Bais", 48.04861, -1.33056), + 35162 => array ("Louvigné-du-Désert", 48.48167, -1.12417), + 35163 => array ("Luitré", 48.28333, -1.11861), + 35164 => array ("Marcillé-Raoul", 48.38694, -1.60583), + 35165 => array ("Marcillé-Robert", 47.95, -1.36111), + 35166 => array ("Marpiré", 48.14306, -1.33972), + 35167 => array ("Martigné-Ferchaud", 47.82778, -1.31833), + 35168 => array ("Maure-de-Bretagne", 47.89111, -1.99167), + 35169 => array ("Maxent", 47.98333, -2.03333), + 35170 => array ("Mecé", 48.23722, -1.3025), + 35171 => array ("Médréac", 48.26667, -2.06667), + 35172 => array ("Meillac", 48.41222, -1.81333), + 35173 => array ("Melesse", 48.21722, -1.69611), + 35174 => array ("Mellé", 48.4884, -1.1881), + 35175 => array ("Mernel", 47.89778, -1.96722), + 35176 => array ("Messac", 47.82472, -1.80861), + 35177 => array ("La Mézière", 48.21833, -1.75556), + 35178 => array ("Mézières-sur-Couesnon", 48.29556, -1.43278), + 35179 => array ("Miniac-Morvan", 48.51472, -1.9), + 35180 => array ("Miniac-sous-Bécherel", 48.28528, -1.93222), + 35181 => array ("Le Minihic-sur-Rance", 48.57667, -2.0125), + 35183 => array ("Mondevert", 48.08417, -1.09889), + 35184 => array ("Montauban-de-Bretagne", 48.19917, -2.04806), + 35185 => array ("Montautour", 48.20389, -1.14694), + 35186 => array ("Mont-Dol", 48.5694, -1.765), + 35187 => array ("Monterfil", 48.06583, -1.97861), + 35188 => array ("Montfort-sur-Meu", 48.13806, -1.95583), + 35189 => array ("Montgermont", 48.15667, -1.71611), + 35190 => array ("Monthault", 48.51139, -1.18139), + 35191 => array ("Montours", 48.44278, -1.30889), + 35192 => array ("Montreuil-des-Landes", 48.24694, -1.22333), + 35193 => array ("Montreuil-le-Gast", 48.24694, -1.72667), + 35194 => array ("Montreuil-sous-Pérouse", 48.15083, -1.23667), + 35195 => array ("Montreuil-sur-Ille", 48.30722, -1.66917), + 35196 => array ("Mordelles", 48.07472, -1.84583), + 35197 => array ("Mouazé", 48.23167, -1.60972), + 35198 => array ("Moulins", 48.0025, -1.37222), + 35199 => array ("Moussé", 47.9232, -1.2691), + 35200 => array ("Moutiers", 47.96722, -1.21389), + 35201 => array ("Muel", 48.1275, -2.1565), + 35202 => array ("La Noë-Blanche", 47.8025, -1.74139), + 35203 => array ("La Nouaye", 48.1626, -1.9783), + 35204 => array ("Nouvoitou", 48.04056, -1.54583), + 35205 => array ("Noyal-sous-Bazouges", 48.41333, -1.62417), + 35206 => array ("Noyal-Châtillon-sur-Seiche", 48.0438, -1.657), + 35207 => array ("Noyal-sur-Vilaine", 48.11167, -1.52444), + 35208 => array ("Orgères", 47.99889, -1.66833), + 35209 => array ("Ossé", 48.05583, -1.44944), + 35210 => array ("Pacé", 48.14778, -1.77389), + 35211 => array ("Paimpont", 48.01806, -2.17111), + 35212 => array ("Pancé", 47.88139, -1.65833), + 35214 => array ("Parcé", 48.27306, -1.20028), + 35215 => array ("Parigné", 48.42806, -1.19167), + 35216 => array ("Parthenay-de-Bretagne", 48.19194, -1.82806), + 35217 => array ("Le Pertre", 48.03472, -1.03722), + 35218 => array ("Le Petit-Fougeray", 47.92778, -1.60889), + 35219 => array ("Pipriac", 47.80917, -1.94778), + 35220 => array ("Piré-sur-Seiche", 48.00944, -1.43056), + 35221 => array ("Pléchâtel", 47.89444, -1.74861), + 35222 => array ("Pleine-Fougères", 48.53361, -1.56417), + 35223 => array ("Plélan-le-Grand", 48.00167, -2.09917), + 35224 => array ("Plerguer", 48.52667, -1.84722), + 35225 => array ("Plesder", 48.41361, -1.9225), + 35226 => array ("Pleugueneuc", 48.39639, -1.90361), + 35227 => array ("Pleumeleuc", 48.18444, -1.91889), + 35228 => array ("Pleurtuit", 48.57972, -2.05833), + 35229 => array ("Pocé-les-Bois", 48.11667, -1.25), + 35230 => array ("Poilley", 48.46556, -1.2625), + 35231 => array ("Poligné", 47.8875, -1.68639), + 35232 => array ("Princé", 48.21639, -1.08833), + 35233 => array ("Québriac", 48.34472, -1.82667), + 35234 => array ("Quédillac", 48.24944, -2.1425), + 35235 => array ("Rannée", 47.92417, -1.24056), + 35236 => array ("Redon", 47.65139, -2.08472), + 35237 => array ("Renac", 47.71972, -1.97639), + 35238 => array ("Rennes", 48.11417, -1.68083), + 35239 => array ("Retiers", 47.91333, -1.38194), + 35240 => array ("Le Rheu", 48.10194, -1.79556), + 35241 => array ("La Richardais", 48.60639, -2.035), + 35242 => array ("Rimou", 48.3993, -1.5112), + 35243 => array ("Romagné", 48.34222, -1.27722), + 35244 => array ("Romazy", 48.37556, -1.49694), + 35245 => array ("Romillé", 48.21583, -1.89194), + 35246 => array ("Roz-Landrieux", 48.54333, -1.81583), + 35247 => array ("Roz-sur-Couesnon", 48.58889, -1.5925), + 35248 => array ("Sains", 48.55139, -1.58417), + 35249 => array ("Sainte-Anne-sur-Vilaine", 47.73056, -1.82556), + 35250 => array ("Saint-Armel", 48.01167, -1.59056), + 35251 => array ("Saint-Aubin-d'Aubigné", 48.26222, -1.60611), + 35252 => array ("Saint-Aubin-des-Landes", 48.09417, -1.29528), + 35253 => array ("Saint-Aubin-du-Cormier", 48.25944, -1.39861), + 35254 => array ("Saint-Aubin-du-Pavail", 48.04361, -1.46139), + 35255 => array ("Saint-Benoît-des-Ondes", 48.61972, -1.85139), + 35256 => array ("Saint-Briac-sur-Mer", 48.6207, -2.1331), + 35257 => array ("Saint-Brice-en-Coglès", 48.411, -1.3663), + 35258 => array ("Saint-Brieuc-des-Iffs", 48.29111, -1.85139), + 35259 => array ("Saint-Broladre", 48.58667, -1.65667), + 35260 => array ("Saint-Christophe-des-Bois", 48.22667, -1.2475), + 35261 => array ("Saint-Christophe-de-Valains", 48.34278, -1.44722), + 35262 => array ("Sainte-Colombe", 47.88778, -1.45611), + 35263 => array ("Saint-Coulomb", 48.675, -1.91167), + 35264 => array ("Saint-Didier", 48.09472, -1.37167), + 35265 => array ("Saint-Domineuc", 48.37417, -1.87611), + 35266 => array ("Saint-Erblon", 48.01889, -1.65167), + 35267 => array ("Saint-Étienne-en-Coglès", 48.40361, -1.325), + 35268 => array ("Saint-Ganton", 47.7648, -1.8892), + 35269 => array ("Saint-Georges-de-Chesné", 48.27361, -1.29139), + 35270 => array ("Saint-Georges-de-Gréhaigne", 48.56806, -1.54917), + 35271 => array ("Saint-Georges-de-Reintembault", 48.50833, -1.24333), + 35272 => array ("Saint-Germain-du-Pinel", 48.01222, -1.16611), + 35273 => array ("Saint-Germain-en-Coglès", 48.40583, -1.26333), + 35274 => array ("Saint-Germain-sur-Ille", 48.24889, -1.65917), + 35275 => array ("Saint-Gilles", 48.15361, -1.82611), + 35276 => array ("Saint-Gondran", 48.26694, -1.83556), + 35277 => array ("Saint-Gonlay", 48.11306, -2.06583), + 35278 => array ("Saint-Grégoire", 48.15111, -1.68611), + 35279 => array ("Saint-Guinoux", 48.57556, -1.88361), + 35280 => array ("Saint-Hilaire-des-Landes", 48.35139, -1.35806), + 35281 => array ("Saint-Jacques-de-la-Lande", 48.09028, -1.69556), + 35282 => array ("Saint-Jean-sur-Couesnon", 48.29056, -1.36722), + 35283 => array ("Saint-Jean-sur-Vilaine", 48.11694, -1.36083), + 35284 => array ("Saint-Jouan-des-Guérets", 48.59861, -1.97306), + 35285 => array ("Saint-Just", 47.76583, -1.96111), + 35286 => array ("Saint-Léger-des-Prés", 48.39528, -1.65167), + 35287 => array ("Saint-Lunaire", 48.63444, -2.10889), + 35288 => array ("Saint-Malo", 48.64722, -2.00889), + 35289 => array ("Saint-Malo-de-Phily", 47.8773, -1.7875), + 35290 => array ("Saint-Malon-sur-Mel", 48.0933, -2.098), + 35291 => array ("Saint-Marcan", 48.58778, -1.63333), + 35292 => array ("Saint-Marc-le-Blanc", 48.36444, -1.40944), + 35293 => array ("Saint-Marc-sur-Couesnon", 48.30389, -1.36556), + 35294 => array ("Sainte-Marie", 47.69389, -2.00083), + 35295 => array ("Saint-Maugan", 48.135, -2.08361), + 35296 => array ("Saint-Médard-sur-Ille", 48.27278, -1.65972), + 35297 => array ("Saint-Méen-le-Grand", 48.18917, -2.19083), + 35299 => array ("Saint-Méloir-des-Ondes", 48.6378, -1.9018), + 35300 => array ("Saint-M'Hervé", 48.17861, -1.11611), + 35301 => array ("Saint-M'Hervon", 48.22833, -2.05889), + 35302 => array ("Saint-Onen-la-Chapelle", 48.177, -2.1718), + 35303 => array ("Saint-Ouen-la-Rouërie", 48.4632, -1.4401), + 35304 => array ("Saint-Ouen-des-Alleux", 48.32833, -1.42583), + 35305 => array ("Saint-Péran", 48.055, -2.05611), + 35306 => array ("Saint-Père", 48.58778, -1.92389), + 35307 => array ("Saint-Pern", 48.28778, -1.98667), + 35308 => array ("Saint-Pierre-de-Plesguen", 48.44722, -1.91333), + 35309 => array ("Saint-Rémy-du-Plain", 48.37028, -1.57139), + 35310 => array ("Saint-Sauveur-des-Landes", 48.3425, -1.31361), + 35311 => array ("Saint-Séglin", 47.85333, -2.00472), + 35312 => array ("Saint-Senoux", 47.90583, -1.78778), + 35314 => array ("Saint-Suliac", 48.57, -1.9725), + 35315 => array ("Saint-Sulpice-la-Forêt", 48.21806, -1.57944), + 35316 => array ("Saint-Sulpice-des-Landes", 47.7666, -1.6219), + 35317 => array ("Saint-Symphorien", 48.2925, -1.82167), + 35318 => array ("Saint-Thual", 48.33667, -1.93389), + 35319 => array ("Saint-Thurial", 48.02917, -1.93167), + 35320 => array ("Saint-Uniac", 48.17306, -2.02861), + 35321 => array ("Saulnières", 47.91583, -1.5875), + 35322 => array ("Le Sel-de-Bretagne", 47.89583, -1.61056), + 35323 => array ("La Selle-en-Coglès", 48.4375, -1.34472), + 35324 => array ("La Selle-en-Luitré", 48.3102, -1.1269), + 35325 => array ("La Selle-Guerchaise", 47.94389, -1.17278), + 35326 => array ("Sens-de-Bretagne", 48.33361, -1.53611), + 35327 => array ("Servon-sur-Vilaine", 48.12139, -1.46056), + 35328 => array ("Sixt-sur-Aff", 47.77583, -2.07833), + 35329 => array ("Sougéal", 48.51, -1.52278), + 35330 => array ("Taillis", 48.18889, -1.23889), + 35331 => array ("Talensac", 48.10889, -1.92611), + 35332 => array ("Teillay", 47.80722, -1.53861), + 35333 => array ("Le Theil-de-Bretagne", 47.91944, -1.42972), + 35334 => array ("Thorigné-Fouillard", 48.1599, -1.5787), + 35335 => array ("Thourie", 47.85417, -1.48083), + 35336 => array ("Le Tiercent", 48.34917, -1.40639), + 35337 => array ("Tinténiac", 48.3291, -1.8343), + 35338 => array ("Torcé", 48.06167, -1.26583), + 35339 => array ("Trans-la-Forêt", 48.49667, -1.58861), + 35340 => array ("Treffendel", 48.04, -2.00556), + 35341 => array ("Tremblay", 48.42222, -1.47583), + 35342 => array ("Trémeheuc", 48.42556, -1.70333), + 35343 => array ("Tresbœuf", 47.8825, -1.54583), + 35344 => array ("Tressé", 48.48111, -1.885), + 35345 => array ("Trévérien", 48.37083, -1.92806), + 35346 => array ("Trimer", 48.34056, -1.89361), + 35347 => array ("Val-d'Izé", 48.17722, -1.30472), + 35348 => array ("Vendel", 48.29917, -1.31139), + 35350 => array ("Vergéal", 48.03583, -1.26583), + 35351 => array ("Le Verger", 48.06944, -1.93306), + 35352 => array ("Vern-sur-Seiche", 48.04472, -1.60028), + 35353 => array ("Vezin-le-Coquet", 48.11861, -1.75611), + 35354 => array ("Vieux-Viel", 48.51139, -1.54472), + 35355 => array ("Vieux-Vy-sur-Couesnon", 48.34167, -1.48944), + 35356 => array ("Vignoc", 48.24889, -1.78222), + 35357 => array ("Villamée", 48.46083, -1.22167), + 35358 => array ("La Ville-ès-Nonais", 48.54778, -1.95333), + 35359 => array ("Visseiche", 47.95583, -1.30083), + 35360 => array ("Vitré", 48.12333, -1.20944), + 35361 => array ("Le Vivier-sur-Mer", 48.60222, -1.77417), + 35362 => array ("Le Tronchet", 48.48611, -1.83639), + 35363 => array ("Pont-Péan", 48.0132, -1.7038), + 44001 => array ("Abbaretz", 47.5525, -1.53167), + 44002 => array ("Aigrefeuille-sur-Maine", 47.07667, -1.40056), + 44003 => array ("Ancenis", 47.37278, -1.17833), + 44004 => array ("Anetz", 47.38222, -1.10278), + 44005 => array ("Arthon-en-Retz", 47.11639, -1.93861), + 44006 => array ("Assérac", 47.42917, -2.39056), + 44007 => array ("Avessac", 47.65333, -1.98972), + 44008 => array ("Barbechat", 47.2775, -1.28639), + 44009 => array ("Basse-Goulaine", 47.21528, -1.46556), + 44010 => array ("Batz-sur-Mer", 47.27722, -2.48028), + 44011 => array ("Belligné", 47.4675, -1.02806), + 44012 => array ("La Bernerie-en-Retz", 47.08139, -2.0375), + 44013 => array ("Besné", 47.3975, -2.09139), + 44014 => array ("Le Bignon", 47.0988, -1.4896), + 44015 => array ("Blain", 47.47611, -1.76361), + 44016 => array ("La Boissière-du-Doré", 47.232, -1.2196), + 44017 => array ("Bonnœuvre", 47.5275, -1.23611), + 44018 => array ("Bouaye", 47.14361, -1.68889), + 44019 => array ("Bouée", 47.32083, -1.91), + 44020 => array ("Bouguenais", 47.17917, -1.62472), + 44021 => array ("Bourgneuf-en-Retz", 47.04306, -1.95278), + 44022 => array ("Boussay", 47.04417, -1.18639), + 44023 => array ("Bouvron", 47.41778, -1.84778), + 44024 => array ("Brains", 47.16917, -1.72167), + 44025 => array ("Campbon", 47.41194, -1.96861), + 44026 => array ("Carquefou", 47.29694, -1.49278), + 44027 => array ("Casson", 47.38583, -1.55944), + 44028 => array ("Le Cellier", 47.31944, -1.34639), + 44029 => array ("La Chapelle-Basse-Mer", 47.27167, -1.33806), + 44030 => array ("La Chapelle-des-Marais", 47.44722, -2.24167), + 44031 => array ("La Chapelle-Glain", 47.62306, -1.19694), + 44032 => array ("La Chapelle-Heulin", 47.17639, -1.34056), + 44033 => array ("La Chapelle-Launay", 47.3725, -1.97194), + 44034 => array ("La Chapelle-Saint-Sauveur", 47.44111, -0.98639), + 44035 => array ("La Chapelle-sur-Erdre", 47.29889, -1.55278), + 44036 => array ("Châteaubriant", 47.71694, -1.37611), + 44037 => array ("Château-Thébaud", 47.1249, -1.4185), + 44038 => array ("Chauvé", 47.15111, -1.98611), + 44039 => array ("Cheix-en-Retz", 47.18167, -1.81556), + 44040 => array ("Chéméré", 47.12194, -1.91472), + 44041 => array ("La Chevrolière", 47.09083, -1.61194), + 44043 => array ("Clisson", 47.08694, -1.28361), + 44044 => array ("Conquereuil", 47.62444, -1.75139), + 44045 => array ("Cordemais", 47.29028, -1.87889), + 44046 => array ("Corsept", 47.2775, -2.05917), + 44047 => array ("Couëron", 47.21472, -1.72389), + 44048 => array ("Couffé", 47.39139, -1.29278), + 44049 => array ("Le Croisic", 47.29306, -2.51056), + 44050 => array ("Crossac", 47.41139, -2.16833), + 44051 => array ("Derval", 47.6675, -1.67139), + 44052 => array ("Donges", 47.3229, -2.0755), + 44053 => array ("Drefféac", 47.4768, -2.0557), + 44054 => array ("Erbray", 47.65583, -1.31778), + 44055 => array ("La Baule-Escoublac", 47.286, -2.3914), + 44056 => array ("Fay-de-Bretagne", 47.415, -1.79139), + 44057 => array ("Fégréac", 47.58472, -2.04444), + 44058 => array ("Fercé", 47.79611, -1.41528), + 44059 => array ("Fresnay-en-Retz", 47.02444, -1.87639), + 44060 => array ("Le Fresne-sur-Loire", 47.40083, -0.92833), + 44061 => array ("Frossay", 47.24389, -1.93417), + 44062 => array ("Le Gâvre", 47.52056, -1.74861), + 44063 => array ("Gétigné", 47.07556, -1.24944), + 44064 => array ("Gorges", 47.10167, -1.30389), + 44065 => array ("Grand-Auverné", 47.59139, -1.33056), + 44066 => array ("Grandchamps-des-Fontaines", 47.3652, -1.6048), + 44067 => array ("Guémené-Penfao", 47.63, -1.8325), + 44068 => array ("Guenrouet", 47.51917, -1.95528), + 44069 => array ("Guérande", 47.32806, -2.42917), + 44070 => array ("La Haie-Fouassière", 47.15556, -1.39917), + 44071 => array ("Haute-Goulaine", 47.19889, -1.42861), + 44072 => array ("Herbignac", 47.44889, -2.3175), + 44073 => array ("Héric", 47.4125, -1.65167), + 44074 => array ("Indre", 47.1987, -1.6705), + 44075 => array ("Issé", 47.62389, -1.45111), + 44076 => array ("Jans", 47.62111, -1.61389), + 44077 => array ("Joué-sur-Erdre", 47.49556, -1.42028), + 44078 => array ("Juigné-des-Moutiers", 47.67889, -1.18528), + 44079 => array ("Le Landreau", 47.205, -1.30556), + 44080 => array ("Lavau-sur-Loire", 47.30694, -1.96694), + 44081 => array ("Legé", 46.88556, -1.59889), + 44082 => array ("Ligné", 47.41167, -1.37722), + 44083 => array ("La Limouzinière", 46.9935, -1.5958), + 44084 => array ("Le Loroux-Bottereau", 47.2383, -1.3483), + 44085 => array ("Louisfert", 47.675, -1.43306), + 44086 => array ("Lusanger", 47.68083, -1.58833), + 44087 => array ("Machecoul", 46.99306, -1.82278), + 44088 => array ("Maisdon-sur-Sèvre", 47.09722, -1.385), + 44089 => array ("Malville", 47.36111, -1.86389), + 44090 => array ("La Marne", 46.99556, -1.74111), + 44091 => array ("Marsac-sur-Don", 47.59556, -1.68028), + 44092 => array ("Massérac", 47.67278, -1.91472), + 44093 => array ("Maumusson", 47.48278, -1.10556), + 44094 => array ("Mauves-sur-Loire", 47.29611, -1.39333), + 44095 => array ("La Meilleraye-de-Bretagne", 47.55972, -1.40139), + 44096 => array ("Mésanger", 47.43306, -1.23139), + 44097 => array ("Mesquer", 47.39944, -2.46), + 44098 => array ("Missillac", 47.48222, -2.15889), + 44099 => array ("Moisdon-la-Rivière", 47.62139, -1.37222), + 44100 => array ("Monnières", 47.13167, -1.35583), + 44101 => array ("La Montagne", 47.19, -1.68389), + 44102 => array ("Montbert", 47.05778, -1.48889), + 44103 => array ("Montoir-de-Bretagne", 47.32833, -2.14917), + 44104 => array ("Montrelais", 47.38889, -0.96639), + 44105 => array ("Mouais", 47.69556, -1.64444), + 44106 => array ("Les Moutiers-en-Retz", 47.0637, -2.0008), + 44107 => array ("Mouzeil", 47.44806, -1.34806), + 44108 => array ("Mouzillon", 47.14139, -1.28222), + 44109 => array ("Nantes", 47.21722, -1.55389), + 44110 => array ("Nort-sur-Erdre", 47.43944, -1.49833), + 44111 => array ("Notre-Dame-des-Landes", 47.38083, -1.71), + 44112 => array ("Noyal-sur-Brutz", 47.77611, -1.34694), + 44113 => array ("Nozay", 47.56444, -1.62528), + 44114 => array ("Orvault", 47.27083, -1.62361), + 44115 => array ("Oudon", 47.34639, -1.28694), + 44116 => array ("Paimbœuf", 47.2875, -2.03167), + 44117 => array ("Le Pallet", 47.13833, -1.33583), + 44118 => array ("Pannecé", 47.48611, -1.23944), + 44119 => array ("Paulx", 46.96278, -1.75472), + 44120 => array ("Le Pellerin", 47.19833, -1.75389), + 44121 => array ("Petit-Auverné", 47.61, -1.29056), + 44122 => array ("Petit-Mars", 47.39417, -1.455), + 44123 => array ("Pierric", 47.68667, -1.73667), + 44124 => array ("Le Pin", 47.58909, -1.15356), + 44125 => array ("Piriac-sur-Mer", 47.38, -2.54611), + 44126 => array ("La Plaine-sur-Mer", 47.1375, -2.18972), + 44127 => array ("La Planche", 47.01639, -1.43528), + 44128 => array ("Plessé", 47.54167, -1.88694), + 44129 => array ("Pontchâteau", 47.43694, -2.08778), + 44130 => array ("Pont-Saint-Martin", 47.12361, -1.58444), + 44131 => array ("Pornic", 47.11556, -2.10333), + 44132 => array ("Pornichet", 47.26583, -2.34), + 44133 => array ("Port-Saint-Père", 47.13306, -1.74833), + 44134 => array ("Pouillé-les-Côteaux", 47.45528, -1.16222), + 44135 => array ("Le Pouliguen", 47.277, -2.4292), + 44136 => array ("Préfailles", 47.12833, -2.215), + 44137 => array ("Prinquiau", 47.36278, -2.01111), + 44138 => array ("Puceul", 47.52194, -1.61694), + 44139 => array ("Quilly", 47.46167, -1.95333), + 44140 => array ("La Regrippière", 47.18167, -1.17611), + 44141 => array ("La Remaudière", 47.2375, -1.24333), + 44142 => array ("Remouillé", 47.05556, -1.37694), + 44143 => array ("Rezé", 47.19056, -1.56917), + 44144 => array ("Riaillé", 47.51889, -1.29556), + 44145 => array ("Rouans", 47.18528, -1.85972), + 44146 => array ("Rougé", 47.78278, -1.44833), + 44147 => array ("La Rouxière", 47.44278, -1.06778), + 44148 => array ("Ruffigné", 47.75694, -1.49361), + 44149 => array ("Saffré", 47.50167, -1.57861), + 44150 => array ("Saint-Aignan-Grandlieu", 47.124, -1.63), + 44151 => array ("Saint-André-des-Eaux", 47.31389, -2.31083), + 44152 => array ("Sainte-Anne-sur-Brivet", 47.4597, -2.0026), + 44153 => array ("Saint-Aubin-des-Châteaux", 47.72, -1.48639), + 44154 => array ("Saint-Brevin-les-Pins", 47.24639, -2.16694), + 44155 => array ("Saint-Colomban", 47.0088, -1.5823), + 44156 => array ("Corcoué-sur-Logne", 46.9656, -1.576), + 44157 => array ("Saint-Étienne-de-Mer-Morte", 46.92972, -1.74278), + 44158 => array ("Saint-Étienne-de-Montluc", 47.27639, -1.78028), + 44159 => array ("Saint-Fiacre-sur-Maine", 47.14333, -1.41667), + 44160 => array ("Saint-Géréon", 47.36917, -1.19833), + 44161 => array ("Saint-Gildas-des-Bois", 47.51639, -2.03833), + 44162 => array ("Saint-Herblain", 47.21139, -1.65111), + 44163 => array ("Saint-Herblon", 47.40833, -1.09583), + 44164 => array ("Saint-Hilaire-de-Chaléons", 47.10306, -1.865), + 44165 => array ("Saint-Hilaire-de-Clisson", 47.06222, -1.30944), + 44166 => array ("Saint-Jean-de-Boiseau", 47.19417, -1.72472), + 44168 => array ("Saint-Joachim", 47.38222, -2.20083), + 44169 => array ("Saint-Julien-de-Concelles", 47.25333, -1.38556), + 44170 => array ("Saint-Julien-de-Vouvantes", 47.64167, -1.23889), + 44171 => array ("Saint-Léger-les-Vignes", 47.13611, -1.73222), + 44172 => array ("Sainte-Luce-sur-Loire", 47.24944, -1.48667), + 44173 => array ("Saint-Lumine-de-Clisson", 47.08361, -1.33694), + 44174 => array ("Saint-Lumine-de-Coutais", 47.05444, -1.72639), + 44175 => array ("Saint-Lyphard", 47.39778, -2.30778), + 44176 => array ("Saint-Malo-de-Guersac", 47.35083, -2.17972), + 44178 => array ("Saint-Mars-de-Coutais", 47.1125, -1.73583), + 44179 => array ("Saint-Mars-du-Désert", 47.36639, -1.40583), + 44180 => array ("Saint-Mars-la-Jaille", 47.52528, -1.18472), + 44181 => array ("Saint-Même-le-Tenu", 47.02056, -1.795), + 44182 => array ("Saint-Michel-Chef-Chef", 47.18083, -2.14861), + 44183 => array ("Saint-Molf", 47.39139, -2.425), + 44184 => array ("Saint-Nazaire", 47.27944, -2.21), + 44185 => array ("Saint-Nicolas-de-Redon", 47.64417, -2.06611), + 44186 => array ("Sainte-Pazanne", 47.10306, -1.81083), + 44187 => array ("Saint-Père-en-Retz", 47.20694, -2.04472), + 44188 => array ("Saint-Philbert-de-Grand-Lieu", 47.035, -1.64), + 44189 => array ("Sainte-Reine-de-Bretagne", 47.44056, -2.19389), + 44190 => array ("Saint-Sébastien-sur-Loire", 47.20766, -1.50337), + 44191 => array ("Saint-Sulpice-des-Landes", 47.5749, -1.2052), + 44192 => array ("Saint-Viaud", 47.25583, -2.01944), + 44193 => array ("Saint-Vincent-des-Landes", 47.65556, -1.49667), + 44194 => array ("Sautron", 47.26361, -1.66833), + 44195 => array ("Savenay", 47.36111, -1.94194), + 44196 => array ("Sévérac", 47.55, -2.07667), + 44197 => array ("Sion-les-Mines", 47.735, -1.59194), + 44198 => array ("Les Sorinières", 47.14639, -1.53), + 44199 => array ("Soudan", 47.7375, -1.30472), + 44200 => array ("Soulvache", 47.82917, -1.47333), + 44201 => array ("Sucé-sur-Erdre", 47.3445, -1.5283), + 44202 => array ("Teillé", 47.4615, -1.279), + 44203 => array ("Le Temple-de-Bretagne", 47.3283, -1.7893), + 44204 => array ("Thouaré-sur-Loire", 47.2675, -1.44056), + 44205 => array ("Les Touches", 47.44132, -1.4296), + 44206 => array ("Touvois", 46.90278, -1.68306), + 44207 => array ("Trans-sur-Erdre", 47.47639, -1.37361), + 44208 => array ("Treffieux", 47.61972, -1.5375), + 44209 => array ("Treillières", 47.33083, -1.62667), + 44210 => array ("Trignac", 47.31806, -2.18917), + 44211 => array ("La Turballe", 47.34556, -2.50667), + 44212 => array ("Vallet", 47.16167, -1.26694), + 44213 => array ("Varades", 47.38444, -1.03), + 44214 => array ("Vay", 47.55472, -1.70111), + 44215 => array ("Vertou", 47.16806, -1.47139), + 44216 => array ("Vieillevigne", 46.97111, -1.43333), + 44217 => array ("Vigneux-de-Bretagne", 47.32611, -1.73833), + 44218 => array ("Villepot", 47.77528, -1.2775), + 44219 => array ("Vritz", 47.58167, -1.06972), + 44220 => array ("Vue", 47.19861, -1.88111), + 44221 => array ("La Chevallerais", 47.4675, -1.67139), + 44222 => array ("La Roche-Blanche", 47.44083, -1.14056), + 44223 => array ("Geneston", 47.0556, -1.5121), + 44224 => array ("La Grigonnais", 47.52667, -1.6675), + 50001 => array ("Acqueville", 49.61126, -1.74961), + 50002 => array ("Agneaux", 49.11549, -1.12991), + 50003 => array ("Agon-Coutainville", 49.03883, -1.58144), + 50004 => array ("Airel", 49.21250, -1.08007), + 50005 => array ("Amfreville", 49.41166, -1.38677), + 50006 => array ("Amigny", 49.15684, -1.18525), + 50007 => array ("Ancteville", 49.10304, -1.45942), + 50008 => array ("Anctoville-sur-Boscq", 48.84613, -1.52840), + 50009 => array ("Angey", 48.75121, -1.48484), + 50010 => array ("Angoville-au-Plain", 49.35001, -1.25501), + 50012 => array ("Angoville-sur-Ay", 49.25249, -1.55151), + 50013 => array ("Anneville-en-Saire", 49.63620, -1.28555), + 50014 => array ("Anneville-sur-Mer", 49.12059, -1.58682), + 50015 => array ("Annoville", 48.96132, -1.53199), + 50016 => array ("Appeville", 49.32704, -1.33590), + 50018 => array ("Argouges", 48.49162, -1.37949), + 50019 => array ("Aucey-la-Plaine", 48.52650, -1.47519), + 50020 => array ("Auderville", 49.70723, -1.93531), + 50021 => array ("Audouville-la-Hubert", 49.41704, -1.22666), + 50022 => array ("Aumeville-Lestre", 49.53899, -1.32249), + 50023 => array ("Auvers", 49.30004, -1.32395), + 50024 => array ("Auxais", 49.21541, -1.30575), + 50025 => array ("Avranches", 48.68847, -1.35732), + 50026 => array ("Azeville", 49.45727, -1.31285), + 50027 => array ("Bacilly", 48.70411, -1.43259), + 50028 => array ("La Baleine", 48.92406, -1.30193), + 50029 => array ("Barenton", 48.60835, -0.83064), + 50030 => array ("Barfleur", 49.66873, -1.26330), + 50031 => array ("Barneville-Carteret", 49.38044, -1.77027), + 50032 => array ("La Barre-de-Semilly", 49.10632, -1.01818), + 50033 => array ("Baubigny", 49.42779, -1.80597), + 50034 => array ("Baudre", 49.08748, -1.06982), + 50035 => array ("Baudreville", 49.30888, -1.62386), + 50036 => array ("Baupte", 49.30984, -1.36772), + 50037 => array ("La Bazoge", 48.64335, -1.03561), + 50038 => array ("Beauchamps", 48.83700, -1.36645), + 50039 => array ("Beaucoudray", 48.96138, -1.14237), + 50040 => array ("Beauficel", 48.74226, -0.96207), + 50041 => array ("Beaumont-Hague", 49.66316, -1.84267), + 50042 => array ("Beauvoir", 48.60363, -1.52825), + 50043 => array ("Bellefontaine", 48.68203, -0.98087), + 50044 => array ("Belval", 49.06075, -1.35521), + 50045 => array ("Benoîtville", 49.52886, -1.77573), + 50046 => array ("Bérigny", 49.13724, -0.94735), + 50048 => array ("Beslon", 48.85822, -1.15769), + 50049 => array ("Besneville", 49.37669, -1.62681), + 50050 => array ("Beuvrigny", 48.96574, -1.00442), + 50051 => array ("Beuzeville-au-Plain", 49.42963, -1.28328), + 50052 => array ("Beuzeville-la-Bastille", 49.35256, -1.36515), + 50054 => array ("Biéville", 49.07365, -0.88908), + 50055 => array ("Biniville", 49.42958, -1.48671), + 50056 => array ("Bion", 48.62505, -0.91848), + 50057 => array ("Biville", 49.61435, -1.81083), + 50058 => array ("Blainville-sur-Mer", 49.07287, -1.57173), + 50059 => array ("Blosville", 49.37184, -1.28832), + 50060 => array ("La Bloutière", 48.87009, -1.24787), + 50061 => array ("Boisroger", 49.09302, -1.53451), + 50062 => array ("Boisyvon", 48.80669, -1.12798), + 50063 => array ("Bolleville", 49.30526, -1.58976), + 50064 => array ("La Bonneville", 49.39179, -1.45932), + 50066 => array ("Jullouville", 48.76345, -1.52729), + 50069 => array ("Bourguenolles", 48.79366, -1.28697), + 50070 => array ("Boutteville", 49.39072, -1.25588), + 50071 => array ("Braffais", 48.75429, -1.27055), + 50072 => array ("Brainville", 49.08245, -1.50021), + 50073 => array ("Branville-Hague", 49.65534, -1.78554), + 50074 => array ("Brécey", 48.72505, -1.17310), + 50075 => array ("Brectouville", 49.01858, -1.02450), + 50076 => array ("Bréhal", 48.89910, -1.53298), + 50077 => array ("Bretteville", 49.65003, -1.50827), + 50078 => array ("Bretteville-sur-Ay", 49.25744, -1.62881), + 50079 => array ("Breuville", 49.53819, -1.66976), + 50080 => array ("Brévands", 49.34013, -1.17110), + 50081 => array ("Bréville-sur-Mer", 48.86964, -1.55887), + 50082 => array ("Bricquebec", 49.46998, -1.63261), + 50083 => array ("Bricquebosq", 49.53572, -1.71763), + 50084 => array ("Bricqueville-la-Blouette", 49.03803, -1.47913), + 50085 => array ("Bricqueville-sur-Mer", 48.91838, -1.51381), + 50086 => array ("Brillevast", 49.62518, -1.41274), + 50087 => array ("Brix", 49.55341, -1.57357), + 50088 => array ("Brouains", 48.72077, -0.96695), + 50089 => array ("Brucheville", 49.35853, -1.20653), + 50090 => array ("Buais", 48.51855, -0.97123), + 50092 => array ("Cambernon", 49.08714, -1.38108), + 50093 => array ("Cametours", 49.07080, -1.28408), + 50094 => array ("Camprond", 49.08580, -1.33862), + 50095 => array ("Canisy", 49.07976, -1.18008), + 50096 => array ("Canteloup", 49.64032, -1.35985), + 50097 => array ("Canville-la-Rocque", 49.34680, -1.63643), + 50098 => array ("Carantilly", 49.06611, -1.24968), + 50099 => array ("Carentan", 49.29632, -1.25592), + 50100 => array ("Carnet", 48.50491, -1.35301), + 50101 => array ("Carneville", 49.65941, -1.44840), + 50102 => array ("Carolles", 48.74903, -1.55955), + 50103 => array ("Carquebut", 49.37444, -1.32327), + 50105 => array ("Catteville", 49.35497, -1.56562), + 50106 => array ("Cavigny", 49.19115, -1.12208), + 50107 => array ("Catz", 49.31271, -1.18665), + 50108 => array ("Céaux", 48.63052, -1.38499), + 50109 => array ("Cérences", 48.91116, -1.43836), + 50110 => array ("Cerisy-la-Forêt", 49.19211, -0.93853), + 50111 => array ("Cerisy-la-Salle", 49.03643, -1.27126), + 50112 => array ("La Chaise-Baudouin", 48.76497, -1.23764), + 50114 => array ("Les Chambres", 48.76425, -1.37929), + 50115 => array ("Champcervon", 48.77205, -1.40097), + 50116 => array ("Champcey", 48.72524, -1.45249), + 50117 => array ("Champeaux", 48.73778, -1.52927), + 50118 => array ("Champrepus", 48.83400, -1.31607), + 50119 => array ("Les Champs-de-Losque", 49.17786, -1.22163), + 50120 => array ("Chanteloup", 48.89295, -1.48259), + 50121 => array ("La Chapelle-Cécelin", 48.80784, -1.16781), + 50123 => array ("La Chapelle-en-Juger", 49.13270, -1.21267), + 50124 => array ("La Chapelle-Urée", 48.66422, -1.14933), + 50125 => array ("Chasseguey", 48.64425, -1.06949), + 50126 => array ("Chavoy", 48.73601, -1.33360), + 50127 => array ("Chef-du-Pont", 49.38373, -1.34658), + 50128 => array ("Le Chefresne", 48.90410, -1.15592), + 50129 => array ("Cherbourg-Octeville", 49.63341, -1.63390), + 50130 => array ("Chérencé-le-Héron", 48.79900, -1.20150), + 50131 => array ("Chérencé-le-Roussel", 48.71122, -0.99936), + 50132 => array ("Les Chéris", 48.63305, -1.24595), + 50133 => array ("Chèvreville", 48.61722, -1.05120), + 50134 => array ("Chevry", 48.97120, -1.11387), + 50135 => array ("Clitourps", 49.65122, -1.37120), + 50136 => array ("Coigny", 49.32219, -1.38816), + 50137 => array ("La Colombe", 48.87557, -1.19526), + 50138 => array ("Colomby", 49.45414, -1.48894), + 50139 => array ("Condé-sur-Vire", 49.05405, -1.02453), + 50140 => array ("Contrières", 48.98622, -1.42923), + 50142 => array ("Cosqueville", 49.69150, -1.40009), + 50143 => array ("Coudeville-sur-Mer", 48.87352, -1.52424), + 50144 => array ("Coulouvray-Boisbenâtre", 48.78210, -1.10464), + 50145 => array ("Courcy", 49.05093, -1.38869), + 50146 => array ("Courtils", 48.62811, -1.42282), + 50147 => array ("Coutances", 49.05661, -1.44345), + 50148 => array ("Couvains", 49.16194, -1.00825), + 50149 => array ("Couville", 49.55837, -1.68158), + 50150 => array ("Crasville", 49.55419, -1.33206), + 50151 => array ("Créances", 49.19999, -1.56218), + 50152 => array ("Les Cresnays", 48.70906, -1.11382), + 50153 => array ("Cretteville", 49.34156, -1.39364), + 50154 => array ("La Croix-Avranchin", 48.55026, -1.38007), + 50155 => array ("Crollon", 48.58240, -1.38068), + 50156 => array ("Crosville-sur-Douve", 49.38547, -1.47607), + 50158 => array ("Cuves", 48.72926, -1.09806), + 50159 => array ("Dangy", 49.02922, -1.22193), + 50160 => array ("Denneville", 49.31469, -1.66265), + 50161 => array ("Le Dézert", 49.20883, -1.16303), + 50162 => array ("Digosville", 49.62888, -1.52811), + 50163 => array ("Digulleville", 49.69105, -1.85876), + 50164 => array ("Domjean", 48.99079, -1.03423), + 50165 => array ("Donville-les-Bains", 48.85084, -1.57315), + 50166 => array ("Doville", 49.33702, -1.54822), + 50167 => array ("Dragey-Ronthon", 48.71669, -1.48832), + 50168 => array ("Ducey", 48.61296, -1.27873), + 50169 => array ("écausseville", 49.45972, -1.38260), + 50170 => array ("écoquenéauville", 49.40011, -1.29251), + 50171 => array ("éculleville", 49.68260, -1.82441), + 50172 => array ("émondeville", 49.46030, -1.33759), + 50173 => array ("équeurdreville-Hainneville", 49.64414, -1.67828), + 50174 => array ("équilly", 48.83311, -1.38244), + 50175 => array ("éroudeville", 49.47424, -1.39648), + 50176 => array ("L'étang-Bertrand", 49.47245, -1.57060), + 50177 => array ("étienville", 49.38542, -1.43648), + 50178 => array ("Fermanville", 49.68108, -1.45453), + 50179 => array ("Ferrières", 48.54544, -0.95497), + 50180 => array ("Fervaches", 48.99477, -1.07982), + 50181 => array ("Feugères", 49.14877, -1.31727), + 50182 => array ("La Feuillie", 49.17017, -1.49237), + 50183 => array ("Fierville-les-Mines", 49.39547, -1.66642), + 50184 => array ("Flamanville", 49.53058, -1.86809), + 50185 => array ("Fleury", 48.84889, -1.26431), + 50186 => array ("Flottemanville", 49.47626, -1.45169), + 50187 => array ("Flottemanville-Hague", 49.61834, -1.71707), + 50188 => array ("Folligny", 48.82023, -1.41781), + 50189 => array ("Fontenay", 48.62535, -1.02073), + 50190 => array ("Fontenay-sur-Mer", 49.49353, -1.30873), + 50191 => array ("Foucarville", 49.44695, -1.25589), + 50192 => array ("Fourneaux", 48.96643, -1.04146), + 50193 => array ("Le Fresne-Poret", 48.70827, -0.82086), + 50194 => array ("Fresville", 49.44139, -1.35332), + 50195 => array ("Gathemo", 48.76938, -0.96398), + 50196 => array ("Gatteville-le-Phare", 49.67965, -1.29047), + 50197 => array ("Gavray", 48.90186, -1.32927), + 50198 => array ("Geffosses", 49.12933, -1.55102), + 50199 => array ("Genêts", 48.68916, -1.47354), + 50200 => array ("Ger", 48.67184, -0.80295), + 50202 => array ("Giéville", 49.01354, -0.98461), + 50203 => array ("La Glacerie", 49.60769, -1.58562), + 50204 => array ("Glatigny", 49.27179, -1.63652), + 50205 => array ("La Godefroy", 48.69059, -1.28421), + 50206 => array ("La Gohannière", 48.70133, -1.26089), + 50207 => array ("Golleville", 49.43870, -1.51369), + 50208 => array ("Gonfreville", 49.23103, -1.40286), + 50209 => array ("Gonneville", 49.63193, -1.47264), + 50210 => array ("Gorges", 49.26442, -1.38981), + 50211 => array ("Gouberville", 49.68875, -1.32170), + 50212 => array ("Gourbesville", 49.42269, -1.40960), + 50213 => array ("Gourfaleur", 49.07129, -1.10821), + 50214 => array ("Gouvets", 48.92852, -1.09363), + 50215 => array ("Gouville-sur-Mer", 49.09794, -1.57923), + 50216 => array ("Graignes-Mesnil-Angot", 49.23801, -1.20015), + 50217 => array ("Le Grand-Celland", 48.68820, -1.17301), + 50218 => array ("Granville", 48.83270, -1.56670), + 50219 => array ("Gratot", 49.06831, -1.49454), + 50220 => array ("Gréville-Hague", 49.67262, -1.79920), + 50221 => array ("Grimesnil", 48.95491, -1.35072), + 50222 => array ("Grosville", 49.50706, -1.73298), + 50223 => array ("Guéhébert", 48.96687, -1.37072), + 50224 => array ("Guilberville", 48.98667, -0.94958), + 50225 => array ("Le Guislain", 48.97168, -1.21557), + 50227 => array ("Le Ham", 49.44995, -1.40982), + 50228 => array ("Hambye", 48.94743, -1.25972), + 50229 => array ("Hamelin", 48.54292, -1.22248), + 50230 => array ("Hardinvast", 49.57669, -1.64595), + 50231 => array ("Hauteville-sur-Mer", 48.97411, -1.54436), + 50232 => array ("Hauteville-la-Guichard", 49.12441, -1.30305), + 50233 => array ("Hautteville-Bocage", 49.42829, -1.46687), + 50234 => array ("La Haye-Bellefond", 48.98083, -1.18892), + 50235 => array ("La Haye-d'Ectot", 49.39368, -1.73016), + 50236 => array ("La Haye-du-Puits", 49.28351, -1.53943), + 50237 => array ("La Haye-Pesnel", 48.80972, -1.37493), + 50238 => array ("Héauville", 49.57536, -1.80249), + 50239 => array ("Hébécrevon", 49.12217, -1.17047), + 50240 => array ("Helleville", 49.55801, -1.78437), + 50241 => array ("Hémevez", 49.46239, -1.43528), + 50242 => array ("Herqueville", 49.66736, -1.88085), + 50243 => array ("Heugueville-sur-Sienne", 49.03793, -1.51183), + 50244 => array ("Hérenguerville", 48.97484, -1.50155), + 50245 => array ("Heussé", 48.51327, -0.90329), + 50246 => array ("Hiesville", 49.37112, -1.25909), + 50247 => array ("Hocquigny", 48.80852, -1.40197), + 50248 => array ("Le Hommet-d'Arthenay", 49.19011, -1.19499), + 50249 => array ("Houesville", 49.35165, -1.29451), + 50250 => array ("Houtteville", 49.34137, -1.35673), + 50251 => array ("Huberville", 49.50934, -1.43254), + 50252 => array ("Hudimesnil", 48.87002, -1.46798), + 50253 => array ("Huisnes-sur-Mer", 48.61242, -1.44918), + 50254 => array ("Husson", 48.58271, -0.90628), + 50255 => array ("Hyenville", 48.98979, -1.48502), + 50256 => array ("Isigny-le-Buat", 48.62149, -1.18298), + 50257 => array ("Jobourg", 49.68069, -1.91434), + 50258 => array ("Joganville", 49.47156, -1.35154), + 50259 => array ("Juilley", 48.59090, -1.34729), + 50260 => array ("Juvigny-le-Tertre", 48.67857, -1.04281), + 50261 => array ("Lamberville", 49.07902, -0.91672), + 50262 => array ("La Lande-d'Airou", 48.81910, -1.28348), + 50263 => array ("Lapenty", 48.56893, -1.02322), + 50265 => array ("Laulne", 49.25063, -1.45537), + 50266 => array ("Lengronne", 48.93381, -1.37830), + 50267 => array ("Lessay", 49.21671, -1.52478), + 50268 => array ("Lestre", 49.52561, -1.32237), + 50269 => array ("Liesville-sur-Douve", 49.35268, -1.32225), + 50270 => array ("Lieusaint", 49.48361, -1.47249), + 50271 => array ("Lingeard", 48.73814, -1.02635), + 50272 => array ("Lingreville", 48.94328, -1.52855), + 50273 => array ("Lithaire", 49.29640, -1.48893), + 50274 => array ("Les Loges-Marchis", 48.53425, -1.08560), + 50275 => array ("Les Loges-sur-Brécey", 48.75667, -1.16089), + 50276 => array ("Lolif", 48.73170, -1.39448), + 50277 => array ("Longueville", 48.85856, -1.53528), + 50278 => array ("Le Loreur", 48.86409, -1.42396), + 50279 => array ("Le Lorey", 49.09774, -1.30007), + 50280 => array ("Lozon", 49.14565, -1.27192), + 50281 => array ("La Lucerne-d'Outremer", 48.78839, -1.40995), + 50282 => array ("Le Luot", 48.76615, -1.32946), + 50283 => array ("La Luzerne", 49.14139, -1.04606), + 50284 => array ("Macey", 48.56901, -1.42615), + 50285 => array ("Magneville", 49.45286, -1.54841), + 50287 => array ("La Mancellière-sur-Vire", 49.06165, -1.08127), + 50288 => array ("Marcey-les-Grèves", 48.70010, -1.38897), + 50289 => array ("Marchésieux", 49.18860, -1.30728), + 50290 => array ("Marcilly", 48.65399, -1.24420), + 50291 => array ("Margueray", 48.90031, -1.12635), + 50292 => array ("Marigny", 49.10421, -1.25220), + 50293 => array ("Martigny", 48.61509, -1.10412), + 50294 => array ("Martinvast", 49.59818, -1.64849), + 50295 => array ("Maupertuis", 48.95518, -1.20001), + 50296 => array ("Maupertus-sur-Mer", 49.65687, -1.48350), + 50297 => array ("La Meauffe", 49.16915, -1.09998), + 50298 => array ("Méautis", 49.28052, -1.29573), + 50299 => array ("Le Mesnil", 49.37192, -1.69334), + 50300 => array ("Le Mesnil-Adelée", 48.70026, -1.06280), + 50301 => array ("Le Mesnil-Amand", 48.87738, -1.35994), + 50302 => array ("Le Mesnil-Amey", 49.10564, -1.21160), + 50304 => array ("Le Mesnil-Aubert", 48.94445, -1.41515), + 50305 => array ("Le Mesnil-au-Val", 49.59838, -1.52768), + 50308 => array ("Le Mesnilbus", 49.13468, -1.35267), + 50310 => array ("Le Mesnil-Eury", 49.16181, -1.23538), + 50311 => array ("Le Mesnil-Garnier", 48.86549, -1.30454), + 50312 => array ("Le Mesnil-Gilbert", 48.72216, -1.05690), + 50313 => array ("Le Mesnil-Herman", 49.02061, -1.14456), + 50315 => array ("Le Mesnillard", 48.63100, -1.08070), + 50316 => array ("Le Mesnil-Opac", 49.01827, -1.11433), + 50317 => array ("Le Mesnil-Ozenne", 48.66750, -1.21767), + 50318 => array ("Le Mesnil-Rainfray", 48.66207, -1.04831), + 50319 => array ("Le Mesnil-Raoult", 49.03432, -1.05969), + 50320 => array ("Le Mesnil-Rogues", 48.85950, -1.37785), + 50321 => array ("Le Mesnil-Rouxelin", 49.14233, -1.07833), + 50323 => array ("Le Mesnil-Tôve", 48.69264, -1.01584), + 50324 => array ("Le Mesnil-Véneron", 49.22592, -1.16299), + 50325 => array ("Le Mesnil-Vigot", 49.16153, -1.27754), + 50326 => array ("Le Mesnil-Villeman", 48.85635, -1.34036), + 50327 => array ("La Meurdraquière", 48.86077, -1.40495), + 50328 => array ("Millières", 49.19067, -1.45514), + 50329 => array ("Milly", 48.59805, -1.02960), + 50330 => array ("Mobecq", 49.26961, -1.52073), + 50332 => array ("Les Moitiers-d'Allonne", 49.40320, -1.78595), + 50333 => array ("Les Moitiers-en-Bauptois", 49.35990, -1.43217), + 50334 => array ("Montabot", 48.93675, -1.13220), + 50335 => array ("Montaigu-la-Brisette", 49.56723, -1.42035), + 50336 => array ("Montaigu-les-Bois", 48.89111, -1.27021), + 50337 => array ("Montanel", 48.48155, -1.40890), + 50338 => array ("Montbray", 48.88308, -1.10930), + 50339 => array ("Montchaton", 49.00599, -1.49812), + 50340 => array ("Montcuit", 49.11581, -1.34654), + 50341 => array ("Montebourg", 49.49735, -1.38713), + 50342 => array ("Montfarville", 49.65566, -1.26338), + 50343 => array ("Montgardon", 49.27387, -1.58021), + 50345 => array ("Monthuchon", 49.08817, -1.42023), + 50347 => array ("Montjoie-Saint-Martin", 48.52573, -1.28858), + 50348 => array ("Montmartin-en-Graignes", 49.26744, -1.16552), + 50349 => array ("Montmartin-sur-Mer", 48.99102, -1.52994), + 50350 => array ("Montpinchon", 49.02633, -1.32027), + 50351 => array ("Montrabot", 49.10788, -0.89659), + 50352 => array ("Montreuil-sur-Lozon", 49.13339, -1.24331), + 50353 => array ("Le Mont-Saint-Michel", 48.62221, -1.53309), + 50354 => array ("Montsurvent", 49.10826, -1.51110), + 50355 => array ("Montviron", 48.74531, -1.41897), + 50356 => array ("Moon-sur-Elle", 49.20374, -1.05390), + 50357 => array ("Morigny", 48.88141, -1.06958), + 50358 => array ("Morsalines", 49.57006, -1.31780), + 50359 => array ("Mortain", 48.64748, -0.93172), + 50360 => array ("Morville", 49.47440, -1.51700), + 50361 => array ("La Mouche", 48.78488, -1.35273), + 50362 => array ("Moulines", 48.54850, -1.04555), + 50363 => array ("Moyon", 48.99412, -1.13665), + 50364 => array ("Muneville-le-Bingard", 49.13676, -1.47609), + 50365 => array ("Muneville-sur-Mer", 48.93648, -1.48702), + 50368 => array ("Nay", 49.24184, -1.37167), + 50369 => array ("Négreville", 49.50390, -1.55157), + 50370 => array ("Néhou", 49.42623, -1.56321), + 50371 => array ("Le Neufbourg", 48.66301, -0.94743), + 50372 => array ("Neufmesnil", 49.31093, -1.52894), + 50373 => array ("Neuville-au-Plain", 49.42417, -1.33468), + 50374 => array ("Neuville-en-Beaumont", 49.35025, -1.59768), + 50375 => array ("Néville-sur-Mer", 49.69159, -1.33947), + 50376 => array ("Nicorps", 49.03136, -1.41066), + 50378 => array ("Notre-Dame-de-Cenilly", 48.99824, -1.25757), + 50379 => array ("Notre-Dame-de-Livoye", 48.74716, -1.19991), + 50380 => array ("Notre-Dame-d'Elle", 49.11078, -0.95441), + 50381 => array ("Notre-Dame-du-Touchet", 48.58665, -0.95630), + 50382 => array ("Nouainville", 49.62001, -1.68096), + 50384 => array ("Octeville-l'Avenel", 49.54680, -1.36608), + 50385 => array ("Omonville-la-Petite", 49.69299, -1.88472), + 50386 => array ("Omonville-la-Rogue", 49.69683, -1.84105), + 50387 => array ("Orglandes", 49.42407, -1.44023), + 50388 => array ("Orval", 49.01142, -1.46437), + 50389 => array ("Ouville", 49.02074, -1.36684), + 50390 => array ("Ozeville", 49.50396, -1.34095), + 50391 => array ("Parigny", 48.59374, -1.08234), + 50393 => array ("Percy", 48.91746, -1.20524), + 50394 => array ("Périers", 49.19180, -1.41249), + 50395 => array ("La Pernelle", 49.61318, -1.30916), + 50396 => array ("Les Perques", 49.44850, -1.65942), + 50397 => array ("Perriers-en-Beauficel", 48.73616, -0.99961), + 50398 => array ("Le Perron", 49.05562, -0.89307), + 50399 => array ("Le Petit-Celland", 48.69765, -1.21523), + 50400 => array ("Picauville", 49.37946, -1.38778), + 50401 => array ("Pierreville", 49.46742, -1.77803), + 50402 => array ("Les Pieux", 49.51030, -1.82036), + 50403 => array ("Pirou", 49.16466, -1.55903), + 50404 => array ("Placy-Montaigu", 49.03098, -0.89338), + 50405 => array ("Le Plessis-Lastelle", 49.27916, -1.43510), + 50406 => array ("Plomb", 48.73663, -1.30896), + 50407 => array ("Poilley", 48.61792, -1.32049), + 50408 => array ("Pontaubault", 48.62494, -1.35394), + 50409 => array ("Pont-Hébert", 49.16345, -1.15107), + 50410 => array ("Pontorson", 48.56897, -1.48653), + 50411 => array ("Ponts", 48.71128, -1.33485), + 50412 => array ("Portbail", 49.35203, -1.69541), + 50413 => array ("Précey", 48.60570, -1.37641), + 50414 => array ("Précorbin", 49.07993, -0.96360), + 50415 => array ("Prétot-Sainte-Suzanne", 49.31844, -1.44820), + 50416 => array ("Querqueville", 49.65895, -1.70909), + 50417 => array ("Quettehou", 49.58839, -1.32408), + 50418 => array ("Quettetot", 49.48686, -1.69704), + 50419 => array ("Quettreville-sur-Sienne", 48.96184, -1.46914), + 50420 => array ("Quibou", 49.07189, -1.21322), + 50421 => array ("Quinéville", 49.51089, -1.30534), + 50422 => array ("Raids", 49.20793, -1.34553), + 50423 => array ("Rampan", 49.14645, -1.13114), + 50425 => array ("Rauville-la-Bigot", 49.51746, -1.66546), + 50426 => array ("Rauville-la-Place", 49.39051, -1.50155), + 50427 => array ("Ravenoville", 49.45633, -1.27348), + 50428 => array ("Reffuveille", 48.67736, -1.11165), + 50429 => array ("Regnéville-sur-Mer", 49.01574, -1.53843), + 50430 => array ("Reigneville-Bocage", 49.41095, -1.47453), + 50431 => array ("Remilly-sur-Lozon", 49.17971, -1.26085), + 50432 => array ("Réthoville", 49.69378, -1.35953), + 50433 => array ("Réville", 49.62613, -1.25142), + 50434 => array ("La Rochelle-Normande", 48.76410, -1.43534), + 50435 => array ("Rocheville", 49.50014, -1.59313), + 50436 => array ("Romagny", 48.63743, -0.98072), + 50437 => array ("Roncey", 48.98352, -1.33024), + 50438 => array ("La Ronde-Haye", 49.12686, -1.44258), + 50440 => array ("Rouffigny", 48.80875, -1.24387), + 50441 => array ("Rouxeville", 49.09259, -0.94088), + 50442 => array ("Le Rozel", 49.48148, -1.82868), + 50443 => array ("Sacey", 48.49957, -1.45950), + 50444 => array ("Saint-Amand", 49.04449, -0.94847), + 50445 => array ("Saint-André-de-Bohon", 49.23897, -1.24797), + 50446 => array ("Saint-André-de-l'épine", 49.13375, -1.02272), + 50447 => array ("Saint-Aubin-des-Préaux", 48.80640, -1.50085), + 50448 => array ("Saint-Aubin-de-Terregatte", 48.57032, -1.28722), + 50449 => array ("Saint-Aubin-du-Perron", 49.15314, -1.37994), + 50450 => array ("Saint-Barthélemy", 48.68425, -0.95065), + 50451 => array ("Saint-Brice", 48.69914, -1.29775), + 50452 => array ("Saint-Brice-de-Landelles", 48.54120, -1.13243), + 50453 => array ("Sainte-Cécile", 48.83351, -1.18922), + 50454 => array ("Saint-Christophe-du-Foc", 49.55465, -1.73523), + 50455 => array ("Saint-Clair-sur-l'Elle", 49.18518, -1.04046), + 50456 => array ("Saint-Clément-Rancoudray", 48.66896, -0.89038), + 50457 => array ("Sainte-Colombe", 49.41514, -1.51172), + 50458 => array ("Saint-Côme-du-Mont", 49.33178, -1.26025), + 50460 => array ("Sainte-Croix-Hague", 49.64262, -1.76503), + 50461 => array ("Saint-Cyr", 49.48691, -1.41571), + 50462 => array ("Saint-Cyr-du-Bailleul", 48.56788, -0.82677), + 50463 => array ("Saint-Denis-le-Gast", 48.94386, -1.32723), + 50464 => array ("Saint-Denis-le-Vêtu", 48.99327, -1.37792), + 50465 => array ("Saint-ébremond-de-Bonfossé", 49.07269, -1.14523), + 50467 => array ("Saint-Floxel", 49.48858, -1.35724), + 50468 => array ("Saint-Fromond", 49.22576, -1.10862), + 50469 => array ("Sainte-Geneviève", 49.65959, -1.30696), + 50470 => array ("Saint-Georges-de-Bohon", 49.25657, -1.25960), + 50471 => array ("Saint-Georges-de-la-Rivière", 49.36301, -1.72971), + 50472 => array ("Saint-Georges-de-Livoye", 48.73726, -1.22270), + 50473 => array ("Saint-Georges-d'Elle", 49.14772, -0.97809), + 50474 => array ("Saint-Georges-de-Rouelley", 48.61496, -0.77536), + 50475 => array ("Saint-Georges-Montcocq", 49.13511, -1.10954), + 50476 => array ("Saint-Germain-d'Elle", 49.11977, -0.92085), + 50477 => array ("Saint-Germain-des-Vaux", 49.71068, -1.91336), + 50478 => array ("Saint-Germain-de-Tournebut", 49.52745, -1.40278), + 50479 => array ("Saint-Germain-de-Varreville", 49.43910, -1.24467), + 50480 => array ("Saint-Germain-le-Gaillard", 49.48909, -1.77283), + 50481 => array ("Saint-Germain-sur-Ay", 49.23764, -1.60885), + 50482 => array ("Saint-Germain-sur-Sèves", 49.22003, -1.37670), + 50483 => array ("Saint-Gilles", 49.09694, -1.16151), + 50484 => array ("Saint-Hilaire-du-Harcouët", 48.57205, -1.08283), + 50485 => array ("Saint-Hilaire-Petitville", 49.30359, -1.21788), + 50486 => array ("Saint-Jacques-de-Néhou", 49.42013, -1.61663), + 50487 => array ("Saint-James", 48.52574, -1.32634), + 50488 => array ("Saint-Jean-de-Daye", 49.23173, -1.13630), + 50489 => array ("Saint-Jean-de-la-Haize", 48.71984, -1.35962), + 50490 => array ("Saint-Jean-de-la-Rivière", 49.36876, -1.73964), + 50491 => array ("Saint-Jean-de-Savigny", 49.18868, -1.00370), + 50492 => array ("Saint-Jean-des-Baisants", 49.09360, -0.99121), + 50493 => array ("Saint-Jean-des-Champs", 48.82404, -1.46346), + 50494 => array ("Saint-Jean-du-Corail", 48.61635, -0.88902), + 50495 => array ("Saint-Jean-du-Corail-des-Bois", 48.77213, -1.20394), + 50496 => array ("Saint-Jean-le-Thomas", 48.72690, -1.51493), + 50497 => array ("Saint-Jores", 49.30264, -1.40297), + 50498 => array ("Saint-Joseph", 49.53178, -1.52353), + 50499 => array ("Saint-Laurent-de-Cuves", 48.74944, -1.11520), + 50500 => array ("Saint-Laurent-de-Terregatte", 48.57075, -1.24632), + 50502 => array ("Saint-Lô", 49.10996, -1.07755), + 50503 => array ("Saint-Lô-d'Ourville", 49.33178, -1.67378), + 50504 => array ("Saint-Louet-sur-Vire", 48.99405, -0.98719), + 50505 => array ("Saint-Loup", 48.66573, -1.30380), + 50506 => array ("Saint-Malo-de-la-Lande", 49.06305, -1.54261), + 50507 => array ("Saint-Marcouf", 49.47831, -1.28643), + 50508 => array ("Sainte-Marie-du-Bois", 48.55160, -0.89561), + 50509 => array ("Sainte-Marie-du-Mont", 49.39507, -1.20388), + 50510 => array ("Saint-Martin-d'Aubigny", 49.16865, -1.35248), + 50511 => array ("Saint-Martin-d'Audouville", 49.52900, -1.36946), + 50512 => array ("Saint-Martin-de-Bonfossé", 49.04168, -1.17448), + 50513 => array ("Saint-Martin-de-Cenilly", 48.97749, -1.28613), + 50514 => array ("Chaulieu", 48.74756, -0.86091), + 50515 => array ("Saint-Martin-de-Landelles", 48.55328, -1.18372), + 50516 => array ("Saint-Martin-des-Champs", 48.66682, -1.33678), + 50517 => array ("Saint-Martin-de-Varreville", 49.42899, -1.23371), + 50518 => array ("Saint-Martin-le-Bouillant", 48.78003, -1.15914), + 50519 => array ("Saint-Martin-le-Gréard", 49.55310, -1.64677), + 50520 => array ("Saint-Martin-le-Hébert", 49.50740, -1.61429), + 50521 => array ("Saint-Maur-des-Bois", 48.81682, -1.15153), + 50522 => array ("Saint-Maurice-en-Cotentin", 49.39487, -1.70004), + 50523 => array ("Sainte-Mère-église", 49.40886, -1.32366), + 50524 => array ("Saint-Michel-de-la-Pierre", 49.13429, -1.38239), + 50525 => array ("Saint-Michel-de-Montjoie", 48.76438, -1.02574), + 50528 => array ("Saint-Nicolas-de-Pierrepont", 49.32047, -1.58266), + 50529 => array ("Saint-Nicolas-des-Bois", 48.76157, -1.19249), + 50531 => array ("Saint-Ovin", 48.67923, -1.24401), + 50532 => array ("Saint-Pair-sur-Mer", 48.80237, -1.54696), + 50533 => array ("Saint-Patrice-de-Claids", 49.22756, -1.44033), + 50534 => array ("Saint-Pellerin", 49.29530, -1.18923), + 50535 => array ("Sainte-Pience", 48.77131, -1.29851), + 50536 => array ("Saint-Pierre-d'Arthéglise", 49.41736, -1.68504), + 50537 => array ("Saint-Pierre-de-Coutances", 49.03957, -1.43513), + 50538 => array ("Saint-Pierre-de-Semilly", 49.12217, -0.99713), + 50539 => array ("Saint-Pierre-église", 49.66971, -1.39807), + 50540 => array ("Saint-Pierre-Langers", 48.78430, -1.48878), + 50541 => array ("Saint-Planchers", 48.83088, -1.50561), + 50542 => array ("Saint-Pois", 48.74769, -1.05750), + 50543 => array ("Saint-Quentin-sur-le-Homme", 48.64522, -1.30041), + 50544 => array ("Saint-Rémy-des-Landes", 49.29761, -1.65790), + 50545 => array ("Saint-Romphaire", 49.03991, -1.09243), + 50546 => array ("Saint-Samson-de-Bonfossé", 49.04980, -1.13101), + 50548 => array ("Saint-Sauveur-de-Pierrepont", 49.33532, -1.60977), + 50549 => array ("Saint-Sauveur-la-Pommeraye", 48.84005, -1.43412), + 50550 => array ("Saint-Sauveur-Lendelin", 49.12381, -1.40639), + 50551 => array ("Saint-Sauveur-le-Vicomte", 49.37989, -1.54758), + 50552 => array ("Saint-Sébastien-de-Raids", 49.19530, -1.37598), + 50553 => array ("Saint-Senier-de-Beuvron", 48.55852, -1.32041), + 50554 => array ("Saint-Senier-sous-Avranches", 48.68706, -1.31913), + 50556 => array ("Sainte-Suzanne-sur-Vire", 49.07331, -1.05714), + 50557 => array ("Saint-Symphorien-des-Monts", 48.54456, -0.98622), + 50558 => array ("Saint-Symphorien-le-Valois", 49.29641, -1.56257), + 50562 => array ("Saint-Vaast-la-Hougue", 49.59935, -1.27306), + 50563 => array ("Saint-Vigor-des-Monts", 48.90967, -1.05669), + 50564 => array ("Sainteny", 49.24390, -1.32743), + 50565 => array ("Sartilly", 48.75402, -1.46351), + 50567 => array ("Saussemesnil", 49.57288, -1.48944), + 50568 => array ("Saussey", 49.01151, -1.42526), + 50569 => array ("Savigny", 49.05917, -1.32787), + 50570 => array ("Savigny-le-Vieux", 48.51955, -1.02472), + 50571 => array ("Sébeville", 49.38660, -1.28424), + 50572 => array ("Sénoville", 49.42923, -1.77035), + 50573 => array ("Servigny", 49.09112, -1.48469), + 50574 => array ("Servon", 48.60282, -1.41113), + 50575 => array ("Sideville", 49.59317, -1.68646), + 50576 => array ("Siouville-Hague", 49.55996, -1.83086), + 50577 => array ("Sortosville-en-Beaumont", 49.42450, -1.70971), + 50578 => array ("Sortosville", 49.48174, -1.42985), + 50579 => array ("Sottevast", 49.52215, -1.60619), + 50580 => array ("Sotteville", 49.53935, -1.75245), + 50581 => array ("Soulles", 49.00934, -1.18833), + 50582 => array ("Sourdeval", 48.71344, -0.89188), + 50583 => array ("Sourdeval-les-Bois", 48.91207, -1.27685), + 50584 => array ("Subligny", 48.75300, -1.35897), + 50585 => array ("Surtainville", 49.45295, -1.79283), + 50586 => array ("Surville", 49.28291, -1.63994), + 50587 => array ("Taillepied", 49.36226, -1.58574), + 50588 => array ("Tamerville", 49.54199, -1.46098), + 50589 => array ("Tanis", 48.58471, -1.44326), + 50590 => array ("Le Tanu", 48.80420, -1.33539), + 50591 => array ("Le Teilleul", 48.54125, -0.87841), + 50592 => array ("Tessy-sur-Vire", 48.96333, -1.08464), + 50593 => array ("Teurthéville-Bocage", 49.59244, -1.39477), + 50594 => array ("Teurthéville-Hague", 49.58330, -1.73831), + 50595 => array ("Le Theil", 49.60339, -1.46606), + 50596 => array ("Théville", 49.64822, -1.41776), + 50597 => array ("Tirepied", 48.72393, -1.27353), + 50598 => array ("Tocqueville", 49.67119, -1.33613), + 50599 => array ("Tollevast", 49.57902, -1.60738), + 50600 => array ("Tonneville", 49.64137, -1.71941), + 50601 => array ("Torigni-sur-Vire", 49.03198, -0.98281), + 50602 => array ("Tourlaville", 49.63908, -1.56916), + 50603 => array ("Tourville-sur-Sienne", 49.05081, -1.52656), + 50604 => array ("Tréauville", 49.53795, -1.82387), + 50605 => array ("Trelly", 48.96063, -1.41757), + 50606 => array ("Tribehou", 49.20913, -1.24592), + 50607 => array ("La Trinité", 48.78828, -1.24631), + 50608 => array ("Troisgots", 49.01803, -1.07203), + 50609 => array ("Turqueville", 49.40521, -1.26997), + 50610 => array ("Urville", 49.44925, -1.44567), + 50611 => array ("Urville-Nacqueville", 49.66727, -1.74465), + 50612 => array ("Vains", 48.67600, -1.42560), + 50613 => array ("Valcanville", 49.64801, -1.32179), + 50614 => array ("Le Valdécie", 49.42240, -1.65913), + 50615 => array ("Valognes", 49.51282, -1.47383), + 50616 => array ("Le Val-Saint-Père", 48.66157, -1.37225), + 50617 => array ("Varenguebec", 49.33750, -1.49183), + 50618 => array ("Varouville", 49.67130, -1.37126), + 50619 => array ("Le Vast", 49.61812, -1.35780), + 50620 => array ("Vasteville", 49.59900, -1.79186), + 50621 => array ("Vaudreville", 49.51689, -1.35870), + 50622 => array ("Vaudrimesnil", 49.16106, -1.41852), + 50623 => array ("Vauville", 49.63722, -1.82536), + 50624 => array ("La Vendelée", 49.08335, -1.45799), + 50625 => array ("Vengeons", 48.75169, -0.91973), + 50626 => array ("Ver", 48.89558, -1.39039), + 50627 => array ("Vergoncey", 48.56819, -1.38661), + 50628 => array ("Vernix", 48.71517, -1.22832), + 50629 => array ("Vesly", 49.24644, -1.49007), + 50630 => array ("Vessey", 48.52855, -1.42666), + 50631 => array ("Les Veys", 49.32008, -1.14460), + 50633 => array ("Le Vicel", 49.63256, -1.32055), + 50634 => array ("Videcosville", 49.56687, -1.37148), + 50635 => array ("Vidouville", 49.09594, -0.89844), + 50636 => array ("Vierville", 49.35180, -1.23399), + 50637 => array ("Villebaudon", 48.96496, -1.17120), + 50638 => array ("Villechien", 48.57300, -0.98524), + 50639 => array ("Villedieu-les-Poêles", 48.83441, -1.22248), + 50640 => array ("Villiers-le-Pré", 48.53285, -1.38845), + 50641 => array ("Villiers-Fossard", 49.16138, -1.06108), + 50642 => array ("Vindefontaine", 49.34139, -1.42827), + 50643 => array ("Virandeville", 49.56930, -1.71504), + 50644 => array ("Virey", 48.58176, -1.14231), + 50646 => array ("Le Vrétot", 49.45139, -1.71372), + 50647 => array ("Yquelon", 48.84441, -1.55122), + 50648 => array ("Yvetot-Bocage", 49.49605, -1.51043), + 56001 => array ("Allaire", 47.63694, -2.16278), + 56002 => array ("Ambon", 47.55389, -2.55639), + 56003 => array ("Arradon", 47.62556, -2.82333), + 56004 => array ("Arzal", 47.51667, -2.37611), + 56005 => array ("Arzon", 47.54806, -2.89139), + 56006 => array ("Augan", 47.91972, -2.27806), + 56007 => array ("Auray", 47.66778, -2.9825), + 56008 => array ("Baden", 47.61861, -2.92028), + 56009 => array ("Bangor", 47.315, -3.18861), + 56010 => array ("Baud", 47.87556, -3.01889), + 56011 => array ("Béganne", 47.59639, -2.24), + 56012 => array ("Beignon", 47.97083, -2.17111), + 56013 => array ("Belz", 47.67583, -3.17), + 56014 => array ("Berné", 47.995, -3.3925), + 56015 => array ("Berric", 47.63194, -2.52556), + 56016 => array ("Bieuzy", 47.9826, -3.0637), + 56017 => array ("Bignan", 47.87917, -2.77444), + 56018 => array ("Billiers", 47.53167, -2.48944), + 56019 => array ("Billio", 47.86806, -2.63278), + 56020 => array ("Bohal", 47.78139, -2.43722), + 56021 => array ("Brandérion", 47.7936, -3.1942), + 56022 => array ("Brandivy", 47.77389, -2.94528), + 56023 => array ("Brech", 47.72056, -2.99556), + 56024 => array ("Bréhan", 48.0609, -2.6866), + 56025 => array ("Brignac", 48.12056, -2.38833), + 56026 => array ("Bubry", 47.96361, -3.17306), + 56027 => array ("Buléon", 47.935, -2.67694), + 56028 => array ("Caden", 47.63083, -2.28806), + 56029 => array ("Calan", 47.87583, -3.32194), + 56030 => array ("Camoël", 47.48194, -2.395), + 56031 => array ("Camors", 47.84778, -3.00083), + 56032 => array ("Campénéac", 47.95778, -2.29472), + 56033 => array ("Carentoir", 47.81667, -2.13333), + 56034 => array ("Carnac", 47.58472, -3.07778), + 56035 => array ("Caro", 47.86417, -2.31917), + 56036 => array ("Caudan", 47.80889, -3.3425), + 56037 => array ("La Chapelle-Caro", 47.8656, -2.422), + 56038 => array ("La Chapelle-Gaceline", 47.78361, -2.10667), + 56039 => array ("La Chapelle-Neuve", 47.86472, -2.94306), + 56040 => array ("Cléguer", 47.8539, -3.3832), + 56041 => array ("Cléguérec", 48.12417, -3.07056), + 56042 => array ("Colpo", 47.81917, -2.81028), + 56043 => array ("Concoret", 48.06417, -2.20639), + 56044 => array ("Cournon", 47.74639, -2.10556), + 56045 => array ("Le Cours", 47.74167, -2.50139), + 56046 => array ("Crach", 47.61722, -3.00139), + 56047 => array ("Crédin", 48.03472, -2.76694), + 56048 => array ("Le Croisty", 48.06528, -3.36389), + 56049 => array ("Croixanvec", 48.13944, -2.86917), + 56050 => array ("La Croix-Helléan", 47.95694, -2.50083), + 56051 => array ("Cruguel", 47.87861, -2.59556), + 56052 => array ("Damgan", 47.51972, -2.57778), + 56053 => array ("Elven", 47.73139, -2.59056), + 56054 => array ("Erdeven", 47.64222, -3.15667), + 56055 => array ("Étel", 47.65778, -3.20083), + 56056 => array ("Évriguet", 48.07889, -2.41139), + 56057 => array ("Le Faouët", 48.033, -3.4903), + 56058 => array ("Férel", 47.48222, -2.34361), + 56059 => array ("Les Forges", 48.01806, -2.64694), + 56060 => array ("Les Fougerêts", 47.74028, -2.2125), + 56061 => array ("La Gacilly", 47.765, -2.13194), + 56062 => array ("Gâvres", 47.6946, -3.3526), + 56063 => array ("Gestel", 47.80333, -3.44417), + 56064 => array ("Glénac", 47.72667, -2.13306), + 56065 => array ("Gourhel", 47.9375, -2.36083), + 56066 => array ("Gourin", 48.13944, -3.6075), + 56067 => array ("Grand-Champ", 47.75861, -2.84444), + 56068 => array ("La Grée-Saint-Laurent", 47.99528, -2.49889), + 56069 => array ("Groix", 47.63861, -3.455), + 56070 => array ("Guégon", 47.93806, -2.56472), + 56071 => array ("Guéhenno", 47.89222, -2.63944), + 56072 => array ("Gueltas", 48.09417, -2.79583), + 56073 => array ("Guémené-sur-Scorff", 48.06833, -3.20333), + 56074 => array ("Guénin", 47.90694, -2.98056), + 56075 => array ("Guer", 47.90389, -2.12056), + 56076 => array ("Guern", 48.03056, -3.09167), + 56077 => array ("Le Guerno", 47.58306, -2.40833), + 56078 => array ("Guidel", 47.7909, -3.4876), + 56079 => array ("Guillac", 47.91056, -2.46611), + 56080 => array ("Guilliers", 48.0425, -2.40556), + 56081 => array ("Guiscriff", 48.05, -3.64528), + 56082 => array ("Helléan", 47.97111, -2.47389), + 56083 => array ("Hennebont", 47.80417, -3.27889), + 56084 => array ("Le Hézo", 47.58528, -2.70361), + 56085 => array ("Hœdic", 47.34, -2.8783), + 56086 => array ("Île-d'Houat", 47.3904, -2.9548), + 56087 => array ("Île-aux-Moines", 47.5965, -2.8441), + 56088 => array ("Île-d'Arz", 47.5902, -2.8018), + 56089 => array ("Inguiniel", 47.97639, -3.28278), + 56090 => array ("Inzinzac-Lochrist", 47.844, -3.2662), + 56091 => array ("Josselin", 47.95583, -2.54833), + 56092 => array ("Kerfourn", 48.04306, -2.83333), + 56093 => array ("Kergrist", 48.14639, -2.95611), + 56094 => array ("Kervignac", 47.76333, -3.23889), + 56096 => array ("Landaul", 47.74833, -3.07611), + 56097 => array ("Landévant", 47.76472, -3.12167), + 56098 => array ("Lanester", 47.76333, -3.33889), + 56099 => array ("Langoëlan", 48.12111, -3.23417), + 56100 => array ("Langonnet", 48.10556, -3.49333), + 56101 => array ("Languidic", 47.83333, -3.15778), + 56102 => array ("Lanouée", 48.00194, -2.58194), + 56103 => array ("Lantillac", 47.95667, -2.65194), + 56104 => array ("Lanvaudan", 47.89944, -3.26167), + 56105 => array ("Lanvénégen", 47.99833, -3.54167), + 56106 => array ("Larmor-Baden", 47.58722, -2.89722), + 56107 => array ("Larmor-Plage", 47.70639, -3.38417), + 56108 => array ("Larré", 47.71167, -2.51639), + 56109 => array ("Lauzach", 47.61472, -2.54472), + 56110 => array ("Lignol", 48.0375, -3.27111), + 56111 => array ("Limerzel", 47.63667, -2.35389), + 56112 => array ("Lizio", 47.86306, -2.52611), + 56113 => array ("Locmalo", 48.0726, -3.1861), + 56114 => array ("Locmaria", 47.2941, -3.0826), + 56115 => array ("Locmaria-Grand-Champ", 47.7575, -2.7875), + 56116 => array ("Locmariaquer", 47.56917, -2.94528), + 56117 => array ("Locminé", 47.88667, -2.83583), + 56118 => array ("Locmiquélic", 47.72528, -3.34083), + 56119 => array ("Locoal-Mendon", 47.71167, -3.105), + 56120 => array ("Locqueltas", 47.75694, -2.76611), + 56121 => array ("Lorient", 47.74583, -3.36639), + 56122 => array ("Loyat", 47.99, -2.38333), + 56123 => array ("Malansac", 47.67778, -2.29472), + 56124 => array ("Malestroit", 47.80972, -2.38361), + 56125 => array ("Malguénac", 48.08056, -3.05139), + 56126 => array ("Marzan", 47.54083, -2.32389), + 56127 => array ("Mauron", 48.08222, -2.28583), + 56128 => array ("Melrand", 47.98028, -3.11167), + 56129 => array ("Ménéac", 48.1392, -2.4598), + 56130 => array ("Merlevenez", 47.73667, -3.23361), + 56131 => array ("Meslan", 47.99444, -3.43139), + 56132 => array ("Meucon", 47.71694, -2.76417), + 56133 => array ("Missiriac", 47.83583, -2.35083), + 56134 => array ("Mohon", 48.05278, -2.52639), + 56135 => array ("Molac", 47.73028, -2.435), + 56136 => array ("Monteneuf", 47.87278, -2.20972), + 56137 => array ("Monterblanc", 47.7425, -2.68194), + 56138 => array ("Monterrein", 47.88028, -2.35778), + 56139 => array ("Montertelot", 47.88056, -2.42194), + 56140 => array ("Moréac", 47.91917, -2.82111), + 56141 => array ("Moustoir-Ac", 47.85556, -2.83528), + 56142 => array ("Moustoir-Remungol", 47.99639, -2.90278), + 56143 => array ("Muzillac", 47.55306, -2.48167), + 56144 => array ("Naizin", 47.98944, -2.83194), + 56145 => array ("Néant-sur-Yvel", 48.01361, -2.32917), + 56146 => array ("Neulliac", 48.12778, -2.98222), + 56147 => array ("Nivillac", 47.53333, -2.28333), + 56148 => array ("Nostang", 47.74972, -3.18778), + 56149 => array ("Noyal-Muzillac", 47.59167, -2.45694), + 56150 => array ("Noyalo", 47.61028, -2.68111), + 56151 => array ("Noyal-Pontivy", 48.06667, -2.88194), + 56152 => array ("Le Palais", 47.3463, -3.1546), + 56153 => array ("Péaule", 47.58194, -2.35611), + 56154 => array ("Peillac", 47.71333, -2.21917), + 56155 => array ("Pénestin", 47.48278, -2.47444), + 56156 => array ("Persquen", 48.02944, -3.19639), + 56157 => array ("Plaudren", 47.77889, -2.6925), + 56158 => array ("Plescop", 47.69889, -2.80611), + 56159 => array ("Pleucadeuc", 47.75889, -2.37583), + 56160 => array ("Pleugriffet", 47.98944, -2.68583), + 56161 => array ("Ploemel", 47.65056, -3.07222), + 56162 => array ("Ploemeur", 47.73583, -3.43111), + 56163 => array ("Ploërdut", 48.08778, -3.28833), + 56164 => array ("Ploeren", 47.65611, -2.86667), + 56165 => array ("Ploërmel", 47.93167, -2.3975), + 56166 => array ("Plouay", 47.91472, -3.33528), + 56167 => array ("Plougoumelen", 47.6525, -2.91694), + 56168 => array ("Plouharnel", 47.59806, -3.11278), + 56169 => array ("Plouhinec", 47.6973, -3.25), + 56170 => array ("Plouray", 48.14583, -3.38806), + 56171 => array ("Pluherlin", 47.69611, -2.365), + 56172 => array ("Plumelec", 47.83778, -2.64083), + 56173 => array ("Pluméliau", 47.95778, -2.97333), + 56174 => array ("Plumelin", 47.86167, -2.88806), + 56175 => array ("Plumergat", 47.74194, -2.91722), + 56176 => array ("Pluneret", 47.67556, -2.9575), + 56177 => array ("Pluvigner", 47.77583, -3.01028), + 56178 => array ("Pontivy", 48.06861, -2.96278), + 56179 => array ("Pont-Scorff", 47.8341, -3.4021), + 56180 => array ("Porcaro", 47.90972, -2.19861), + 56181 => array ("Port-Louis", 47.7063, -3.3526), + 56182 => array ("Priziac", 48.06139, -3.40944), + 56183 => array ("Quelneuc", 47.82333, -2.06667), + 56184 => array ("Questembert", 47.66139, -2.45306), + 56185 => array ("Quéven", 47.78861, -3.41528), + 56186 => array ("Quiberon", 47.48333, -3.12056), + 56187 => array ("Quily", 47.89083, -2.46806), + 56188 => array ("Quistinic", 47.9052, -3.133), + 56189 => array ("Radenac", 47.96278, -2.7125), + 56190 => array ("Réguiny", 47.9772, -2.7451), + 56191 => array ("Réminiac", 47.8611, -2.2348), + 56192 => array ("Remungol", 47.93333, -2.9), + 56193 => array ("Riantec", 47.71083, -3.31056), + 56194 => array ("Rieux", 47.59778, -2.1075), + 56195 => array ("La Roche-Bernard", 47.51833, -2.29917), + 56196 => array ("Rochefort-en-Terre", 47.69944, -2.33667), + 56197 => array ("Le Roc-Saint-André", 47.86472, -2.44889), + 56198 => array ("Rohan", 48.06861, -2.75278), + 56199 => array ("Roudouallec", 48.12667, -3.71694), + 56200 => array ("Ruffiac", 47.81806, -2.28222), + 56201 => array ("Le Saint", 48.08944, -3.56222), + 56202 => array ("Saint-Abraham", 47.85583, -2.40972), + 56203 => array ("Saint-Aignan", 48.18139, -3.01389), + 56204 => array ("Saint-Allouestre", 47.9097, -2.7226), + 56205 => array ("Saint-Armel", 47.5725, -2.71056), + 56206 => array ("Saint-Avé", 47.68667, -2.73444), + 56207 => array ("Saint-Barthélemy", 47.92583, -3.04528), + 56208 => array ("Saint-Brieuc-de-Mauron", 48.08972, -2.36306), + 56209 => array ("Sainte-Brigitte", 48.1626, -3.1285), + 56210 => array ("Saint-Caradec-Trégomel", 48.0375, -3.35111), + 56211 => array ("Saint-Congard", 47.77028, -2.31722), + 56212 => array ("Saint-Dolay", 47.54472, -2.15444), + 56213 => array ("Saint-Gérand", 48.10833, -2.88806), + 56214 => array ("Saint-Gildas-de-Rhuys", 47.5, -2.83806), + 56215 => array ("Saint-Gonnery", 48.12389, -2.81972), + 56216 => array ("Saint-Gorgon", 47.64917, -2.23667), + 56218 => array ("Saint-Gravé", 47.72583, -2.28083), + 56219 => array ("Saint-Guyomard", 47.78083, -2.51361), + 56220 => array ("Sainte-Hélène", 47.72, -3.20472), + 56221 => array ("Saint-Jacut-les-Pins", 47.68528, -2.21528), + 56222 => array ("Saint-Jean-Brévelay", 47.845, -2.72222), + 56223 => array ("Saint-Jean-la-Poterie", 47.63639, -2.12583), + 56224 => array ("Saint-Laurent-sur-Oust", 47.7945, -2.3185), + 56225 => array ("Saint-Léry", 48.09, -2.25611), + 56226 => array ("Saint-Malo-de-Beignon", 47.95889, -2.14972), + 56227 => array ("Saint-Malo-des-Trois-Fontaines", 48.01306, -2.47194), + 56228 => array ("Saint-Marcel", 47.80417, -2.41889), + 56229 => array ("Saint-Martin-sur-Oust", 47.7459, -2.2537), + 56230 => array ("Saint-Nicolas-du-Tertre", 47.80306, -2.22167), + 56231 => array ("Saint-Nolff", 47.70361, -2.65167), + 56232 => array ("Saint-Perreux", 47.66917, -2.10806), + 56233 => array ("Saint-Philibert", 47.58667, -3), + 56234 => array ("Saint-Pierre-Quiberon", 47.52083, -3.13417), + 56236 => array ("Saint-Servant", 47.91528, -2.51278), + 56237 => array ("Saint-Thuriau", 48.01667, -2.95), + 56238 => array ("Saint-Tugdual", 48.0987, -3.3382), + 56239 => array ("Saint-Vincent-sur-Oust", 47.69972, -2.14639), + 56240 => array ("Sarzeau", 47.52722, -2.76972), + 56241 => array ("Sauzon", 47.37111, -3.22333), + 56242 => array ("Séglien", 48.10694, -3.15972), + 56243 => array ("Séné", 47.61972, -2.73722), + 56244 => array ("Sérent", 47.82306, -2.50583), + 56245 => array ("Silfiac", 48.14889, -3.15667), + 56246 => array ("Le Sourn", 48.04306, -2.98972), + 56247 => array ("Sulniac", 47.67556, -2.57167), + 56248 => array ("Surzur", 47.57833, -2.62972), + 56249 => array ("Taupont", 47.95861, -2.43861), + 56250 => array ("Théhillac", 47.56778, -2.11639), + 56251 => array ("Theix", 47.62917, -2.65583), + 56252 => array ("Le Tour-du-Parc", 47.52556, -2.64556), + 56253 => array ("Tréal", 47.83917, -2.22389), + 56254 => array ("Trédion", 47.7925, -2.59333), + 56255 => array ("Treffléan", 47.68139, -2.61306), + 56256 => array ("Tréhorenteuc", 48.00833, -2.2875), + 56257 => array ("La Trinité-Porhoët", 48.09722, -2.54667), + 56258 => array ("La Trinité-sur-Mer", 47.58528, -3.02972), + 56259 => array ("La Trinité-Surzur", 47.60472, -2.59472), + 56260 => array ("Vannes", 47.655, -2.7604), + 56261 => array ("La Vraie-Croix", 47.68972, -2.54278), + 56262 => array ("Bono", 47.64028, -2.95083), + 56263 => array ("Sainte-Anne-d'Auray", 47.7036, -2.953), + 56264 => array ("Kernascléden", 48.00611, -3.32083), + 75101 => array ("Paris 1er", 48.86263, 2.33629), + 75102 => array ("Paris 2e", 48.86790, 2.34410), + 75103 => array ("Paris 3e", 48.86305, 2.35936), + 75104 => array ("Paris 4e", 48.85422, 2.35736), + 75105 => array ("Paris 5e", 48.84450, 2.34985), + 75106 => array ("Paris 6e", 48.84896, 2.33267), + 75107 => array ("Paris 7e", 48.85608, 2.31243), + 75108 => array ("Paris 8e", 48.87252, 2.31258), + 75109 => array ("Paris 9e", 48.87689, 2.33746), + 75110 => array ("Paris 10e", 48.87602, 2.36111), + 75111 => array ("Paris 11e", 48.85941, 2.37874), + 75112 => array ("Paris 12e", 48.83515, 2.41980), + 75113 => array ("Paris 13e", 48.82871, 2.36246), + 75114 => array ("Paris 14e", 48.82899, 2.32710), + 75115 => array ("Paris 15e", 48.84015, 2.29355), + 75116 => array ("Paris 16e", 48.86039, 2.26209), + 75117 => array ("Paris 17e", 48.88733, 2.30748), + 75118 => array ("Paris 18e", 48.89273, 2.34871), + 75119 => array ("Paris 19e", 48.88686, 2.38469), + 75120 => array ("Paris 20e", 48.86318, 2.40081) +); diff --git a/scheduleRoot.class.php b/scheduleRoot.class.php new file mode 100644 index 0000000..a0e1647 --- /dev/null +++ b/scheduleRoot.class.php @@ -0,0 +1,465 @@ + + * + * Plugin Schedule: manage events per wiki @groups + * + * $df_ : 31/12/2000 + * $ds_ : 20001231 + * $dn_ : 978217200 + * $dt_ : array ("Y" => 2000, "m" => 12, "d" => "31) + */ +if (!defined ('DOKU_INC')) + define ('DOKU_INC', realpath (dirname (__FILE__).'/../../../').'/'); +if (!defined ('DOKU_PLUGIN')) + define ('DOKU_PLUGIN', DOKU_INC.'lib/plugins/'); + +function cmpScheduleConfig ($a, $b) { + return ($a['nameSpace'] < $b['nameSpace']) ? -1 : 1; +} + +// ============================================================ +// admin class +class scheduleRoot { + + // ============================================================ + // Config attributs + // ============================================================ + var $cacheRootDir; // root cache directory + var $dataRootDir; // root data directory + + var $scheduleGroup; // group who can display and fill form + var $groupsDir; // pages groups directories + var $scheduleDir; // schedule pages groups directories + var $sharedDir; // schedule directory for shared events + var $scheduleWhat = array (); // set of legal "what" + var $scheduleAudience = array (); // set of legal "audience" + var $iconName; // group icon name + var $iconWidth; // group icon width + // lang + var $scheduleShared = array (); // notshared/shared + var $currentDateFormat; + + // ============================================================ + // Constant attributs $this->scheduleRoot-> + // ============================================================ + //var $imagesUrlDir = '/lib/plugins/schedule/images/'; // DOKU_REL + var $wizardIconUrl = '/lib/plugins/schedule/images/wizard.png'; // $this->imagesUrlDir + var $editIconUrl = '/lib/plugins/schedule/images/edit.png'; + var $repeatIconUrl = '/lib/plugins/schedule/images/repeat.png'; + var $emptyIconUrl = '/lib/plugins/schedule/images/empty.png'; + + var $oldConfigFile = "config.xml"; // old file config name + var $configFile = "cfg.xml"; // file config name + var $mbrPrefix = "mbr-"; // member prefix file name + var $propFile = "prop.xml"; // member prefix file name + var $configAttributsName = array ("nameSpace", "lastNotification", "lastNotificationReset"); + // scheduleDir == NS ? + var $dateFormat = array ("dmY" => array ("shortScan" => "(?[0-9]{1,2})[ /-](?[0-9]{1,2})[ /-](?[0-9]{1,2})", + "longScan" => "(?[0-9]{1,2})[ /-](?[0-9]{1,2})[ /-](?[0-9]{4})", + "fieldName" => array ("d", "m", "Y"), + "prettyPrint" => "d/m/Y", + "dayMonth" => "d/m"), + + "mdY" => array ("shortScan" => "(?[0-9]{1,2})[ /-](?[0-9]{1,2})[ /-](?[0-9]{1,2})", + "longScan" => "(?[0-9]{1,2})[ /-](?[0-9]{1,2})[ /-](?[0-9]{4})", + "fieldName" => array ("m", "d", "Y"), + "prettyPrint" => "m/d/Y", + "dayMonth" => "m/d")); + var $oddEvenValues = array ("odd", "even"); + var $scheduleRequestAttributsName = array ("id", "audience", "shared", + "what", "title", "lead", "posterURL", "paperURL", "remark", "rate", + "where", "lon", "lat", "addr", + "from", "to", "at", + "repeat", "repeatType", "weekRank", "dayInWeek", "dayRank", + "user"); + var $scheduleSaveAttributsName; + var $filterNames = array ("noMember" => "member", + "noWhat" => "what", + "NoAudience" => "audience"); + + + // ============================================================ + // Transcient attributs + // ============================================================ + var $message = array (); // messages to display : "notify" =>, "info" =>, "success" =>, "error" => + var $plugin; // link to wiki plugin inforation (conf, lang, ...) + var $isAdmin; // if user member of schedule adminGroup + var $oddEven = 0; // background color for table lines + + // ============================================================ + // Util + // ============================================================ + /* messages container to be display before plugin */ + function message ($type, $text) { + if (isset ($this->message[$type])) + $this->message[$type] .= '
    '.NL; + $this->message[$type] .= $text; + } + function startMessage ($type, $text) { + if (isset ($this->message[$type])) + $this->message[$type] = '
    '.NL.$this->message[$type]; + $this->message[$type] = $text.$this->message[$type]; + } + /* debug messages for admin only */ + function debug ($text) { + global $INFO; + // XXX + if (isset ($INFO['userinfo'] ['grps']) && in_array ('admin', $INFO ['userinfo'] ['grps'])) + $this->message ('notify', '
    '.$text.'
    '); + } + /* Change date format from array to number */ + function dt2dn ($dt_date) { + $dn_date = mktime (0, 0, 0, $dt_date["m"], $dt_date["d"], $dt_date["Y"]); + if (!$dn_date) + $this->message ('error', $dt_date["Y"]."-".$dt_date["d"]."-".$dt_date["m"]." : ".$this->plugin->getLang ('mktimeError').'
    '.NL); + return $dn_date; + } + /* Check date format 31/12/2000 */ + function checkDS ($ds_date) { + return + preg_match ("#([0-9]{8})#", $ds_date, $dumy); + } + /* Change date format from MM/DD/AAAA to number */ + function df2ds ($df_date) { + $df_date = trim ($df_date); + if (!$df_date) + return; + if ("!" == $df_date) + $ds_date = date ("Ymd"); + elseif (preg_match ("#\+(?[0-9]+)#", $df_date, $matches)) + $ds_date = sprintf ("%08d", date ("Ymd", mktime (0, 0, 0, date ("n"), date ("j")+$matches["delta"], date ("Y")))); + elseif (preg_match ("#\-(?[0-9]+)#", $df_date, $matches)) + $ds_date = sprintf ("%08d", date ("Ymd", mktime (0, 0, 0, date ("n"), date ("j")-$matches["delta"], date ("Y")))); + elseif (preg_match ("#".$this->currentDateFormat ["longScan"]."#", $df_date, $dt_date)) + $ds_date = sprintf ("%04d%02d%02d", $dt_date["Y"], $dt_date["m"], $dt_date["d"]); + elseif (preg_match ("#".$this->currentDateFormat ["shortScan"]."#", $df_date, $dt_date)) + $ds_date = sprintf ("20%02d%02d%02d", $dt_date["Y"], $dt_date["m"], $dt_date["d"]); + else + $ds_date = "{$df_date}"; + return $ds_date; + } + /* Change date format from number to MM/DD/AAAA */ + function ds2df ($ds_date) { + $ds_date = trim ($ds_date); + if (preg_match ("#(?[0-9][0-9][0-9][0-9])(?[0-9][0-9])(?[0-9][0-9])#", $ds_date, $dt_date)) + return sprintf ("%02d/%02d/%04d", + $dt_date[$this->currentDateFormat["fieldName"][0]], + $dt_date[$this->currentDateFormat["fieldName"][1]], + $dt_date[$this->currentDateFormat["fieldName"][2]]); + else + return "{$ds_date}"; + } + function ds2ln ($ds_date, $bold = false) { + $ds_date = trim ($ds_date); + if (preg_match ("#(?[0-9][0-9][0-9][0-9])(?[0-9][0-9])(?[0-9][0-9])#", $ds_date, $dt_date)) { + // XXX prévoir le format anglais + setlocale (LC_TIME, 'fr_FR.utf8','fra'); + return strftime (($bold ? "**%A %e %B** %Y" : "%A %e %B %Y"), + mktime (0, 0, 0, + $dt_date[$this->currentDateFormat["fieldName"][1]], + $dt_date[$this->currentDateFormat["fieldName"][0]], + $dt_date[$this->currentDateFormat["fieldName"][2]])); + } else + return "{$ds_date}"; + } + + /* Convert array on string (words seperated bay '|') */ + static function array2string ($array) { + if (!$array) + return ""; + $array = array_unique ($array); + if (in_array ("", $array)) + $array = array_diff ("", $array); + if (count ($array) < 1) + return ""; + return implode ("|", $array); + } + /* Convert array on string (words seperated bay '|') */ + function assocImplode ($array, $vSep = '|', $kSep = ':') { + $result = ""; + foreach ($array as $k => $v) { + $result .= $vSepNeeded.$k.$kSep.$v; + $vSepNeeded = $vSep; + } + return $result; + } + /* Convert array on string (words seperated bay '|') */ + function assocExplode ($string, $vSep = '|', $kSep = ':') { + $result = array (); + foreach (explode ($vSep, $string) as $kv) { + $data = explode ($kSep, $kv); + $result [$data[0]] = $data[1]; + } + return $result; + } + /* Convert array on string (words seperated bay '|') */ + // $ find data/[mp]*/membres/*/agenda/*/* data/[mp]*/commun/agenda/*/* -type f -print > dumy + // $ emacs dumy + // C-x ( mv ' ' C-k C-y ' ' C-y C-f C-x ) + // M-1000 C-x e + // C-M-% + // ^\([^ ]* [^ ]* [^ ]*agenda/[^/ ]*_\)/ => \1 + // C-M-% + // ^\([^ ]* [^ ]* [^ ]*agenda/[^/ ]*_\)/ => \1 + // C-M-% + // ^\([^ ]* [^ ]* [^ ]*agenda/[^/ ]*\)/ => \1_ + // $ find data/[mp]*/membres/*/agenda/* -type d -exec rmdir {} \; -print + function removeSep ($titleId) { + return strtr ($titleId, ':/;', ' '); + } + + function getPageId ($schedule) { + return ":". + $this->scheduleDir.":". + ($schedule->shared ? $this->sharedDir : $schedule->member).":". + $this->removeSep ($schedule->title); + } + + function resetOddEven () { + $this->oddEven = 0; + } + function switchOddEven () { + $this->oddEven = 1 - $this->oddEven; + } + + function getOddEven () { + return $this->oddEvenValues [$this->oddEven]; + } + + static function createDirIsNeeded ($dir) { + if (is_dir ($dir)) + return; + @mkdir ($dir); + @chmod ($dir, 0775); + } + + // ============================================================ + function __construct ($plugin) { + $this->plugin = $plugin; + $this->scheduleSaveAttributsName = array_merge ($this->scheduleRequestAttributsName, array ("weekDays")); + global $conf; + $savedir = ((!$conf['savedir'] || strpos ($conf['savedir'], '.') === 0) ? DOKU_INC : "").$conf['savedir']."/"; + $this->cacheRootDir = $savedir."cache/schedule/"; + $this->dataRootDir = $savedir.trim ($this->plugin->getConf ('dataDir').'/'); + scheduleRoot::createDirIsNeeded ($this->cacheRootDir); + scheduleRoot::createDirIsNeeded ($this->dataRootDir); + $this->scheduleGroup = trim ($this->plugin->getConf ('scheduleGroup')); + $this->groupsDir = trim ($this->plugin->getConf ('groupsDir')); + $this->scheduleDir = trim ($this->plugin->getConf ('scheduleDir')); + $this->sharedDir = trim ($this->plugin->getConf ('sharedDir')); + + foreach (explode ('|', $this->plugin->getConf ('scheduleWhat')) as $tmpCatDef) { + $tmpCatExp = explode (':', trim ($tmpCatDef)); + $tmpCat = trim ($tmpCatExp[0]); + foreach (explode (',', $tmpCatExp[1]) as $tmpWhat) { + $tmpWhat = trim ($tmpWhat); + $this->scheduleWhat[$tmpWhat] = $tmpCat; + } + } + $this->scheduleAudience [""] = $this->plugin->getLang ('audienceChoice'); + foreach (explode (',', $this->plugin->getConf ('scheduleAudience')) as $tmpAudience) + $this->scheduleAudience[trim ($tmpAudience)] = trim ($tmpAudience); + $this->iconName = $this->plugin->getConf ('iconName'); + $this->iconWidth = $this->plugin->getConf ('iconWidth'); + foreach (array (false, true) as $sharedVal) { + $tmp = $this->plugin->getLang ('shared'); + $this->scheduleShared[] = trim ($tmp[$sharedVal]); + } + $this->currentDateFormat = $this->dateFormat [$this->plugin->getLang ('dateFormat')]; + + global $INFO; + $this->isAdmin = + isset ($INFO ['userinfo']) && + isset ($INFO ['userinfo']['grps']) && + in_array (trim ($this->plugin->getConf ('adminGroup')), $INFO ['userinfo']['grps']); + } + + // ============================================================ + // Manage XML file + // ============================================================ + /* read lodging config */ + function readConfig ($dir) { + $oldFileName = $dir.$this->oldConfigFile; + $fileName = $dir.$this->configFile; + + // rename old fashion membre file name + if (file_exists ($oldFileName)) { + // XXX migration + rename ($oldFileName, $fileName); + $exclude = ".|..|".$this->scheduleRoot->configFile; + $exclude_array = explode("|", $exclude); + $pathDir = rtrim ($dir, "/") . "/"; + if (is_dir($pathDir)) { + $pathDirObj = opendir ($pathDir); + while (false !== ($file = readdir ($pathDirObj))) { + if (in_array (strtolower ($file), $exclude_array)) + continue; + $pathFile = $pathDir.$file; + if (is_file ($pathFile) && preg_match ('#.*\.xml$#i', $file, $b)) + rename ($pathFile, $pathDir.$this->mbrPrefix.$file); + } + } + } + if (!file_exists ($fileName)) + return false; + $result = array (); + // if (! class_exists ('DOMDocument')) + // return $result; + $xml = new DOMDocument ("1.0", "utf8"); + $xml->load ($fileName); + $root = $xml->documentElement; + foreach ($this->configAttributsName as $field) { + $element = $root->getElementsByTagName ($field); + if ($element) + $result [$field] = $element->item (0)->nodeValue; + } + return $result; + } + /* write lodging config */ + function writeConfig ($schedules) { + $fileName = $schedules->dataDir.$this->configFile; + scheduleRoot::createDirIsNeeded ($schedules->dataDir); + // if (! class_exists ('DOMDocument')) + // return; + $xml = new DOMDocument ("1.0", "utf8"); + $root = $xml->createElement ("schedule"); + $xml->appendChild ($root); + foreach ($this->configAttributsName as $field) + $root->appendChild ($xml->createElement ($field, htmlspecialchars ($schedules->$field))); + $xml->formatOutput = true; + $xml->save ($fileName); + chmod ($fileName, 0664); + } + + // ============================================================ + function manageAction ($request) { + if (!$this->isAdmin) + return; + $md5 = $request['md5']; + if (!$md5) + return; + $schedules = new schedules ($this, $request[$md5]['ns']); + switch ($request["action"]) { + + case 'moveSchedules': + $md5 = $request['md5']; + $date = $this->df2ds ($request[$md5]['date']); + $md5bis = $request[$md5]['toNs']; + if (!$date) + return; + $src = new schedules ($this, $request[$md5]['ns']); + $src->load (); + $dst = new schedules ($this, $request[$md5bis]['ns']); + $dst->load (); + $filter = array (); + foreach ($src->allSchedules as $id => $schedule) + if (($schedule->to ? $schedule->to : $schedule->from) < $date) + $filter [$id] = $schedule; + foreach ($filter as $id => $schedule) { + $dst->addSchedule ($schedule); + $src->removeSchedule ($id); + } + $dst->writeSchedules (); + $src->writeSchedules (); + + unset ($_REQUEST['schd']); + break; + default: + return; + } + } + + // ============================================================ + function printForm () { + if (!$this->isAdmin) + return; + $list = $this->readAllSchedules (); + echo + '
    '.NL. + ' '.NL. + ' '.NL. + // XXX getLang () + ' '.NL. + ' '.NL. + ' '.NL; + $this->resetOddEven (); + foreach ($list as $md5 => $item) { + $nameSpace = $item ["nameSpace"]; + $schedules = $item ["schedules"]; + echo + ' '.NL. + ' '.NL. + ' '.NL. + ' '.NL. + ' '.NL. + ' '.NL. + ' '.NL. + ' '.NL. + ' '.NL. + ' '.NL. + ' '.NL. + ' '.NL. + ' '.NL. + ' '.NL. + ' '.NL. + ' '.NL. + ' '.NL. + ' '.NL. + ' '.NL. + ' '.NL. + ' '.NL. + ' '.NL. + ' '.NL. + ' '.NL. + ' '.NL. + ' '.NL. + ' '.NL. + ' '.NL. + ' '.NL. + ' '.NL. + ' '.NL. + ' '.NL; + $this->switchOddEven (); + } + echo + ' '.NL. + '
    DescriptionValeurs
    NameSpace'.$nameSpace.'
    MD5'.$md5.'
    Members'.count ($schedules->memberSchedules).'
    Events'.count ($schedules->datedSchedules).'
    Repeated'.count ($schedules->repeatedSchedules).'
    Date
    Vers
    Action
    '.NL. + '
    '.NL; + } + + // ============================================================ + function readAllSchedules () { + $exclude_array = explode ("|", ".|.."); + if (!is_dir($this->dataRootDir)) + return; + $pathDirObj = opendir ($this->dataRootDir); + while (false !== ($file = readdir ($pathDirObj))) { + $subDir = $this->dataRootDir.$file.'/'; + if (in_array (strtolower ($file), $exclude_array) || !is_dir ($subDir)) + continue; + $list [$file] = $this->readConfig ($subDir); + $list [$file]["schedules"] = new schedules ($this, $list [$file]["nameSpace"]); + $list [$file]["schedules"]->load (); + } + uasort ($list, "cmpScheduleConfig"); + return $list; + } + + function clearCache () { + $list = $this->readAllSchedules (); + foreach ($list as $md5 => $item) { + $schedules = $item ["schedules"]; + $schedules->clearCache (); + } + } + + // ============================================================ +} diff --git a/schedules.class.php b/schedules.class.php new file mode 100644 index 0000000..d2141f0 --- /dev/null +++ b/schedules.class.php @@ -0,0 +1,1663 @@ + + * + * Plugin Schedule: manage events per wiki @groups + + // TODO : + // passer des actions suivant langue en actions neutres + // selection automatique de cantons suivant lieu si existe le canton existe + // radius dans conf puis select + + */ +if (!defined ('DOKU_INC')) + define ('DOKU_INC', realpath (dirname (__FILE__).'/../../../').'/'); +if (!defined ('DOKU_PLUGIN')) + define ('DOKU_PLUGIN', DOKU_INC.'lib/plugins/'); + +require_once (realpath (dirname (__FILE__)).'/scheduleInseeCities.php'); + +// ============================================================ +// management event class +class schedules { + + // ============================================================ + // Config attributs + // ============================================================ + var $scheduleRoot; // commun data for all schedules + + var $nameSpace; // namespace where definition could be found + var $lastNotification; // time of last proposal notification + var $lastNotificationReset; // time of last administrators acknowledgement + var $md5ns; // the ns directory + var $cacheDir; // cache directory + var $dataDir; // schedule database + + var $migrate; // performe data migration + var $showOldValues; // highlight non migrate data + + // ============================================================ + // Transcient attributs + // ============================================================ + var $allGroups = array (); // dynamique set of groups name + var $allSchedules = array (); // set of all events + var $memberSchedules = array (); // set of events indexed by member + var $repeatedSchedules = array (); // set of repeated events indexed by date + var $fixedSchedules = array (); // set of non repeated events indexed by date + var $datedSchedules = array (); // set of repeated or not events indexed by date + var $membersToSave = array (); // set of modified members + var $allProposals = array (); // set of all proposals + var $proposalsToSave = false; // modified proposals + + var $printProp = false; // display proposal event form + var $printForm = false; // display add event form + var $printCtrl = false; // display action event form + var $isShow = false; // date focus flag mode + var $userGroups = array (); // set of user group wich are schedule groupe + var $defaultSchedule; // empty schedule model + + var $before; // before filter date + var $date; // exact filter date + var $after; // after filter date + var $max; // max events lines + var $filters; + + // ============================================================ + // Util + // ============================================================ + function getConf ($name) { + return $this->scheduleRoot->plugin->getConf ($name); + } + function getLang ($name) { + return $this->scheduleRoot->plugin->getLang ($name); + } + function isAdmin () { + return $this->scheduleRoot->isAdmin; + } + function message ($type, $text) { + return $this->scheduleRoot->message ($type, $text); + } + function startMessage ($type, $text) { + return $this->scheduleRoot->startMessage ($type, $text); + } + function debug ($text) { + return $this->scheduleRoot->debug ($text); + } + function isMessage ($type) { + return isset ($this->scheduleRoot->message[$type]); + } + + // ============================================================ + function __construct ($scheduleRoot, $ns) { + $this->scheduleRoot = $scheduleRoot; + $this->defaultSchedule = new schedule (); + $this->nameSpace = cleanId (trim ($ns)); + $this->md5ns = md5 ($this->nameSpace); + $this->cacheDir = $this->scheduleRoot->cacheRootDir.$this->md5ns.'/'; + $this->dataDir = $this->scheduleRoot->dataRootDir.$this->md5ns.'/'; + $this->migrate = $this->getConf ('migrate'); + $this->showOldValues = $this->getConf ('showOldValues'); + if ($this->migrate) { + global $scheduleInseeCities, $scheduleCitiesInsee; + $scheduleCitiesInsee = array (); + foreach ($scheduleInseeCities as $insee => $cityDef) + $scheduleCitiesInsee [strtolower ($cityDef[0])] = $insee; + } + + $config = $this->scheduleRoot->readConfig ($this->dataDir); + if (!$config) + $this->scheduleRoot->writeConfig ($this); + else + foreach ($this->scheduleRoot->configAttributsName as $field) + if (isset ($config [$field])) + $this->$field = $config [$field]; + } + /** XXX utilisé par scheduleRoot ?! */ + function load () { + $exclude_array = explode ('|', '.|..'); + global $conf; + $pathDir = $conf['savedir'].'/pages/'.trim ($this->scheduleRoot->groupsDir, '/') . '/'; + if (is_dir($pathDir)) { + $pathDirObj = opendir ($pathDir); + while (false !== ($file = readdir ($pathDirObj))) { + if (in_array (strtolower ($file), $exclude_array)) + continue; + $pathFile = $pathDir . $file; + // XXX precedent se trouve dans les membres ??? + if (is_dir ($pathFile) && !in_array ($file, explode (',', trim ($this->getConf ('noSchedule'))))) + $this->allGroups [] = $file; + } + } + sort ($this->allGroups); + + global $INFO; + if (isset ($INFO ['userinfo']) && isset ($INFO ['userinfo']['grps'])) { + $userGroups = array (); + $scheduleGroup = false; + foreach ($INFO['userinfo']['grps'] as $tmpMember) { + $tmpMember = trim ($tmpMember); + if ($this->scheduleRoot->scheduleGroup == $tmpMember) + $scheduleGroup = true; + if (in_array ($tmpMember, $this->allGroups)) + $userGroups [] = $tmpMember; + } + if ($scheduleGroup) + $this->userGroups = $userGroups; + } + $this->readSchedules ($this->dataDir); + $this->readProp (); + } + + function isMember ($member) { + return $this->isAdmin () || in_array ($member, $this->userGroups); + } + + function isAMemberOf ($members) { + if ($this->isAdmin ()) + return true; + foreach ($members as $member) + if (in_array ($member, $this->userGroups)) + return true; + return false; + } + + function isMemberOfAll ($members) { + if ($this->isAdmin ()) + return true; + foreach ($members as $member) + if (!in_array ($member, $this->userGroups)) + return false; + return true; + } + + // ============================================================ + function readCache ($key) { + if (!is_dir ($this->cacheDir)) + return false; + $filename = $this->cacheDir.md5 ($key).'.cache'; + if (!file_exists ($filename)) + return false; + // YYY date du fichier (le calendrier change tous les jours) + if (filemtime ($filename) < strtotime ('today')) + return false; + // XXX ??? Warning: file_get_contents() [function.file-get-contents]: failed to open stream: No such file or directory in on line 153 + return file_get_contents ($filename); + } + + function writeCache ($key, $text) { + scheduleRoot::createDirIsNeeded ($this->cacheDir); + $filename = $this->cacheDir.md5 ($key).'.cache'; + file_put_contents ($filename, $text); + } + + function clearCache () { + if (!$this->isAdmin ()) + return; + $exclude = '.|..|'.$this->scheduleRoot->configFile; + $exclude_array = explode('|', $exclude); + $pathDir = $this->cacheDir; + if (!is_dir($pathDir)) + return; + $pathDirObj = opendir ($pathDir); + while (false !== ($file = readdir ($pathDirObj))) { + if (in_array (strtolower ($file), $exclude_array)) + continue; + $pathFile = $pathDir . $file; + if (is_file ($pathFile) && preg_match ('#.*\.cache#i', $file, $b)) + unlink ($pathFile); + } + @rmdir ($pathDir); + } + + // ============================================================ + function setDateFilters ($before, $date, $after) { + $this->before = $this->scheduleRoot->df2ds ($before); + $this->after = $this->scheduleRoot->df2ds ($after); + $this->date = $this->scheduleRoot->df2ds ($date); + if ($this->date) + $this->before = $this->after = $this->date; + } + + // ============================================================ + function setFormLevel ($formLevel) { + global $INFO; + if ($formLevel > 2) + $this->printProp = true; + if (($formLevel > 0) && + isset ($INFO ['userinfo']) && + isset ($INFO ['userinfo']['grps'])) + foreach ($INFO['userinfo']['grps'] as $tmpMember) { + $tmpMember = trim ($tmpMember); + if ($this->scheduleRoot->scheduleGroup == $tmpMember) { + if ($formLevel > 1) + $this->printForm = true; + $this->printCtrl = true; + $this->printProp = false; + break; + } + } + } + + // ============================================================ + function setOtherFilters ($max, $filters, $grouped) { + $this->max = $max; + foreach ($filters as $name => $values) { + $newValues = array (); + foreach (explode (',', $values) as $value) { + $value = trim ($value); + if (! empty ($value)) + $newValues [] = $value; + } + if (!empty ($newValues)) + $this->filters[$name] = $newValues; + } + $this->repeatGrouped = trim ($this->getConf ('repeatPosition')) == 'grouped'; + $grouped = trim ($grouped); + if (in_array ($grouped, array ('grouped', 'isolated'))) + $this->repeatGrouped = $grouped == 'grouped'; + } + + // ============================================================ + // Manage XML file + // ============================================================ + /* Save a member data base */ + function writeSchedule ($member) { + $xml = new DOMDocument ('1.0', 'utf8'); + $root = $xml->createElement ('schedule'); + $root->setAttribute ('member', $member); + $xml->appendChild ($root); + foreach ($this->memberSchedules [$member] as $id => $schedule) { + $event = $xml->createElement ('event'); + foreach ($this->scheduleRoot->scheduleSaveAttributsName as $field) + $event->appendChild ($xml->createElement ($field, htmlspecialchars ($schedule->$field))); + $root->appendChild ($event); + } + $xml->formatOutput = true; + $fileName = $this->dataDir.$this->scheduleRoot->mbrPrefix.$member.'.xml'; + $xml->save ($fileName); + chmod ($fileName, 0664); + } + + function readProp () { + $this->readSchedule ($this->dataDir.$this->scheduleRoot->propFile, true); + } + + function writeProp () { + // if (! class_exists ('DOMDocument')) + // return; + $xml = new DOMDocument ('1.0', 'utf8'); + $root = $xml->createElement ('schedule'); + $root->setAttribute ('member', 'proposal'); + $xml->appendChild ($root); + foreach ($this->allProposals as $id => $schedule) { + $event = $xml->createElement ('event'); + foreach ($this->scheduleRoot->scheduleSaveAttributsName as $field) + $event->appendChild ($xml->createElement ($field, htmlspecialchars ($schedule->$field))); + $event->appendChild ($xml->createElement ('member', htmlspecialchars ($schedule->member))); + $root->appendChild ($event); + } + $xml->formatOutput = true; + $fileName = $this->dataDir.$this->scheduleRoot->propFile; + $xml->save ($fileName); + chmod ($fileName, 0664); + } + + /* Migrate where format */ + function whereMigrate ($schedule, $sep) { + global $scheduleCitiesInsee; + if (!$schedule) + return false; + if (preg_match ('/[0-9]/', $schedule->where)) { + return false; + } + $newFields = array (); + $updated = false; + foreach (explode ($sep, $schedule->where) as $city) { + $city = trim ($city); + if (!$city) + continue; + $insee = $scheduleCitiesInsee [strtolower ($city)]; + $newFields [] = $insee ? $insee : $city; + if ($insee) + $updated = true; + } + $newWhere = implode (',', $newFields); + if (!$newWhere || !$updated) + return false; + $schedule->where = $newWhere; + return true; + } + + /* Read a member data base */ + function readSchedule ($file, $prop = false) { + if (!file_exists ($file)) + return; + // if (! class_exists ('DOMDocument')) + // return; + $xml = new DOMDocument ('1.0', 'utf8'); + $xml->load ($file); + $root = $xml->documentElement; + $member = $root->getAttribute ('member'); + $events = $root->getElementsByTagName ('event'); + for ($i = $events->length; --$i >= 0; ) { + $event = $events->item ($i); + $schedule = new schedule (); + foreach ($this->scheduleRoot->scheduleSaveAttributsName as $field) { + $element = $event->getElementsByTagName ($field); + if ($element) + $schedule->$field = $element->item (0)->nodeValue; + } + if ($prop) { + $element = $event->getElementsByTagName ('member'); + if ($element) + $schedule->member = $element->item (0)->nodeValue; + } else + $schedule->member = $member; + if ($this->migrate && !$prop && + ($this->whereMigrate ($schedule, ',') || + $this->whereMigrate ($schedule, '/') || + $this->whereMigrate ($schedule, ' - ') || + $this->whereMigrate ($schedule, ' '))) + $this->membersToSave [$member] = true; + // XXX compatibilité + $schedule->shared = ($schedule->shared == true); + $schedule->repeatFlag = $schedule->to != '' && $schedule->from != $schedule->to; + if ($prop) + $this->allProposals [$schedule->id] = $schedule; + else + $this->allSchedules [$schedule->id] = $schedule; + } + } + + // ============================================================ + // IO functions + // ============================================================ + /* Read all member data base */ + function readSchedules ($pathDir) { + $exclude = '.|..|'.$this->scheduleRoot->configFile; + $exclude_array = explode('|', $exclude); + $pathDir = rtrim ($pathDir, '/') . '/'; + if (!is_dir($pathDir)) + return; + $pathDirObj = opendir ($pathDir); + while (false !== ($file = readdir ($pathDirObj))) { + if (in_array (strtolower ($file), $exclude_array)) + continue; + $pathFile = $pathDir . $file; + if (is_file ($pathFile) && + preg_match ('#'.$this->scheduleRoot->mbrPrefix.'.*\.xml$#i', $file, $b)) + $this->readSchedule ($pathFile); + } + $this->updateSchedulesLists (); + } + + // ============================================================ + /* Save all member data base marked as modified */ + function writeSchedules () { + if (!count ($this->membersToSave)) + return; + foreach (array_keys ($this->membersToSave) as $member) + if (count ($this->memberSchedules [$member]) <= 0) + @unlink ($this->dataDir.$this->scheduleRoot->mbrPrefix.$member.'.xml'); + else + $this->writeSchedule ($member); + $this->clearCache (); + } + + // ============================================================ + // YYY + // ============================================================ + /* get all siblings shared events but the pattern */ + function getOtherSharedEvents ($event) { + $result = array (); + if (!$event->from) + return $result; + foreach ($this->datedSchedules [$event->from] as $other) + if ($event->shared == $other->shared && + $event->to == $other->to && + $event->at == $other->at && + $event->title == $other->title && + $event->id != $other->id) + $result [] = $other; + return $result; + } + + // ============================================================ + /* XXX */ + function getMembersSharedEvents ($event) { + if (!$event->shared || !$event->from || !$event->title) + return $event->requestMembers ? $event->requestMembers : array ($event->member); + $result = array (); + foreach ($this->datedSchedules [$event->from] as $other) + if ($event->shared == $other->shared && + $event->to == $other->to && + $event->at == $other->at && + $event->title == $other->title) + $result [] = $other->member; + return $result; + } + + // ============================================================ + /* Performe action */ + function manageAction ($request) { + + if ($request['action'] == $this->getLang ('show')) { + $this->isShow = true; + if (isset ($request['date'])) { + $this->before = $this->after = $this->date = $this->scheduleRoot->df2ds ($request['date']); + } + } elseif ($request['action'] == $this->getLang ('selected')) { + if (isset ($request['id'])) { + $id = $request['id']; + if (isset ($this->allProposals [$id])) { + $this->defaultSchedule = $this->allProposals [$id]; + $this->defaultSchedule->prop = true; + } else if (isset ($this->allSchedules [$id])) { + $this->defaultSchedule = $this->allSchedules [$id]; + } + } + + } elseif (in_array ($request['action'], + array ($this->getLang ('modifyAll'), + $this->getLang ('modify'), + $this->getLang ('add'), + $this->getLang ('valid'), + $this->getLang ('prop')))) { + if (isset ($request['id'])) + $id = $request['id']; + $this->manageAddAction ($request); + if ($request['action'] == $this->getLang ('valid')) + $this->removeSchedule ($id); + + } elseif ($request['action'] == $this->getLang ('remove')) { + if (isset ($request['idl'])) + foreach ($request['idl'] as $id) + $this->removeSchedule ($id); + unset ($_REQUEST['schd']); + + } elseif ($request['action'] == $this->getLang ('created')) + $this->manageCreateAction ($request); + } + + // ============================================================ + function manageAddAction ($request) { + if (isset ($request['id'])) + $id = $request['id']; + $schedule = new schedule (); + $others = array (); + $oldMember = ''; + if (isset ($this->allSchedules [$id]) && + ($request['action'] == $this->getLang ('modifyAll') || + $request['action'] == $this->getLang ('modify'))) { + $schedule = &$this->allSchedules [$id]; + $oldMember = $schedule->member; + if ($request['action'] == $this->getLang ('modifyAll')) + $others = $this->getOtherSharedEvents ($schedule); + } + foreach ($this->scheduleRoot->scheduleRequestAttributsName as $field) + $schedule->$field = trim (str_replace (array ('"'), '', $request [$field])); + if ($request ['member']) { + $schedule->requestMembers = explode (',', trim (str_replace (',,', ',', $request ['member']), ',')); + $schedule->member = trim ($schedule->requestMembers [0]); + } + $schedule->title = strtr ($schedule->title, '"', "''"); + $schedule->lead = strtr ($schedule->lead, '"', "''"); + $schedule->remark = strtr ($schedule->remark, '"', "''"); + + $schedule->weekDays = scheduleRoot::array2string ($request ['weekDays']); + $schedule->from = $this->scheduleRoot->df2ds ($request ['from']); + $schedule->to = $this->scheduleRoot->df2ds ($request ['to']); + if ('' == $schedule->from) { + $schedule->from = $schedule->to; + $schedule->to = ''; + } + if ($schedule->from == $schedule->to) + $schedule->to = ''; + if ('' == $schedule->from && '' == $schedule->where && '' == $schedule->title) { + $this->defaultSchedule = $schedule; + unset ($_REQUEST['schd']); + return; + } + if (!$this->printProp && $schedule->member && !$this->isMemberOfAll ($schedule->requestMembers)) { + $this->message ('info', $this->getLang ('notMemberError')); + $this->defaultSchedule = $schedule; + unset ($_REQUEST['schd']); + return; + } + if (!$request ['audience']) + $this->message ('error', $this->getLang ('noAudienceError')); + if (!$schedule->member) + $this->message ('error', $this->getLang ('noMemberError')); + if ('' == $schedule->what) + $this->message ('error', $this->getLang ('noTypeError')); + if ('' == $schedule->title) + $this->message ('error', $this->getLang ('noTitleError')); + if ('' == $schedule->where) + $this->message ('error', $this->getLang ('noWhereError')); + if (!$this->scheduleRoot->checkDS ($schedule->from)) + $this->message ('error', $request ['from'].' : '.$this->getLang ('badDateError')); + if ($schedule->to && !$this->scheduleRoot->checkDS ($schedule->to)) + $this->message ('error', $request ['to'].' : '.$this->getLang ('badDateError')); + else if ($this->after && ($schedule->to ? $schedule->to : $schedule->from) < $this->after) + $this->message ('error', $request ['from'].' : '.$this->getLang ('pastError')); + else if ($schedule->to != '' && $schedule->from > $schedule->to) + $this->message ('error', $request ['to'].' : '.$this->getLang ('pastToError')); + if ($this->isMessage ('error')) { + $this->startMessage ('error', $this->getLang ('startError')); + $this->defaultSchedule = $schedule; + unset ($_REQUEST['schd']); + return; + } + $schedule->repeatFlag = $schedule->to != '' && $schedule->from != $schedule->to; + + // force un nouvel id, member et file + if (!$schedule->id || in_array ($request['action'], + array ($this->getLang ('add'), + $this->getLang ('valid'), + $this->getLang ('prop')))) + $schedule->id = date ('Ymdhis.0'); + + if ($this->printProp) { + unset ($_REQUEST['schd']); + if (!$captcha =& plugin_load ('helper', 'captcha') || + !$captcha->check ()) { + $this->defaultSchedule = $schedule; + return; + } + $schedule->member = implode (',', $schedule->requestMembers); + $this->allProposals [] = $schedule; + $this->proposalsToSave = true; + $this->adminNotification (); + $this->message ('success', $this->getLang ('propSuccess')); + return; + } + + if ($oldMember) + $this->membersToSave [$oldMember] = true; + + global $INFO; + $schedule->user = $_SERVER['REMOTE_USER']; + $this->addSchedule ($schedule); + unset ($_REQUEST['schd']); + + foreach ($others as $other) { + if (!$this->isMember ($others->member)) + continue; + foreach ($this->scheduleRoot->scheduleSaveAttributsName as $field) + if ($field != 'id') + $other->$field = $schedule->$field; + $this->membersToSave [$other->member] = true; + } + + $sharedMembers = $this->getMembersSharedEvents ($schedule); + $i = 1; + foreach ($schedule->requestMembers as $newMember) { + $newMember = trim ($newMember); + if (!$newMember || in_array ($newMember, $sharedMembers)) + continue; + if (!$this->isMember ($newMember)) + continue; + $newSchedule = new schedule (); + foreach ($this->scheduleRoot->scheduleSaveAttributsName as $field) + $newSchedule->$field = $schedule->$field; + $newSchedule->id = date ('Ymdhis').$i; + $i++; + $newSchedule->member = $newMember; + $this->addSchedule ($newSchedule); + } + + $pageId = $this->scheduleRoot->getPageId ($schedule); + resolve_pageid ($this->nameSpace, $pageId, $exists); + if (!$exists) + return; + $content = io_readFile (wikiFN ($pageId)); + $start = ""; + $option = ""; + $end = $content; + $place = trim ($this->makeListPlaceForMap ($schedule)); + if (preg_match ("#(?(.|\n)*)[^>]*)>(?([^<]|\n)*)(?(.|\n)*)#", $content, $dumy)) { + $start = $dumy ['start']; + $option = $dumy ['option']; + $place2 = trim ($dumy ['place']); + $end = $dumy ['end']; + if ($place == $place2) + return; + } else if (preg_match ("#(?(.|\n)*=+ Lieu =+\n)(?([^=]|=[^=]|\n)*)(?\n=(.|\n)*)#", $content, $dumy)) { + $start = $dumy ['start'].NL; + $end = $dumy ['end']; + } else { + $start = '===== Lieu ====='.NL.NL; + $end = $content; + } + $content = $start.''.NL.$place.NL.''.NL.$end; + saveWikiText ($pageId, $content, 'update place'); + } + + // ============================================================ + function manageCreateAction ($request) { + $id = $request['id']; + $pageId = $request ['pageId']; + resolve_pageid ($this->nameSpace, $pageId, $exists); + if (!$exists && isset ($this->allSchedules [$id])) { + $schedule = $this->allSchedules [$id]; + $sharedMembers = $this->getMembersSharedEvents ($schedule); + // XXX mettre date, lieu, sujet, tarif dans langue + $content = ''; + foreach ($sharedMembers as $member) + $content .= + '[[:'.$this->scheduleRoot->groupsDir.':'.$member.':|'. + '{{ :'.$this->scheduleRoot->groupsDir.':'.$member.':'.$this->scheduleRoot->iconName.'?100}}]]'.NL; + $content .= + '~~NOTOC~~'.NL. + '====== '.$schedule->title.' ======'.NL.NL; + if ($schedule->lead) + $content .= + str_replace ("''", '"', $schedule->lead).NL.NL; + $content .= + '===== Date ====='.NL.NL. + ($schedule->repeatFlag ? + $this->makeRepeatString ($schedule, true) : + ('Le '. $this->scheduleRoot->ds2ln ($schedule->from, true))). + $this->makePrettyAt ($schedule->at).NL.NL. + '===== Lieu ====='.NL.NL. + ''.NL. + $this->makeListPlaceForMap ($schedule). + ''.NL.NL. + '===== Sujet ====='.NL.NL; + if (!$schedule->remark) + $content .= + 'Sujet à préciser'.NL.NL; + if ($schedule->posterURL) + $content .= + '{{ '.$schedule->posterURL.'?direct&200|}}'.NL; + if ($schedule->paperURL) + $content .= + '[['.$schedule->paperURL.' |]]'.NL; + if ($schedule->remark) + $content .= + str_replace ("''", '"', $schedule->remark).NL.NL; + if ($schedule->rate) + $content .= + '===== Tarif ====='.NL.NL. + $schedule->rate.NL; + + saveWikiText ($pageId, $content, 'wizard creation'); + $this->clearCache (); + } + unset ($_REQUEST['schd']); + } + + // ============================================================ + /* Add event */ + function addSchedule (&$schedule) { + if (isset ($this->allSchedules [$schedule->id])) + unset ($this->allSchedules [$schedule->id]); + $this->membersToSave [$schedule->member] = true; + // élimine les doublons + if (isset ($this->memberSchedules [$schedule->member])) + foreach ($this->memberSchedules [$schedule->member] as $event) + if ($event->from == $schedule->from && + $event->to == $schedule->to && + $event->at == $schedule->at && + $event->shared == $schedule->shared && + $event->title == $schedule->title && + $event->id != $schedule->id) { + unset ($this->allSchedules [$event->id]); + } + $this->allSchedules [$schedule->id] = $schedule; + $this->updateSchedulesLists (); + } + + // ============================================================ + /* Remove event */ + function removeSchedule ($id) { + if (isset ($this->allProposals [$id])) { + $this->proposalsToSave = true; + unset ($this->allProposals [$id]); + $this->resetAdminNotification (); + } else if (isset ($this->allSchedules [$id])) { + $this->membersToSave [$this->allSchedules [$id]->member] = true; + unset ($this->allSchedules [$id]); + $this->updateSchedulesLists (); + } + } + + // ============================================================ + /* Update data base memory indexes after modification */ + function updateSchedulesLists () { + $this->memberSchedules = array (); + $this->repeatedSchedules = array (); + $this->fixedSchedules = array (); + $this->datedSchedules = array (); + foreach ($this->allSchedules as $id => $schedule) { + $this->memberSchedules [$schedule->member][$id] = $schedule; + $this->datedSchedules [$schedule->from][] = $schedule; + if ($schedule->repeatFlag) + $this->repeatedSchedules [$schedule->from][] = $schedule; + else + $this->fixedSchedules [$schedule->from][] = $schedule; + } + foreach (array_keys ($this->memberSchedules) as $member) + uasort ($this->memberSchedules [$member], 'cmpSchedule'); + uasort ($this->allSchedules, 'cmpSchedule'); + } + + // ============================================================ + /* Print schedule as a matrix of days */ + function printScheduleCalendar ($mapId, $dn_showDay) { + $nbDaysPerWeek = 7; + $nbWeeks = 5; + $nbDays = $nbDaysPerWeek * $nbWeeks; + $dn_today = mktime (0, 0, 0, date ('n'), date ('j'), date ('Y')); + $dn_start = mktime (0, 0, 0, date ('m', $dn_showDay), date ('d', $dn_showDay)-date ('w', $dn_showDay), date ('Y', $dn_showDay)); + $dn_end = $dn_start + $nbDays * 86400; + $ds_start = date ('Ymd', $dn_start); + $ds_end = date ('Ymd', $dn_end); + $dn_prev = mktime (0, 0, 0, date ('m', $dn_showDay)-1, date ('d', $dn_showDay), date ('Y', $dn_showDay)); + $dn_next = mktime (0, 0, 0, date ('m', $dn_showDay)+1, date ('d', $dn_showDay), date ('Y', $dn_showDay)); + $df_prev = date ($this->scheduleRoot->currentDateFormat['prettyPrint'], $dn_prev); + $df_next = date ($this->scheduleRoot->currentDateFormat['prettyPrint'], $dn_next); + $dayNames = $this->getLang ('days'); + + ptln (''.NL. + ' '.NL. + ' '); + if ($dayNames) + foreach ($dayNames as $dayName => $dayAbrv) + echo ''; + ptln (' '.NL. + ' '); + + $focusSchedules = array (); + $dn_day = $dn_start; + // copie des évènements non répété (à part quotidient) + for ($i = 0; $i < $nbDays; $i++) { + $ds_index = date ('Ymd', $dn_day); + if (isset ($this->fixedSchedules [$ds_index])) + $focusSchedules [$ds_index] = array_values ($this->fixedSchedules [$ds_index]); + $dn_day = mktime(0, 0, 0, date ('m', $dn_day) , date ('d', $dn_day)+1, date ('Y', $dn_day)); + } + + // extends repeated events + foreach ($this->repeatedSchedules as $ds_index => $events) { + foreach ($events as $event) { + if ($event->from >= $ds_end) + continue; + if ($event->to < $ds_start) + continue; + + preg_match ("#(?[0-9]{4})(?[0-9]{2})(?[0-9]{2})#", $event->from, $dt_from); + $dn_from = $this->scheduleRoot->dt2dn ($dt_from); + if ($event->to > $ds_end) + $dn_to = $dn_end; + else { + preg_match ("#(?[0-9]{4})(?[0-9]{2})(?[0-9]{2})#", $event->to, $dt_to); + $dn_to = $this->scheduleRoot->dt2dn ($dt_to); + } + + switch ($event->repeatType) { + // chercher le premier dans la fenêtre + // tant que dans la fenêtre répété la répétition + // ajouter l'évènement dans les autres + + case 'day' : + // répétition des évènements quotidients + $dn_day = $dn_from; + $step = 86400 * $event->repeat; + if ($dn_from < $dn_start) + $dn_day += ceil (($dn_start - $dn_from) / $step) * $step; + for ( ; $dn_day <= $dn_to; $dn_day += $step) + $focusSchedules [date ('Ymd', $dn_day)][] = $event; + break; + case 'week' : + $dn_startWeek = mktime (0, 0, 0, date ('m', $dn_from), date ('d', $dn_from) - date ('w', $dn_from), date ('Y', $dn_from)); + $step = 86400 * 7 * $event->repeat; + if ($dn_startWeek < $dn_start) + $dn_startWeek += floor (($dn_start - $dn_startWeek) / $step) * $step; + for ( ; $dn_startWeek <= $dn_to; $dn_startWeek += $step) + foreach (explode ('|', $event->weekDays) as $idd) { + $dn_day = $dn_startWeek + $idd * 86400; + if ($dn_day >= $dn_start && $dn_day >= $dn_from && + $dn_day <= $dn_to) + $focusSchedules [date ('Ymd', $dn_day)][] = $event; + } + break; + case 'dayMonth' : + case 'dateMonth' : + $dn_startMonth = mktime (0, 0, 0, date ('m', $dn_from), 1, date ('Y', $dn_from)); + if ($dn_startMonth < $dn_start) { + $nbMonth = floor ((date ('m', $dn_start) + date ('Y', $dn_start)*12 - (date ('m', $dn_from) + date ('Y', $dn_from)*12))/$event->repeat)*$event->repeat; + $dn_startMonth = mktime (0, 0, 0, date ('m', $dn_startMonth) + $nbMonth, 1, date ('Y', $dn_startMonth)); + } + for ( ; + $dn_startMonth <= $dn_to; + $dn_startMonth = mktime (0, 0, 0, date ('m', $dn_startMonth) + $event->repeat, 1, date ('Y', $dn_startMonth))) { + $dn_day = $dn_startMonth; + if ($event->repeatType == 'dateMonth') + // XXX pb si moins de jour dans le mois + $dn_day = mktime (0, 0, 0, date ('m', $dn_startMonth), $event->dayRank, date ('Y', $dn_startMonth)); + else { + $delta = $event->dayInWeek - date ('w', $dn_startMonth); + if ($delta < 0) + $delta += 7; + $delta += 7 * $event->weekRank; + // XXX pb si moins de jour dans le mois + $dn_day = mktime (0, 0, 0, date ('m', $dn_startMonth), $delta+1, date ('Y', $dn_startMonth)); + } + if ($dn_day >= $dn_start && $dn_day >= $dn_from && + $dn_day <= $dn_to) + $focusSchedules [date ('Ymd', $dn_day)][] = $event; + } + break; + case 'year' : + $dayInmonth = date ('j', $dn_from); + $monthInYear = date ('n', $dn_from); + $startYear = date ('Y', $dn_start) - (date ('Y', $dn_start) - date ('Y', $dn_from)) % $event->repeat; + $endYear = date ('Y', $dn_end); + for ( ; $startYear <= $endYear; $startYear += $event->repeat) { + $dn_day = mktime (0, 0, 0, $monthInYear, $dayInmonth, $startYear); + if ($dn_day >= $dn_start && $dn_day >= $dn_from && + $dn_day <= $dn_to) + $focusSchedules [date ('Ymd', $dn_day)][] = $event; + } + break; + } + } + } + + global $conf; + $dn_day = $dn_start; + $allLocations = ''; + $LocationsEvents = array (); + for ($s = 0; $s < 5; $s++) { + $line = ''; + for ($d = 0; $d < 7; $d++) { + $locations = ''; + $df_day = date ($this->scheduleRoot->currentDateFormat['prettyPrint'], $dn_day); + $url = "javascript:scheduleSelectDate('".$df_day."');"; + $bubble = ''; + if ($d == 1) + $w = date ('W', $dn_day); + $class = 'day showBubbleOnFocus'; + $dayRank = date ('d', $dn_day); + + $eventClass = ''; + if (isset ($focusSchedules [date ('Ymd', $dn_day)])) { + $eventClass = array (); + $nbEvent = 0; + $dayEvents = array (); + foreach ($focusSchedules [date ('Ymd', $dn_day)] as $event) { + $eventClass [preg_replace ("/\s/", '', htmlspecialchars (strtolower ($this->scheduleRoot->scheduleWhat [$event->what])))] = true; + $nbEvent++; + list ($LocationsEvents, $dayEvents, $locations) = $this->addLocationsDayEvent ($LocationsEvents, $dayEvents, $locations, $dn_day, $event); + } + $bubble = $this->getDayBubble ($dn_day, $dayEvents); + ksort ($eventClass); + $eventClass = 'cat_'.implode ('', array_keys ($eventClass)); + $url = '?id='.$this->nameSpace.':'.$conf['start'].'&schd[ns]='.$this->nameSpace.'&schd[action]='.$this->getLang ('show').'&schd[date]='.$df_day; + $class .= ' '.$eventClass; + $class .= ' op_'.($nbEvent < 2 ? $nbEvent : 3); + } + if (!$bubble && $dn_day != $dn_today && $dn_day != $dn_showDay) + $class .= ' empty'; + $locations = trim (implode (',', array_unique (explode (',', $locations))), ','); + $allLocations = trim (implode (',', array_unique (explode (',', $locations.','.$allLocations))), ','); + + $over = ''; + if ($dn_day < $dn_today) + $over .= '
    '.$dayRank.'
    '; + if ($d == 0 || $d == 6) + $class .= ' weekend'; + if ($dn_day == $dn_today) + $over .= '
    '.$dayRank.'
    '; + elseif ($dn_day == $dn_showDay) + $over .= '
    '.$dayRank.'
    '; + + $js = ' onMouseOver="javascript:scheduleHighlightPOI (\''.$locations.'\')"'; + $line .= '
    '; + $dn_day = mktime(0, 0, 0, date ('m', $dn_day), date ('d', $dn_day)+1, date ('Y', $dn_day)); + } + echo ''.$line.''.NL; + } + + ptln (' '.NL. + ' '.NL. + ' '.NL. + ' '.NL. + '
    '.NL. + '
    '.NL. + ' <'.NL. + '
    '.$this->getLang ('tipPrevM').'
    '.NL. + '
    '.NL. + ' '.date ($this->scheduleRoot->currentDateFormat['prettyPrint'], $dn_showDay).NL. + '
    '.NL. + ' >'. + '
    '.$this->getLang ('tipNextM').'
    '.NL. + '
    '. + '
    '.$dayAbrv.'
    '.$over.$bubble.''.$dayRank.'
    '.$w.'
    '.NL. + $this->getCityBubble ($LocationsEvents). + '
    '.NL. + '
    '.NL. + ' '.NL. + '
    '.NL. + '
    '); + } + + // ============================================================ + function getLocationsFromEvent ($event) { + $locations = ''; + if ($event->lat && $event->lon) { + foreach (array_combine (explode (',', $event->lat), explode (',', $event->lon)) as $lat => $lon) { + $location = '('.$lat.'|'.$lon.')'; + $locations .= ','.$location; + } + } elseif ($event->where) { + $location = $event->where; + $locations .= ','.$location; + } + return $locations; + } + + function addLocationsDayEvent ($LocationsEvents, $dayEvents, $locations, $dn_day, $event) { + $pageId = $this->scheduleRoot->getPageId ($event); + resolve_pageid ($this->nameSpace, $pageId, $exists); + $logoId = $this->scheduleRoot->groupsDir.':'.$event->member.':'.$this->scheduleRoot->iconName; + $pageIdAt = $pageId.'-'.$event->at; + if ($dayEvents [$pageIdAt]) + $dayEvents [$pageIdAt]['logo'][] = $logoId; + else + $dayEvents [$pageIdAt] = + array ('pageId'=> $pageId, 'logo' => array ($logoId), + 'what'=> $event->what, 'where'=> $event->where, 'at' =>$event->at, 'title' => $event->title, + 'exists' => $exists); + if ($event->lat && $event->lon) { + foreach (array_combine (explode (',', $event->lat), explode (',', $event->lon)) as $lat => $lon) { + $location = '('.$lat.'|'.$lon.')'; + $LocationsEvents [$location][$dn_day][$pageIdAt] = $dayEvents [$pageIdAt]; + $locations .= ','.$location; + } + } elseif ($event->where) { + $location = $event->where; + $LocationsEvents [$location][$dn_day][$pageIdAt] = $dayEvents [$pageIdAt]; + $locations .= ','.$location; + } + // YYY sauf répétition de jours + return array ($LocationsEvents, $dayEvents, $locations); + } + + + function formatDayEventsBubble ($dn_day, $dayEvents) { + $content = ''; + $this->scheduleRoot->resetOddEven (); + foreach ($dayEvents as $pageIdAt => $values) { + $line = ''; + sort ($values['logo']); + foreach ($values['logo'] as $logoId) + $line .= ' '; + if ($values['at']) + $line .= $values['at']. ' '; + $line .= $values['what']; + if ($values['where']) + $line .= ' - '.$this->makeWhereString ($values['where']); + $line .= ' : '.$values['title']; + if ($values['exists']) + $line = '
    '.$line.''; + $content .= '
    '.$line.'
    '.NL; + $this->scheduleRoot->switchOddEven (); + } + if (!$content) + return ''; + return '
    '.date ($this->scheduleRoot->currentDateFormat['prettyPrint'], $dn_day).'
    '.$content; + } + + function getDayBubble ($dn_day, $dayEvents) { + uasort ($dayEvents, 'cmpScheduleInDay'); + $this->scheduleRoot->resetOddEven (); + $content = $this->formatDayEventsBubble ($dn_day, $dayEvents); + if (!$content) + return ''; + return '
    '.$content.'
    '; + } + + // ============================================================ + function getCityBubble ($LocationsEvents) { + $content = ''; + // XXX uasort ($dayEvents, 'cmpScheduleInCities'); + foreach ($LocationsEvents as $location => $datedEvents) { + $locationContent = ''; + foreach ($datedEvents as $dn_day => $dayEvents) + $locationContent .= $this->formatDayEventsBubble ($dn_day, $dayEvents); + if ($locationContent) + $content .= '
    '.$locationContent.'
    '; + } + return $content; + } + + // ============================================================ + /* Print schedule as a matrix or list */ + function printScheduleList ($mapId) { + if ($this->isAdmin ()) { + echo '
    '; + foreach (array ('clear', 'clearAll') as $action) + echo ''; + echo '
    '; + } + ptln ('
    '); + if ($this->printProp || $this->printForm) + $this->printScheduleInputForm ($mapId); + if ($this->printCtrl) + ptln ('
    '.NL. + '
    '.NL. + '
    '.NL. + ' '.NL. + ' '); + ptln (' '); + $this->printScheduleListHead (); + if ($this->isAdmin ()) + $this->printScheduleListContent ($this->allProposals, true); + $this->printScheduleListContent ($this->allSchedules); + $this->printScheduleDeleteForm (); + ptln ('
    '); + if ($this->printCtrl) + ptln ('
    '); + ptln ('
    '); + } + + // ============================================================ + /* Print schedule head list */ + function printScheduleListHead () { + global $conf; + + ptln (' '.NL. + ' '.NL. + ' '. + ($this->isShow ? + (''. + $this->getLang ('allDates').'') : + $this->getLang ('when')).''.NL. + ' '.$this->getLang ('what').''.NL. + ' '.$this->getLang ('where').''.NL. + ' '.$this->getLang ('title').''.NL. + ' '.NL. + ' '.NL. + ' '.$this->getLang ('audience').''.NL. + ' '.$this->getLang ('proposedBy').''.NL. + ' '); + if ($this->printCtrl) + ptln (''); + ptln (''.NL. + ' '.NL. + ' '); + } + + // ============================================================ + /* Print schedule line list */ + function printScheduleLineEvent ($pageId, $lineVals, $even, $prop = false) { + $ns = $this->nameSpace; + + $event = $lineVals['event']; + $propClass = $prop ? ' proposal' : ''; + + $day = $this->scheduleRoot->ds2df ($event->syncStart ? $event->syncStart : $event->from); + $overEvent=' onMouseOver="javascript:scheduleHighlightEvent (\''.$day.'\', \''.$this->getLocationsFromEvent ($event).'\')"'; + + ksort ($lineVals['member']); + ptln (' '.NL. + ' '); + if ($prop) + ptln ('proposition
    '); + ptln ($day.(($event->syncStart || ('' == $event->to)) ? '' : '
    '.NL.$this->scheduleRoot->ds2df ($event->to)).('' == $event->at ? '' : '
    '.NL.$event->at).''.NL. + ' '.$event->what.''.NL. + ' '.$this->makeWhereString ($event->where).''.NL. + ' '.$event->title.''.NL. + ' '.NL. + ' '.NL. + ' '.$event->audience.''.NL. + ' '); + if ($this->printCtrl) { + ptln ($this->scheduleRoot->scheduleShared[$event->shared]); + if (!$prop && !$lineVals['exists'] && $this->isAMemberOf (array_keys ($lineVals['member']))) { + // petite baguette magique de création + $id = reset (array_values($lineVals['member'])); + ptln (''. + ' '); + } + } + foreach ($lineVals['member'] as $member => $id) { + echo ''; + if ($this->printCtrl && $this->isMember ($member)) { + // suppression ou édition pour un membre + ptln (' '. + ''. + ''); + } + $logoId = $this->scheduleRoot->groupsDir.':'.$member.':'.$this->scheduleRoot->iconName; + // YYY + ptln (' + '); + } + ptln (''.NL. + ' '); + if ($event->repeatFlag) + ptln (' '.$this->makeRepeatString ($event, false)); + ptln (''.NL. + ' '); + } + + // ============================================================ + function makePrettyAt ($at) { + if (!$at) + return ''; + if (preg_match ("#(?[^-]*)-(?[^-]*)#", $at, $dumy)) + return ' '.$this->getLang ('fromHour').' **'.$dumy ['from'].'** '.$this->getLang ('toHour').' **'.$dumy ['to'].'**'; + return ' '.$this->getLang ('at').' **'.$at.'**'; + } + + function makeListPlaceForMap ($schedule) { + global $scheduleInseeCities; + $result= ''; + $cities = explode (',', $schedule->where); + $lats = explode (',', $schedule->lat); + $lons = explode (',', $schedule->lon); + $addrs = explode ('|', $schedule->addr); + $unknownAddr = false; + for ($i = 0; $i < count ($cities); $i++) { + if (!isset ($addrs [$i])) + $unknownAddr = true; + $result .= + $cities[$i].' | '. + (isset ($lats [$i]) ? $lats [$i] : '').' | '. + (isset ($lons [$i]) ? $lons [$i] : '').' | '. + (isset ($addrs [$i]) ? $addrs [$i] : ''). + NL; + } + if ($unknownAddr) + $result .= 'Les coordonnées par défaut correspondent aux centres-villes !'.NL; + return $result; + } + + // ============================================================ + /* Create a natural langage string for cities zipcode */ + function makeWhereString ($where) { + global $scheduleInseeCities; + $result = array (); + foreach (explode (',', $where) as $zip) + $result[] = (isset ($scheduleInseeCities[$zip]) ? + $scheduleInseeCities[$zip][0] : + ($this->showOldValues ? ''.$zip.'' : $zip)); + return implode (',', array_unique ($result)); + } + + // ============================================================ + /* Create a natural langage string to formulate repeat event */ + function makeRepeatString ($event, $bold = false) { + $orderedFormat = $this->getLang ('orderedFormat'); + $rt = $this->getLang ('repeatType'); + $result = ''; + switch ($event->repeatType) { + case 'day' : + return $this->getLang ('from').' '.$this->scheduleRoot->ds2ln ($event->from, $bold).' '.$this->getLang ('to').' '.$this->scheduleRoot->ds2ln ($event->to, $bold); + break; + // pour gagner du temps + case 'week' : + foreach (array_keys ($this->getLang ('days')) as $idd => $dayName) + $result .= in_array ($idd, explode ('|', $event->weekDays)) ? $dayName.' ' : ''; + break; + case 'dayMonth' : + $dayNames = array_keys ($this->getLang ('days')); + // XXX pb OVH => utilisation de tableau au lieu de fonction + // $result .= $f ($event->weekRank+1).' '.$d [$event->dayInWeek].' '; + $result .= $orderedFormat [$event->weekRank+1].' '.$dayNames [$event->dayInWeek].' '; + break; + case 'dateMonth' : + // XXX pb OVH => utilisation de tableau au lieu de fonction + // $result .= $f ($event->dayRank).' '; + $result .= $orderedFormat [$event->dayRank].' '; + break; + case 'year' : + // XXX le Ie jour du Je mois + preg_match ("#(?[0-9]{4})(?[0-9]{2})(?[0-9]{2})#", $event->from, $dt_from); + $dn_from = $this->scheduleRoot->dt2dn ($dt_from); + $result .= date ($this->scheduleRoot->currentDateFormat ['dayMonth'], $dn_from).' '; + } + $result .= $this->getLang ('all').' '; + if ($event->repeat > 1) + $result .= $event->repeat.' '; + $result .= $rt[$event->repeatType]; + + return $result; + } + + // ============================================================ + /* Add a schedule line list in array for futur print */ + function addEventTab (&$tab, $event, $when, $pageId, $eventClass, $exists, $cssLink) { + if (!$tab [$when]) + $tab [$when] = array (); + if ($tab [$when][$pageId]) + $tab [$when][$pageId]['member'][$event->member] = $event->id; + else + $tab [$when][$pageId] = + array ('member' => array ($event->member => $event->id), 'event' => $event, 'class' => $eventClass, 'exists' => $exists, 'css' => $cssLink); + } + + // ============================================================ + /* Print all schedule line list */ + function printScheduleListContent ($allSchedules, $prop = false) { + $linesTab = array (); + $repeatTab = array (); + if ($this->after) { + preg_match ("#(?[0-9]{4})(?[0-9]{2})(?[0-9]{2})#", $this->after, $dt_start); + $dn_start = $this->scheduleRoot->dt2dn ($dt_start); + } + foreach ($allSchedules as $event) { + // XXX même filtre pour printScheduleCalendar ? + if ($this->before && strcmp ($event->from, $this->before) > 0) + continue; + // XXX repeat le même jour => calcul + if ($this->date && (strcmp ($event->from, $this->date) > 0 || + strcmp (('' != $event->to ? $event->to : $event->from), $this->date) < 0)) + continue; + if ($this->after && + strcmp (('' != $event->to ? $event->to : $event->from), $this->after) < 0) + continue; + if ($this->max && $line > $this->max) + break; + $continue = false; + if ($this->filters) + foreach ($this->filters as $name => $values) { + if (in_array ($name, $this->scheduleRoot->filterNames) && !in_array ($event->$name, $values)) { + $continue = true; + break; + } elseif (in_array ($name, array_keys ($this->scheduleRoot->filterNames))) { + $trueName = $this->scheduleRoot->filterNames[$name]; + if (in_array ($event->$trueName, $values)) { + $continue = true; + break; + } + } + } + if ($continue) + continue; + $pageId = $this->scheduleRoot->getPageId ($event); + resolve_pageid ($this->nameSpace, $pageId, $exists); + $cssLink = $exists ? 'wikilink1' : 'wikilink2'; + $eventClass = 'cat_'.preg_replace ("/\s/", '', htmlspecialchars (strtolower ($this->scheduleRoot->scheduleWhat [$event->what]))); + $when = $event->from.'-'.$event->at.'-'.$event->to; + + if ($event->repeatFlag) { + if (!$this->after || strcmp ($event->from, $this->after) > 0) + $event->syncStart = $event->from; + else { + // cas ou il faut déplacer le début de la répétition (on ne déplace si la 1re occurance est après le début de la fenêtre) + preg_match ("#(?[0-9]{4})(?[0-9]{2})(?[0-9]{2})#", $event->from, $dt_from); + $dn_day = $dn_from = $this->scheduleRoot->dt2dn ($dt_from); + // on déplace sur la 1re occurance + switch ($event->repeatType) { + // chercher le 1er évènement après 'after' + case 'day' : + $step = 86400 * $event->repeat; + $dn_day += ceil (($dn_start - $dn_from) / $step) * $step; + break; + case 'week' : + $dn_startWeek = mktime (0, 0, 0, date ('m', $dn_from), date ('d', $dn_from) - date ('w', $dn_from), date ('Y', $dn_from)); + $step = 86400 * 7 * $event->repeat; + $dn_startWeek += floor (($dn_start - $dn_startWeek) / $step) * $step; + // au pire start commence après les jours répétés dans la semaine + for ($i = 0; $i < 2; $i++, $dn_startWeek += $step) { + foreach (explode ('|', $event->weekDays) as $idd) { + $dn_day = $dn_startWeek + $idd * 86400; + if ($dn_day >= $dn_start && $dn_day >= $dn_from) { + break 2; + } + } + } + break; + case 'dayMonth' : + case 'dateMonth' : { + $dn_startMonth = mktime (0, 0, 0, date ('m', $dn_from), 1, date ('Y', $dn_from)); + $nbMonth = floor ((date ('m', $dn_start) + date ('Y', $dn_start)*12 - (date ('m', $dn_from) + date ('Y', $dn_from)*12))/$event->repeat)*$event->repeat; + $dn_startMonth = mktime (0, 0, 0, date ('m', $dn_startMonth) + $nbMonth, 1, date ('Y', $dn_startMonth)); + for ($i = 0; $i < 2; $i++, $dn_startMonth = mktime (0, 0, 0, date ('m', $dn_startMonth) + $event->repeat, 1, date ('Y', $dn_startMonth))) { + $dn_day = $dn_startMonth; + if ($event->repeatType == 'dateMonth') + // XXX pb si moins de jour dans le mois + $dn_day = mktime (0, 0, 0, date ('m', $dn_startMonth), $event->dayRank, date ('Y', $dn_startMonth)); + else { + $delta = $event->dayInWeek - date ('w', $dn_startMonth); + if ($delta < 0) + $delta += 7; + $delta += 7 * $event->weekRank; + // XXX pb si moins de jour dans le mois + $dn_day = mktime (0, 0, 0, date ('m', $dn_startMonth), $delta+1, date ('Y', $dn_startMonth)); + } + if ($dn_day >= $dn_start && $dn_day >= $dn_from) + break; + } + break; + } + case 'year' : + $dayInmonth = date ('j', $dn_from); + $monthInYear = date ('n', $dn_from); + $startYear = date ('Y', $dn_start) - (date ('Y', $dn_start) - date ('Y', $dn_from)) % $event->repeat; + for ($i = 0; $i < 2; $i++, $startYear += $event->repeat) { + $dn_day = mktime (0, 0, 0, $monthInYear, $dayInmonth, $startYear); + if ($dn_day >= $dn_start && $dn_day >= $dn_from) + break; + } + break; + } + $ds_day = date ('Ymd', $dn_day); + if ($this->before && strcmp ($ds_day, $this->before) > 0) + continue; + if ($this->date && strcmp ($ds_day, $this->date) != 0) + continue; + $event->syncStart = $ds_day; + $when = $ds_day.'-'.$event->at.'-'.$event->to; + } + } + if ($this->repeatGrouped && $event->repeatFlag) + schedules::addEventTab ($repeatTab, $event, $when, $pageId, $eventClass, $exists, $cssLink); + else + schedules::addEventTab ($linesTab, $event, $when, $pageId, $eventClass, $exists, $cssLink); + } + ksort ($repeatTab); + ksort ($linesTab); + + $line = 1; + foreach ($repeatTab as $when) { + foreach ($when as $pageId => $lineVals) { + schedules::printScheduleLineEvent ($pageId, $lineVals, $even, $prop); + $this->scheduleRoot->switchOddEven (); + $line++; + } + } + foreach ($linesTab as $when) { + foreach ($when as $pageId => $lineVals) { + schedules::printScheduleLineEvent ($pageId, $lineVals, $even, $prop); + $this->scheduleRoot->switchOddEven (); + $line++; + } + } + if ($line < 1) + echo ' + '.$this->getLang ('noEvent').''; + } + + // ============================================================ + /* Print delete button in form */ + function printScheduleDeleteForm () { + if (!$this->printCtrl) + return; + echo ' + + + + + '; + } + + // ============================================================ + /* Print add event form */ + function printRepeatForm () { + $repeat = ($this->defaultSchedule->repeat > 1) ? $this->defaultSchedule->repeat : 1; + ptln(' + + + + + +'); + $disabled = ($this->defaultSchedule->repeatType !== "week") ? ' disabled="true"' : ''; + $eventWeekDays = explode ("|", $this->defaultSchedule->weekDays); + $checkbox = ""; + $dayNames = $this->getLang ('days'); + foreach (array_values ($dayNames) as $idd => $dayAbrv) { + ptln (' + +'); + $selected = in_array ($idd, $eventWeekDays) ? ' checked="true"' : ''; + $checkbox .= ' '.NL; + } + $disabled = ($this->defaultSchedule->repeatType !== "dayMonth") ? ' disabled="true"' : ''; + ptln (' + +'.$checkbox.' + + + +
    '.$this->getLang ('all').' + +
    '.$this->getLang ('each').''.$dayAbrv.'
    + + + +
    '); + } + + // ============================================================ + function printScheduleInputForm ($mapId) { + if (!($this->printForm || $this->printProp)) + return; + + $from = (!$this->defaultSchedule->from && isset ($_REQUEST["schd"]["date"])) ? + $_REQUEST["schd"]["date"] : + $this->scheduleRoot->ds2df ($this->defaultSchedule->from); + + ptln ('
    '.NL. + '

    '.$this->getLang ($this->printProp ? 'proposedEvent' : 'addEvent').'

    '.NL. // XXX getLang + ' '.NL. + + '
    '.NL. + '
    '.NL. + '
    '.NL. + '
    '.NL. + ' '.NL. + '
    '.NL. + '
    '.NL. + '
    '.NL. + + '
    '.NL. + '
    '.NL. + ' '.NL. + '

    '.NL. + ' '.$this->getLang ('lead').':
    '.NL. + ' '.NL. + '

    '.NL. + ' '.$this->getLang ('posterURL').':
    '.NL. + ' '.NL. + '

    '.NL. + ' '.$this->getLang ('paperURL').':
    '.NL. + ' '.NL. + '

    '.NL. + ' '.$this->getLang ('remark').':
    '.NL. + ' '.NL. + '

    '.NL. + ' '.$this->getLang ('rate').':
    '.NL. + ' '.NL. + '

    '.NL. + '
    '.NL. + '
    '.NL. + + '
    '.NL. + '
    '.NL. + '
    '.NL. + '
    '.NL. + '
    '.NL. + '
      '.NL); + + if ($this->defaultSchedule->where) { + global $scheduleInseeCities; + $cities = explode (',', $this->defaultSchedule->where); + $lats = explode (',', $this->defaultSchedule->lat); + $lons = explode (',', $this->defaultSchedule->lon); + $addrs = explode ('|', $this->defaultSchedule->addr); + for ($i = 0; $i < count ($cities); $i++) { + $insee = $cities[$i]; + $cityName = + isset ($scheduleInseeCities [$insee]) ? + $scheduleInseeCities [$insee][0] : + $insee; + ptln ('
    • '. + ''.(isset ($addrs [$i]) ? str_replace ('\\\\', '
      ', str_replace ('~br~', '
      ', $addrs [$i])) : "").'
      '. + ''.$cityName.'
    • '); + } + } + ptln ('
    '.NL. + ' '.NL. + ' '.$this->getLang ('enterTip').''.NL. + ' '.NL. + ' '.$this->getLang ('enterTip').''.NL. + '
    '.NL. + '
    '.NL. + '
    '.NL); + if ($this->isAdmin ()) + ptln ('
    '.NL. + ''.NL. + ''.NL. + ''.NL. + '
    '.NL); + ptln ('
    '.NL. + + '
    '.NL. + '
    '.NL. + '

    '.NL. + $this->getLang ('from').' '. + $this->getLang ('to').'

    '. + $this->getLang ('at').'

    '); + $this->printRepeatForm (); + ptln ('
    '.NL. + + '
    '.NL. + '

    '.NL. + + ' '.NL. + ' '.NL); + + if ($this->printForm) { + if ($this->defaultSchedule->id) { + ptln (''); + if (!$this->defaultSchedule->prop) { + if (count ($this->getMembersSharedEvents ($this->defaultSchedule)) > 1) + ptln (''); + ptln (' '); + } + } + ptln (''); + } + if ($this->printProp) { + ptln (' '); + if ($captcha =& plugin_load ('helper', 'captcha')) + ptln ($captcha->getHTML ()); + } + ptln ('

    '.NL. + '
    '.NL. + '
    '.NL. + '
    '.NL. + ' Aide'.NL. + '
    '.NL. + '
    '.NL. + ' '.NL. + ' '.NL. + ' '.NL. + ' '.NL. + ' '.NL. + '
    '.NL. + '
    '.NL. + '
    '.NL. + '
    '.NL); + } + + // ============================================================ + function resetAdminNotification () { + $this->lastNotificationReset = date ('YmdHis'); + $this->scheduleRoot->writeConfig ($this); + } + + function adminNotification () { + if ($this->lastNotification > $this->lastNotificationReset) + return; + + global $auth; + $users = $auth->retrieveUsers (); + foreach ($users as $user => $userinfo) { + $mailSubject = $this->getLang ('notifySubject'); + $mailContent = $this->getLang ('notifyContent'); + if (in_array (trim ($this->getConf ('adminGroup')), $userinfo ['grps'])) { + mail_send ($userinfo['mail'], $mailSubject, $mailContent); + } + } + + $this->lastNotification = date ('YmdHis'); + $this->scheduleRoot->writeConfig ($this); + } + + // ============================================================ +} diff --git a/script.js b/script.js new file mode 100644 index 0000000..e3a7da2 --- /dev/null +++ b/script.js @@ -0,0 +1,79 @@ +/** + * @license http://www.cecill.info/licences/Licence_CeCILL-B_V1-fr.html + * @author Francois Merciol + * + * Javascript functionality for the include plugin + */ + +/* DOKUWIKI:include_once lib/scheduleForm.js */ +/* DOKUWIKI:include_once lib/scheduleTable.js */ +/* DOKUWIKI:include_once ../ol3/script.js */ +/* DOKUWIKI:include_once lib/scheduleCitiesMap.js */ + +// ======================================== +// Ajax function +// ======================================== +/* Clear popup message */ +function scheduleClearMsgJ (item) { + item.closest ("div").find ("div.popup").each (function () { + jQuery (this).remove (); + }); +} + +function scheduleClearCache (action, ns) { + jQuery.ajax ({ + type: "POST", + url: DOKU_BASE+"lib/plugins/schedule/ajaxClearCache.php", + //success: function (response) { alert (response); }, + cache: true, + async: true, + data: "schedule[action]="+action+"&schedule[ns]="+ns + }); +} + +/* performe ajax request to swap month */ +function scheduleAjax (form, action, md5) { + var params = ""; + // XXX jquery + for (var idx = 0; idx < form.elements.length; idx++) { + var elem = form.elements[idx]; + if (elem.type == "checkbox") { + if (elem.checked) + params += "&"+elem.name+"="+elem.value; + } else if (elem.type == "select" || elem.type == "select-multiple") { + while (elem.options.selectedIndex >= 0) { + opt = elem.options [elem.options.selectedIndex]; + params += "&"+elem.name+"="+opt.text; + elem.options [elem.options.selectedIndex] = null; + } + } else + params += "&"+elem.name+"="+elem.value; + } + params += "&schd[action]="+action+"&schd[md5]="+md5; + + scheduleSend (form, + DOKU_BASE+"lib/plugins/schedule/ajax.php", + params); + return false; +} + +function scheduleSend (sender, uri, params) { + var jDiv = jQuery (sender).closest ("div"); + scheduleClearMsgJ (jDiv); + jQuery (''). + insertBefore (jDiv.children ().first ()); + jQuery.ajax ({ + type: "POST", + url: uri, + cache: false, + async: true, + data: params, + success: function (response) { + jDiv.html (response); + scheduleInitMaps (); + scheduleInitPOI (); + } + }); +} + +// ======================================== diff --git a/style.css b/style.css new file mode 100644 index 0000000..91a8631 --- /dev/null +++ b/style.css @@ -0,0 +1,528 @@ +/* + * @license http://www.cecill.info/licences/Licence_CeCILL-B_V1-fr.html + * @author Francois + */ + +div.schedule { + margin: 1em; +} + +div.schedule table { + border: 0 none; + border-spacing: 0; +} + +div.schedule table th, div.schedule table th { + border: 0 none; + text-align: center; + margin: 0; + padding: 0; +} + +/* ==================== */ +div.schedule table.admin { + border-collapse: collapse; + border-spacing: 0; +} +div.schedule table.admin tr { + border: 0 none; + text-align: center; + vertical-align: middle; + margin: 0 4px; + padding: 0 4px; +} +div.schedule table.admin tr.even { + background-color: #DDD; +} +div.schedule table.admin tr td.name { + text-align: right; +} +div.schedule table.admin tr td.value { + text-align: left; + font-weight: bold; +} +div.schedule table.admin tr td input { + width: 100%; +} +div.schedule table.admin select { + width: 100%; +} + +/* ==================== */ +div.schedule table.long { + border-collapse: collapse; + border-spacing: 0; +} + +div.schedule table.long tr.odd { + vertical-align: middle; +} + +div.schedule table.long tr.even { + vertical-align: middle; + background-color: #DDD; +} + +div.schedule table.long tr th, +div.schedule table.long tr td { + border: 0 none; + text-align: center; + margin: 0 4px; + padding: 0 4px; +} + +/* ======================================== + * Depend on "scheduleWhat" configuration + * ======================================== */ + +div.schedule table.long tr.cat_a td.what { + padding-left: 20px; + background-image: url("images/bul_a.png"); + background-repeat: no-repeat; +} + +div.schedule table.long tr.cat_b td.what { + padding-left: 20px; + background-image: url("images/bul_b.png"); + background-repeat: no-repeat; +} + +div.schedule table.long tr.cat_c td.what { + padding-left: 20px; + background-image: url("images/bul_c.png"); + background-repeat: no-repeat; +} + +/* ==================== */ +div.schedule table.short { + margin: auto; + border: 0 none; + border-spacing: 0; +} + +div.schedule table.short th, div.schedule table.short td { + border: 0 none; + text-align: center; + margin: 0; + padding: 0; + width: 1em; + height: 1em; + z-index: 1; +} + +div.schedule table.short td a { + font-weight: bold; +} + +div.schedule table.short td.week { + margin: 2px 0; + color: #888; +} + +div.schedule table.short td.empty { + opacity: 0.4; +} + +div.schedule table.short td.weekend { + background-color: #CCC; +} +div.schedule table.short td.outofscope { + text-decoration: overline line-through underline; + background-color: #888; + color: #666; +} +div.schedule table.short td.free { + background-color: white; +} + +div.schedule table.short caption { + font-weight: bold; + text-align: center; +} +div.schedule table.short caption.scheduleShow { + border: 2px solid orange; +} +div.schedule table.short caption.scheduleToday { + border: 2px solid red; +} + +/* ======================================== + * Depend on "scheduleWhat" configuration + * ======================================== */ + +div.schedule table.short td.cat_a { + font-weight: bold; + background: url("images/cat_a.png") repeat; +} + +div.schedule table.short td.cat_b { + background: url("images/cat_b.png") repeat; +} + +div.schedule table.short td.cat_c { + background: url("images/cat_c.png") repeat; +} + +div.schedule table.short td.cat_ab { + background: url("images/cat_ab.png") repeat; +} + +div.schedule table.short td.cat_ac { + background: url("images/cat_ac.png") repeat; +} + +div.schedule table.short td.cat_bc { + background: url("images/cat_bc.png") repeat; +} + +div.schedule table.short td.cat_abc { + background: url("images/cat_abc.png") repeat; +} + +div.schedule table.short td.op_1 { + opacity: 1; +} + +div.schedule table.short td.op_2 { + opacity: 1; +} + +div.schedule table.short td.op_3 { + opacity: 1; +} + +/* ======================================== + * + * ======================================== */ +div.schedule .popup { + position: absolute; + top: auto; + left: auto; + width: auto; + height: auto; + margin: 1em; + padding: 1em; + overflow: visible; + z-index: 1000; + opacity: 0.7; + background: #EDDB31; + color: black; + border: 1px solid #AE090D; + font-weight: bold; +} + +div.bubble img { + vertical-align: middle; +} + +div.bubble a, +div.bubble a:visited, +div.bubble a:hover, +div.bubble a:active, +div.bubble a:link { + /*cursor: pointer;*/ + display: block; + text-decoration: none ! important; + font-weight: bold; + color: #AE090D ! important; +} + +div.bubble div.date { + font-weight: bold; + text-align: center; +} +div.bubble div.odd { + background-color: #FDD ! important; + padding: 0 4px; +} +div.bubble div.even { + background-color: #FE5 ! important; + padding: 0 4px; +} + +div.schedule table.short caption div div.bubble, +div.schedule table.short td div.bubble { + position: absolute; + z-index: 1000; + opacity: 0.8; + background: #EDDB31; /* Définition du fond, sinon on a le même que notre lien */ + color: black; + border: 1px solid #AE090D; /* Définition des bordures */ + text-align: left; + vertical-align: middle; + padding: 0; + font-size: 75%; + top: auto; + overflow: visible; + width: 16em; + height: auto; + margin-top: 1em; + + display: none; +} + +div.schedule table.short div.leftBubble { + left: -13em; +} + +div.schedule table.short div.rightBubble { + left: 18em; +} + +/* Rend visible tout notre bloc bubble et lui attribue une taille */ +div.schedule .showBubbleOnFocus:hover div.bubble, +div.schedule .showBubbleOnFocus:focus div.bubble, +div.schedule .showBubbleOnFocus:active div.bubble { + display: block; +} +div.schedule table.short td div.bubble a img { + opacity: 1; +} + +div.schedule table.short div.over { + position: absolute; + top: auto; + left: auto; + z-index: 900; +} + +div.schedule table.short td div.past { + font-weight: bold; + color: #FFF; + background-color: #444; + opacity: 0.7; + margin: 0 0 0 3px !important; +} + +div.schedule table.short td div.scheduleToday { + opacity: 1; + z-index: 3; + font-weight: bold; + margin: -3px -4px 0 0; + border: 3px solid red; + padding: 0; + color: black; +} + +div.schedule table.short td div.scheduleShow { + opacity: 1; + z-index: 3; + font-weight: bold; + margin: -3px -4px 0 0; + border: 3px solid orange; + padding: 0; + color: black; +} + +div.schedule table.short td div.mapSelect { + z-index: 4; + font-weight: bold; + margin: -3px -4px 0 0; + border: 3px solid #F0F; + padding: 0; + color: #F0F; +} + +/* ==================== */ +form.scheduleCitiesForm input { + width: 26em; +} + +div.schedule table.form select, +div.schedule table.form input { + font-size: 100%; +} + +div.scheduleTabForm textarea { + width: 100%; + height: 5em; +} + +div.scheduleTabForm input.lat { + width: 4em; +} +div.scheduleTabForm input.lon { + width: 4em; +} + +div.scheduleTabForm input.date { + width: 6em; +} +div.scheduleTabForm input.where { + width: 100%; +} + +div.scheduleTabForm input.hour { + width: 10em; +} + +div.scheduleTabForm input.repeat { + width: 2em; +} + +div.scheduleTabForm td.repeat { + text-align: left; +} + +div.scheduleTabForm div >* { + vertical-align: top; +} +div.scheduleTabForm p >* { + vertical-align: top; +} + +div.scheduleTabForm img.checked { + height: 16px; + background-image: url("images/poi.png"); /* poi.png */ + background-size: 10px 16px; + background-repeat: no-repeat; +} +div.scheduleTabForm li:hover img.checked { + background-image: url("images/poiRed.png"); /* selectedPoi.gif */ +} + +/* ==================== */ + +.ui-accordion .ui-accordion-content { + height: auto !important; +} + +.scheduleCitiesForm ul { + /*padding: 0 !important;*/ + margin: 0 !important; +} +.scheduleCitiesForm ul li { + margin-left: 20px; +} +.scheduleCitiesForm ul img { + margin-left: -20px; +} + +.scheduleCitiesForm .ui-widget>* { + display: block; +} + +.scheduleCitiesForm div>div { + vertical-align: top; + display: inline-block; +} + +.scheduleCitiesForm .cities { + min-height: 4em; + max-height: 4em; + overflow-y: scroll; + overflow-x:hidden; + border-style: solid; + border-color: grey; + border-width: 1px; + padding: 1px; +} + +.scheduleCitiesForm .cities li { + list-style-type:none; +} + +.scheduleCitiesForm .unknown { + background-color: #FDD; +} + +div.schedule table.short div.tabSelect { + position: absolute; + top: auto; + left: auto; + z-index: 900; + margin-top: -16px; + width: 16px; + height: 16px; + min-width: 16px; + min-height: 16px; + background-image: url("images/cat_c.gif"); /* selectedPoi.gif */ + background-size: 10px 16px; +} + +/* ==================== */ +.schedule .scheduleMap { + width: 150px; + height: 150px; +} + +.schedule div.olControlPanZoom { + display: none; +} + +.scheduleScaledMap .scheduleMap { + width: 200px; + height: 300px; +} + +.scheduleScaledMap div.olControlPanZoom { + opacity: 0.2; +} + +div.scheduleMap div.olControlAttribution, +div.scheduleMap div.olControlScaleLine { + font-family: Verdana; + font-size: 0.5em; + right: 1px; + bottom: 1px; +} + +ul.ui-autocomplete { + max-height: 10em; + overflow-y:scroll; + overflow-x:hidden; +} + +.poiPinPush { + background-image: url("images/cat_ab.png"); /* poi.png */ + opacity: 0.6; +} +.poiPinPop { + background-image: url("images/cat_ac.png"); /* poi.png */ + background-size: 10px 16px; +} +.poiPinLight { + background-image: url("images/cat_bc.png"); /* poi.png */ + opacity: 0.3; +} + +/* ==================== */ +.scheduleAddresse { + width: 16em; + max-width: 16em; + min-height: 8em; + text-align: left; +} + +.proposal { + border-width: 2px; + border-style: solid none; + border-color: orange; +} + +div.scheduleTabForm div.warningPlace { + display: inline-block; +} +div.scheduleTabForm div.warning { + width: 16px; + height: 16px; +} +div.schedule table.long tr.proposal, +div.scheduleTabForm div.warning, +div.scheduleTabForm a.warning, +div.scheduleTabForm input.warning, +div.scheduleTabForm ul.warning { + background-image: url("images/warning.png") !important; + background-repeat: no-repeat !important; +} +div.scheduleTabForm input.warning { + border: 2px solid yellow !important; + background-position: right center !important; +} + +div.scheduleAddresse { +} + +div.scheduleAddresse.poiAdrLight { + background-color: #DDD; +} diff --git a/syntax/block.php b/syntax/block.php new file mode 100644 index 0000000..89f01d9 --- /dev/null +++ b/syntax/block.php @@ -0,0 +1,205 @@ + + * + * Plugin Schedule: manage events per wiki @groups + */ + +if (!defined ('DOKU_INC')) + define ('DOKU_INC', realpath (__DIR__.'/../../../').'/'); +if (!defined ('DOKU_PLUGIN')) + define ('DOKU_PLUGIN', DOKU_INC.'lib/plugins/'); +require_once(DOKU_PLUGIN.'syntax.php'); + +require_once (realpath (__DIR__.'/..').'/schedule.class.php'); +require_once (realpath (__DIR__.'/..').'/scheduleRoot.class.php'); +require_once (realpath (__DIR__.'/..').'/schedules.class.php'); + +// ============================================================ +class syntax_plugin_schedule_block extends DokuWiki_Syntax_Plugin { + + // ============================================================ + function getType () { return 'substition'; } + function getPType () { return 'block'; } + function getSort () { return 299; } + function connectTo ($mode) { + $this->Lexer->addSpecialPattern ('\{\{schedule[^}]*\}\}', $mode, 'plugin_schedule_block'); + } + + // ============================================================ + function handle ($match, $state, $pos, Doku_Handler $handler) { + switch ($state) { + case DOKU_LEXER_SPECIAL : + return array ($state, trim (substr ($match, 10, -2))); // "{{schedule" => 10 "}}" => 2 + } + return false; + } + + var $scheduleRoot; + var $schedules; + + // ============================================================ + function render ($mode, Doku_Renderer $renderer, $indata) { + $dumy = ""; + if (empty($indata)) + return false; + if ($mode != 'xhtml') + return false; + list ($instr, $data) = $indata; + switch ($instr) { + case DOKU_LEXER_SPECIAL : + $args = " ".$data." "; + if (preg_match_all ("#(\"[^\"]*\")* help (\"[^\"]*\")*#", strtolower ($args), $dumy) > 0) { + $renderer->doc .= $this->scheduleHelp (); + return true; + } + + $this->scheduleRoot = new scheduleRoot ($this); + + if (preg_match_all ("#(\"[^\"]*\")* admin (\"[^\"]*\")*#", strtolower ($args), $dumy) > 0) { + ob_start (); + echo '
    '.NL; + $this->scheduleRoot->manageAction ($_REQUEST['schd']); + $this->scheduleRoot->printForm (); + echo '
    '; + $text = ob_get_contents (); + ob_end_clean (); + foreach ($this->scheduleRoot->message as $type => $msg) + $text = '
    '.$msg.'
    '.$text; + $renderer->doc .= $text; + return true; + } + + // namespace + global $ID; + $ns = getNS ($ID); + if (preg_match_all ("#^ >([^ ]*) (.*)#", $args, $dumy) > 0) { + $ns = $dumy[1][0]; + $args = ' '.$dumy[2][0]; + if (($ns == '*') || ($ns == ':')) + $ns = ''; + elseif ($ns == '.') + $ns = getNS ($ID); + else + $ns = cleanID ($ns); + } + $mapId = ""; + if (preg_match_all ('/("[^"]*")* id="(?[0-9a-zA-Z_]+)" ("[^"]*")*/', strtolower ($args), $dumy) > 0) + for ($i = 0; $i < count ($dumy['id']); $i++) + $mapId = $dumy ['id'][$i]; + $this->schedules = new schedules ($this->scheduleRoot, $ns); + $renderer->info ['cache'] = FALSE; + $request = false; + global $_REQUEST; + if ((isset ($_REQUEST['schd']) && isset ($_REQUEST['schd']['ns']) && $_REQUEST['schd']['ns'] == $ns) || + (preg_match_all ("#(\"[^\"]*\")* (prop|form|ctrl) (\"[^\"]*\")*#", strtolower ($args), $dumy) > 0)) + $request = true; + if ($this->getConf ('useMap') && !is_dir (__DIR__.'/../../ol3')) + $renderer->doc .= '

    ol3 plugin not installed (see doc)

    '; + $cache = $this->schedules->readCache ($args); + if ($cache) { + $renderer->doc .= $cache; + return true; + } + $this->schedules->load (); + ob_start (); + if (preg_match_all ("#(\"[^\"]*\")* table (\"[^\"]*\")*#", strtolower ($args), $dumy) > 0) { + echo '
    '.NL; + $this->schedules->printScheduleCalendar (($mapId ? $mapId : "scheduleMapTable"), mktime (0, 0, 0, date ("n"), date ("j"), date ("Y"))); + echo '
    '; + } else { + $formLevel = 0; + // form + if (preg_match_all ("#(\"[^\"]*\")* prop (\"[^\"]*\")*#", strtolower ($args), $dumy) > 0) + $formLevel = max ($formLevel, 3); + if (preg_match_all ("#(\"[^\"]*\")* form (\"[^\"]*\")*#", strtolower ($args), $dumy) > 0) + $formLevel = max ($formLevel, 2); + if (preg_match_all ("#(\"[^\"]*\")* ctrl (\"[^\"]*\")*#", strtolower ($args), $dumy) > 0) + $formLevel = max ($formLevel, 1); + $this->scheduleDateFilters ($args); + $this->schedules->setFormLevel ($formLevel); + if ($request) + $this->schedules->manageAction ($_REQUEST['schd']); + $this->scheduleOtherFilters ($args); + $this->schedules->printScheduleList (($mapId ? $mapId : "scheduleMapList")); + } + $text = ob_get_contents (); + ob_end_clean (); + if (!$request) + $this->schedules->writeCache ($args, $text); + + $this->schedules->writeSchedules (); + $this->schedules->writeProp (); + foreach ($this->scheduleRoot->message as $type => $msg) + $text = '
    '.$msg.'
    '.$text; + $renderer->doc .= $text; + } + return true; + } + + // ============================================================ + function scheduleHelp () { + $url = "http://admin.parlenet.org/plugins/schedule/"; + return + '

    Help Schedule

    '.NL. + '
      '.NL. + '
    • {{schedule help }}
    • '.NL. + '
    • {{schedule admin }}
    • '.NL. + '
    • {{schedule>nameSpace table [(<|=|>)(!|+-delta|jj/mm/aa|jj/mm/aaaa)] }}
    • '.NL. + '
    • {{schedule>nameSpace id="id" [(<|=|>)(!|+-delta|jj/mm/aa|jj/mm/aaaa)] [#maxLine] [prop|form|ctrl] [(member|what|audience|noMember|noWhat|noAudience)="x1,x2,..."] }}
    • '.NL. + '
    '.NL. + '

    '.$url.'

    '.NL; + } + + // ============================================================ + function scheduleDateFilters ($args) { + $before = ""; + $date = ""; + $after = ""; + $periode = array ("<" => &$before, "=" => &$date, ">" => &$after); + $dumy = ""; + // ! + if (preg_match_all ("#(\"[^\"]*\")*(?[<=>])!(\"[^\"]*\")*#", $args, $dumy) > 0) + $periode [$dumy ['op'][0]] = "!"; + // <+j =+j >+j <-j =-j >-j + if (preg_match_all ("#(\"[^\"]*\")*(?[<=>])(?[+\-][0-9]*)(\"[^\"]*\")*#", $args, $dumy) > 0) + for ($i = 0; $i < count ($dumy['op']); $i++) + $periode [$dumy ['op'][$i]] = $dumy ['delta'][$i]; + // jj/mm/aaa + if (preg_match_all ("# (?[<=>])(?[0-9]{1,2}[-/][0-9]{1,2}[-/][0-9]{1,4}) #", $args, $dumy) > 0) + for ($i = 0; $i < count ($dumy['op']); $i++) + $periode [$dumy ['op'][$i]] = $dumy ['date'][$i]; + $this->schedules->setDateFilters ($before, $date, $after); + } + + // ============================================================ + function scheduleOtherFilters ($args) { + $max = ""; + $filterNames = array ("member", "what", "audience", "noMember", "noWhat", "noAudience"); + $filters = array (); + $grouped = ""; + $dumy = ""; + + // maxline + if (preg_match_all ("/(\"[^\"]*\")*#(?[0-9]+)(\"[^\"]*\")*/", strtolower ($args), $dumy) > 0) + for ($i = 0; $i < count ($dumy['max']); $i++) + $max = $dumy ['max'][$i]; + // filters + foreach ($filterNames as $filterName) { + if (preg_match_all ("#".$filterName."=\"(?<".$filterName.">[^\"]*)\"#", $args, $dumy) > 0) { + $sep = ""; + for ($i = 0; $i < count ($dumy[$filterName]); $i++) { + $filters [$filterName] .= $sep.trim (trim ($dumy[$filterName][$i]), ','); + $sep = ','; + } + } + } + // grouped + if (preg_match_all ("#(\"[^\"]*\")* (?grouped|isolated) (\"[^\"]*\")*#", strtolower ($args), $dumy) > 0) + $grouped = $dumy ['grouped'][0]; + $this->schedules->setOtherFilters ($max, $filters, $grouped); + } + + // ============================================================ +} diff --git a/syntax/display.php b/syntax/display.php new file mode 100644 index 0000000..dbcc986 --- /dev/null +++ b/syntax/display.php @@ -0,0 +1,136 @@ + + * + * Plugin Schedule: manage events per wiki @groups + */ + +if (!defined ('DOKU_INC')) + define ('DOKU_INC', realpath (__DIR__.'/../../../').'/'); +if (!defined ('DOKU_PLUGIN')) + define ('DOKU_PLUGIN', DOKU_INC.'lib/plugins/'); +require_once(DOKU_PLUGIN.'syntax.php'); + +require_once (realpath (__DIR__.'/..').'/scheduleInseeCities.php'); + +// ============================================================ +class syntax_plugin_schedule_display extends DokuWiki_Syntax_Plugin { + + // ============================================================ + function getInfo() { + return confToHash (__DIR__.'/../INFO.txt'); + } + function getType () { return 'substition'; } + function getPType () { return 'block'; } + function getSort () { return 299; } + function connectTo ($mode) { + $this->Lexer->addEntryPattern (']*>', $mode, 'plugin_schedule_display'); + } + function postConnect () { + $this->Lexer->addExitPattern ('', 'plugin_schedule_display'); + } + + // ============================================================ + function handle ($match, $state, $pos, Doku_Handler $handler) { + switch ($state) { + case DOKU_LEXER_ENTER: + return array ($state, trim (substr ($match, 8, -1))); // " 8 ">" => 1 + case DOKU_LEXER_UNMATCHED: + return array ($state, $match); + case DOKU_LEXER_EXIT: + return array ($state, ''); + } + return false; + } + + // ============================================================ + function render ($mode, Doku_Renderer $renderer, $indata) { + if (empty($indata)) + return false; + if ($mode != 'xhtml') + return false; + list ($instr, $data) = $indata; + switch ($instr) { + + case DOKU_LEXER_ENTER : + $args = strtolower (" ".$data." "); + $mapId = "scheduleMapPOI"; + $width = ""; + $height = ""; + if (preg_match_all ('/("[^"]*")* id="(?[0-9a-zA-Z_]+)" ("[^"]*")*/', $args, $dumy) > 0) + for ($i = 0; $i < count ($dumy['id']); $i++) + $mapId = $dumy ['id'][$i]; + if (preg_match_all ('/.* width=(?[0-9]+) .*/', $args, $dumy) > 0) + for ($i = 0; $i < count ($dumy['width']); $i++) + $width=' width="'.$dumy ['width'][$i].'"'; + if (preg_match_all ('/.* height=(?[0-9]+) .*/', $args, $dumy) > 0) + for ($i = 0; $i < count ($dumy['height']); $i++) + $height=' height="'.$dumy ['height'][$i].'"'; + $renderer->doc .= + '
    '.NL. + '
    '.NL; + if ($this->getConf ('useMap')) { + if (!is_dir (realpath (__DIR__.'/../../ol3'))) + $renderer->doc .= '

    ol3 plugin not installed (see doc)

    '; + $renderer->doc .= '
    '.NL; + } + break; + + case DOKU_LEXER_UNMATCHED : + $data = trim ($data); + $address =""; + if ($this->getConf ('useMap')) + $renderer->doc .= ''.$this->getLang ('reactiveMap').''.NL; + $renderer->doc .= ' '.NL. + '
    '.NL. + '
    '.NL. + $address. + '
    '.NL; + break; + case DOKU_LEXER_EXIT : + break; + } + return true; + } + + // ============================================================ +}