├── .gitattributes ├── .gitignore ├── LICENSE ├── README.md ├── composer.json ├── composer.lock ├── mapall_setupdatabase.sql ├── private ├── init_example.php ├── layout │ ├── header.php │ └── navigate.php ├── setup.php ├── shared │ └── mapall_functions.php ├── updatemap.php └── wiki.php └── public_html ├── .vscode └── launch.json ├── about.php ├── css ├── leaflet-search.css ├── screen.css └── style.css ├── dave.php ├── dist ├── MarkerCluster.Default.css ├── MarkerCluster.css ├── leaflet-search.js ├── leaflet.featuregroup.subgroup.js ├── leaflet.markercluster-src.js ├── leaflet.markercluster-src.js.map ├── leaflet.markercluster.js ├── leaflet.markercluster.js.map ├── leaflet.spin.min.js ├── spin.js ├── spin.min.js └── subgroup.js ├── faq.php ├── favicon.ico ├── heatmap ├── colors.inc.php ├── index.css ├── index.php ├── json.php ├── open.php ├── opendata-logo.jpg ├── sakura.css ├── show.css ├── show.php └── stats.php ├── hswikilist.php ├── image ├── fablab.png ├── github-white.png ├── hs.png ├── hsWikiLogo.png ├── hs_black.png ├── hs_closed.png ├── hs_open.png ├── hslogo.png ├── loader.gif ├── search-icon-mobile.png └── search-icon.png ├── index.php ├── login.php ├── onespace.php ├── spaceapi ├── index.php ├── json.php └── spacestatus.txt ├── testapi.php └── wikiupdate.php /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Subliem Workspace files are user-specific 2 | *.sublime-workspace 3 | *.sublime-project 4 | # VS code workspace 5 | *.code-workspace 6 | /vscode/* 7 | 8 | #mac os 9 | .DS_Store 10 | 11 | 12 | # Composer components 13 | /vendor/ 14 | 15 | #data files for this project 16 | database.db 17 | *.geojson 18 | /public_html/*.json 19 | settings.php 20 | test.php 21 | errorlog.txt 22 | wikilog.txt 23 | cookie.txt 24 | private/init.php 25 | privatecookie.txt 26 | 27 | # local ssl for testing 28 | ssl cert/* 29 | *.crt 30 | *.key 31 | 32 | .vscode/ 33 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # map-all-spaces 2 | 3 | This site exist of tree parts: the map, the heatmap and the hackerspace wiki cences. 4 | 5 | ## Map 6 | 7 | Map with all hackerspaces, fablabs and makerspaces. Check it out [here](https://mapall.space)! 8 | 9 | Combine several sources and put them together on one map. This will result in a up-to-date map. 10 | 11 | Sources : 12 | - [SpaceAPI](https://spaceapi.io/) 13 | - [Fablab.io](https://fablab.io) 14 | - [Fablab Quebec (Canada)](https://wiki.fablabs-quebec.org/) 15 | - [Hackerspace.org wiki](https://wiki.hackerspaces.org) 16 | 17 | If you want to be added/removed/changed check one of the above source and modify it there. 18 | 19 | For details about what 'rules' apply check the FAQ html page. 20 | 21 | Any questions or remarks contact me mapall@daveborghuis.nl. 22 | 23 | ## Heatmap 24 | See heatmap of open/closed status of spaces with a spaceapi. Api status is read every 10 min and stored in database, statistics is calculated once you request page. Original made by [Folkert van Heusden](https://github.com/folkertvanheusden/spaceapi), integrated into mapall site on end september 2021. 25 | 26 | ## Hackerspace Cencus 27 | Check if a hackerspace is still active (and edit status if not) 28 | This is done by reading wiki page of space and determine if site is up, twitter/mastadon is active and more. If program is sure of the activity the wiki page is set to status 'suspented inactivity' or 'active'. 29 | 30 | Source : 31 | * [Hackerspace.org wiki](https://wiki.hackerspaces.org) 32 | 33 | Create bot via 34 | * https://wiki.hackerspaces.org/Special:BotPasswords 35 | 36 | Curl Errors 37 | * https://curl.haxx.se/libcurl/c/libcurl-errors.html 38 | 39 | ### Instal for local use/test 40 | Clone this github to a local directory. I asume you have php and composer installed. 41 | 42 | -- run 'composer install' to install database libary's. 43 | - Copy 'init_example.php' to 'init.php' and change paths to reflect your local system. Add the needed api keys/logins. 44 | - Run 'mapall_setupdatabase.sql' to set up database and used tables. 45 | - You can now get all the data and proces it with 'php update.php'. If you run it for the first time you want to use the options "--init --all" 46 | - go to 'public_html' directory and use the php server to enjoy the results ('php -S localhost:8000') 47 | 48 | Options of update.php 49 | * --all Process all options 50 | * --wiki Update data from wiki 51 | * --fablab Update data from fablab.io 52 | * --log=0 Define loglevel, 0 for all message, 5 only errors 53 | * --init Delete all records and logfile 54 | * --api Spaceapi 55 | * --comp Dedupe wiki 56 | 57 | With the '--all' option it will : 58 | * -get api data, check if json could be retrieved and procesed. 59 | * -get fablab.io data, only include the 'active' fablabs 60 | * -get wiki.hackerspaces.org, only active hackerspaces with a location 61 | * -dedupe above resuls, check name match 45% and distance <200m 62 | 63 | You can only do one part of the processing by giving eg '--wiki' to do only the part of getting and processing the wiki.hackerspaces.org data. 64 | 65 | The above steps will generate \*.geojson files that will be read by the maps leaflet. You have to have a webserver that points to the 'public_html' directory of alternative you can use the php server to do this. 66 | 67 | 68 | ### Used components 69 | 70 | - [Leaflet](https://leafletjs.com/) 71 | - [Control groups](https://github.com/Leaflet/Leaflet.markercluster) 72 | - [Search](https://github.com/stefanocudini/leaflet-search) 73 | - [database layer](http://github.com/joshcam/PHP-MySQLi-Database-Class) installed via composer 74 | 75 | ## API 76 | 77 | You can use the JSON API of this repository: 78 | 79 | - url: `https://mapall.space/heatmap/json.php` 80 | - parameter `id`: id of the hackerspace, example: `TkkrLab` 81 | - parameter `period`: period of data, one of `week`, `month`, `year`, `everything` 82 | 83 | example: https://mapall.space/heatmap/json.php?id=TkkrLab&period=week 84 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "require": { 3 | "thingengineer/mysqli-database-class": "dev-master" 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /composer.lock: -------------------------------------------------------------------------------- 1 | { 2 | "_readme": [ 3 | "This file locks the dependencies of your project to a known state", 4 | "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", 5 | "This file is @generated automatically" 6 | ], 7 | "content-hash": "10b28516f81d5eda89055c27ffb0b913", 8 | "packages": [ 9 | { 10 | "name": "thingengineer/mysqli-database-class", 11 | "version": "dev-master", 12 | "source": { 13 | "type": "git", 14 | "url": "https://github.com/ThingEngineer/PHP-MySQLi-Database-Class.git", 15 | "reference": "5159467ae081adbe96586e45e65a58c0fe7a32ce" 16 | }, 17 | "dist": { 18 | "type": "zip", 19 | "url": "https://api.github.com/repos/ThingEngineer/PHP-MySQLi-Database-Class/zipball/5159467ae081adbe96586e45e65a58c0fe7a32ce", 20 | "reference": "5159467ae081adbe96586e45e65a58c0fe7a32ce", 21 | "shasum": "" 22 | }, 23 | "require": { 24 | "php": ">=5.3.0" 25 | }, 26 | "default-branch": true, 27 | "type": "library", 28 | "autoload": { 29 | "files": [ 30 | "MysqliDb.php", 31 | "dbObject.php" 32 | ] 33 | }, 34 | "notification-url": "https://packagist.org/downloads/", 35 | "license": [ 36 | "GPL-3.0-or-later" 37 | ], 38 | "authors": [ 39 | { 40 | "name": "Josh Campbell", 41 | "email": "josh.lee.campbell@gmail.com", 42 | "homepage": "https://github.com/thingengineer", 43 | "role": "Developer" 44 | }, 45 | { 46 | "name": "Alexander V. Butenko", 47 | "email": "a.butenka@gmail.com", 48 | "homepage": "http://smarttechdo.com", 49 | "role": "Developer" 50 | } 51 | ], 52 | "description": "PHP MySQL Wrapper and object mapper which utilizes MySQLi and prepared statements", 53 | "support": { 54 | "issues": "https://github.com/ThingEngineer/PHP-MySQLi-Database-Class/issues", 55 | "source": "https://github.com/ThingEngineer/PHP-MySQLi-Database-Class/tree/master" 56 | }, 57 | "time": "2022-09-13T15:42:11+00:00" 58 | } 59 | ], 60 | "packages-dev": [], 61 | "aliases": [], 62 | "minimum-stability": "stable", 63 | "stability-flags": { 64 | "thingengineer/mysqli-database-class": 20 65 | }, 66 | "prefer-stable": false, 67 | "prefer-lowest": false, 68 | "platform": [], 69 | "platform-dev": [], 70 | "plugin-api-version": "2.3.0" 71 | } 72 | -------------------------------------------------------------------------------- /mapall_setupdatabase.sql: -------------------------------------------------------------------------------- 1 | -- setup tables for mapall database 2 | -- apply these manual 3 | CREATE IF NOT EXISTS DATABASE mapall; 4 | USE mapall; 5 | 6 | CREATE IF NOT EXISTS TABLE heatmspaces 7 | ( 8 | `key` varchar(32) not null 9 | primary key, 10 | name varchar(256) null, 11 | url mediumtext null, 12 | logo mediumtext not null, 13 | get_ok int(6) default 0 not null, 14 | get_err int(6) default 0 not null, 15 | get_total int(6) default 0 not null, 16 | sa mediumtext not null, 17 | lns tinyint(1) default 0 not null, 18 | timezone varchar(32) null, 19 | timezone_long mediumtext null, 20 | offset int default 0 not null, 21 | lat float null, 22 | lon float null, 23 | lastupdated datetime null 24 | ); 25 | 26 | CREATE IF NOT EXISTS TABLE mapspace 27 | ( 28 | source varchar(1) not null, 29 | sourcekey varchar(250) not null, 30 | lon float null, 31 | lat float null, 32 | name varchar(250) null, 33 | lastcurlerror int null, 34 | curlerrorcount int null, 35 | lastdataupdated int not null, 36 | primary key (source, sourcekey) 37 | ) 38 | 39 | CREATE IF NOT EXISTS TABLE wikispace 40 | ( 41 | wikiurl varchar(500) not null 42 | primary key, 43 | name varchar(150) null, 44 | lastcurlerror int null, 45 | lastdataupdated datetime not null, 46 | status varchar(10) null 47 | ) 48 | 49 | -------------------------------------------------------------------------------- /private/init_example.php: -------------------------------------------------------------------------------- 1 | $databaseHost, 31 | 'username' => $databaseUser, 32 | 'password' => $databasePassword, 33 | 'db'=> $databaseName, 34 | 'port' => $databasePort, 35 | )); 36 | 37 | // Register for free api key on timezonedb.com 38 | $timezoneApiKey = ''; 39 | 40 | ?> -------------------------------------------------------------------------------- /private/layout/header.php: -------------------------------------------------------------------------------- 1 | 5 | 6 | 7 | 8 | 9 | 10 | '; 11 | echo $title. 12 | ' 13 | 14 | '; -------------------------------------------------------------------------------- /private/layout/navigate.php: -------------------------------------------------------------------------------- 1 | 6 | -------------------------------------------------------------------------------- /private/setup.php: -------------------------------------------------------------------------------- 1 | 'sqlite', 10 | 'database_file' => $databasefile 11 | ]); 12 | } else { 13 | echo 'Set $databasefile in settings.php'; 14 | exit; 15 | }; 16 | 17 | //for mapall.space map 18 | $database->drop("space"); 19 | $database->create("space", [ 20 | "source" => [ 21 | "VARCHAR(30)", 22 | "NOT NULL" 23 | ], 24 | "sourcekey" => [ 25 | "VARCHAR(30)", 26 | "NOT NULL" 27 | ], 28 | "lon" => [ 29 | //"REAL", 30 | "DECIMAL(3,6)", 31 | ], 32 | "lat" => [ 33 | "DECIMAL(3,6)", 34 | ], 35 | "name" => [ 36 | "VARCHAR(30)" 37 | ], 38 | "lastcurlerror" => [ 39 | "INTEGER" 40 | ], 41 | "curlerrorcount" => [ 42 | "INTEGER" 43 | ], 44 | "lastdataupdated" => [ 45 | "INTEGER", 46 | "NOT NULL" 47 | ], 48 | "PRIMARY KEY (source,sourcekey)" 49 | ]); 50 | 51 | 52 | //check for hackerspace wiki 53 | $database->drop("wikispace"); 54 | $database->create("wikispace", [ 55 | "wikiurl" => [ 56 | "TEXT" 57 | ], 58 | "name" => [ 59 | "TEXT" 60 | ], 61 | "lastcurlerror" => [ 62 | "INTEGER" 63 | ], 64 | // "curlerrorcount" => [ 65 | // "INTEGER" 66 | // ], 67 | "lastdataupdated" => [ 68 | "DATETIME", 69 | "NOT NULL" 70 | ], 71 | // "emailsenddate" => [ 72 | // "TEXT" 73 | // ], 74 | "status" => [ 75 | "TEXT" 76 | ], 77 | 78 | "PRIMARY KEY (wikiurl,lastdataupdated)" 79 | ]); 80 | 81 | 82 | $errorlog = $database->error(); 83 | if ($errorlog[1] != 0) { 84 | echo 'SqLite Error '.$errorlog[1]; 85 | } else { 86 | echo 'File created :'.$databasefile; 87 | } 88 | -------------------------------------------------------------------------------- /private/shared/mapall_functions.php: -------------------------------------------------------------------------------- 1 | $json,'error'=>0, 'cors' => $result['cors'] ); 10 | } else { 11 | return array('json'=>null,'error'=>1000, 'cors' => $result['cors'] ); 12 | }; 13 | } else { 14 | return array('json'=>null,'error'=>$result['error'], 'cors'=>$result['cors']); 15 | }; 16 | 17 | } 18 | 19 | function getCurl($url,$postFields=null,$timeout=240) { 20 | global $PRIVATE; 21 | global $httpHeaders; 22 | 23 | $httpHeaderLastModified=null; 24 | $httpHeaderCORSEnabled=false; 25 | 26 | $httpHeaders =[]; 27 | 28 | $curlSession = curl_init(); 29 | 30 | //curl_setopt($curlSession, CURLOPT_BINARYTRANSFER, true); 31 | curl_setopt($curlSession, CURLOPT_RETURNTRANSFER, true); 32 | curl_setopt($curlSession, CURLOPT_USERAGENT, "http://mapall.space"); 33 | 34 | //for redirect 35 | curl_setopt($curlSession, CURLOPT_FOLLOWLOCATION, true); 36 | 37 | //timeout in secs 38 | curl_setopt($curlSession, CURLOPT_TIMEOUT,$timeout); 39 | 40 | //get file 41 | curl_setopt($curlSession, CURLOPT_URL, $url); 42 | 43 | //set post options if needed 44 | if (is_array($postFields)) { 45 | curl_setopt( $curlSession, CURLOPT_POST, true ); 46 | curl_setopt( $curlSession, CURLOPT_POSTFIELDS, http_build_query( $postFields ) ); 47 | }; 48 | 49 | curl_setopt($curlSession, CURLOPT_COOKIEJAR, $PRIVATE."cookie.txt"); 50 | curl_setopt($curlSession, CURLOPT_COOKIEFILE, $PRIVATE."cookie.txt"); 51 | 52 | curl_setopt($curlSession, CURLOPT_HEADERFUNCTION, "CurlHeader"); 53 | 54 | $result = curl_exec($curlSession); 55 | $curl_error = curl_errno($curlSession); 56 | $curl_info = curl_getinfo($curlSession,CURLINFO_HTTP_CODE); 57 | $curl_ssl = curl_getinfo($curlSession, CURLINFO_SSL_VERIFYRESULT); 58 | 59 | curl_close($curlSession); 60 | 61 | if ($curl_ssl != 0) { 62 | echo 'SSL verify error '.$curl_ssl.PHP_EOL; 63 | }; 64 | 65 | foreach ($httpHeaders as $line) { 66 | if (substr($line,0,13)=='Last-Modified') { 67 | $httpHeaderLastModified = date("Y-m-d H:i",strtotime(trim(substr($line,14),"\x0A..\x0D"))); 68 | }; 69 | if (strtolower(substr($line,0,30)) == 'access-control-allow-origin: *') { 70 | $httpHeaderCORSEnabled = true; 71 | }; 72 | } 73 | 74 | if ( $curl_error == 0 && $curl_info == 200 && $curl_ssl==0) { 75 | return array('result'=>$result,'error'=>0,'lastmodified'=>$httpHeaderLastModified,'cors'=> $httpHeaderCORSEnabled); 76 | } else { 77 | //$curl_ssl 78 | if ($curl_error!=0) { 79 | $error = $curl_error; 80 | } elseif($curl_info!=0) { 81 | $error = $curl_info; 82 | } else { 83 | $error = $curl_ssl+2000; 84 | } 85 | 86 | // echo '** CURL Error :' . $curl_error . PHP_EOL; 87 | // echo '** HTTP Error :' . $curl_info . PHP_EOL; 88 | // echo '** SSL Error :' . $curl_ssl . PHP_EOL; 89 | 90 | return array('result'=>null,'error'=>$error,'lastmodified'=>null, 'cors' => $httpHeaderCORSEnabled); 91 | }; 92 | }; 93 | 94 | function CurlHeader( $curl, $header_line ) { 95 | global $httpHeaders; 96 | $httpHeaders[] = $header_line; 97 | //echo "HeaderLine :" . $header_line . PHP_EOL; 98 | return strlen($header_line); 99 | }; 100 | 101 | ?> -------------------------------------------------------------------------------- /private/wiki.php: -------------------------------------------------------------------------------- 1 | delete('wikispace'); 26 | if ($db->getLastErrno() !=0) { 27 | echo "SQL error : ".$db->getLastError(); 28 | } 29 | echo('Init : database empty'); 30 | }; 31 | 32 | //Have or live or test option. 33 | if (!(isset($cliOptions['test']) or isset($cliOptions['live']))) { 34 | message('Use or test of live funtion.'.php_sapi_name()); 35 | exit; 36 | }; 37 | 38 | //testing 39 | if (isset($cliOptions['test'])) { 40 | $testrun = true; 41 | } else { 42 | $testrun = false; 43 | }; 44 | 45 | if (isset($cliOptions['count'])) { 46 | $maxcount = $cliOptions['count']; 47 | } else { 48 | $maxcount=0; 49 | }; 50 | 51 | message('Maxcount = '.$maxcount); 52 | 53 | // ** Login wiki **// 54 | $login_Token = getLoginToken(); 55 | //message('Login token ='.$login_Token); 56 | loginRequest( $login_Token ); 57 | $csrf_Token = getCSRFToken(); 58 | 59 | //Have or live or test option. 60 | if ( isset($cliOptions['close']) ) { 61 | //test with wikiMessage text 62 | $wikiMessage = '[This might be the bot text]'; 63 | $space = $cliOptions['close']; 64 | updateOneHackerSpace($space,'update'); 65 | } else { 66 | //For each hackerspace do 67 | getHackerspacesOrgJson(); 68 | }; 69 | 70 | //all done, logout 71 | echo 'Logout'.PHP_EOL; 72 | logoutRequest($csrf_Token); 73 | 74 | 75 | } else { 76 | //call from web 77 | }; 78 | 79 | 80 | function updateOneHackerSpace($space,$action) { 81 | global $wikiApi,$login_Token,$csrf_Token,$wikiMessage; 82 | 83 | $date = date("h:i:sa"); 84 | $wikiMessage .= "Updated manual on $date via http://mapall.space/hswikilist.php\n "; 85 | 86 | if (empty($login_Token)) { 87 | $login_Token = getLoginToken(); 88 | loginRequest( $login_Token ); 89 | $csrf_Token = getCSRFToken(); 90 | } 91 | 92 | $wikitext = getWikiPage($space); 93 | 94 | //email 95 | preg_match('/^\/|email=(.*)/', $wikitext, $newmail); 96 | 97 | //residencies contact 98 | preg_match('/^\/|residencies_contact=(.*)/', $wikitext, $resmail); 99 | 100 | if (isset($newmail[1]) and !isset($resmail[1])) { 101 | $email = $newmail[1]; 102 | } elseif (isset($resmail[1])) { 103 | $email = $resmail[1]; 104 | }; 105 | 106 | switch ($action) { 107 | case 'close': 108 | if(isset($email)) { 109 | sendEmail($email,$space,'https://wiki.hackerspaces.org/'.$space); 110 | //echo "Send email to :".$email.PHP_EOL; 111 | } 112 | updateHackerspaceWiki($space,'inactive'); 113 | break; 114 | case 'update': 115 | //sendEmail($email,$space,'https://wiki.hackerspaces.org/'.$space); 116 | updateHackerspaceWiki($space,'update'); 117 | break; 118 | default: 119 | message('ERROR : Action not defined!!',5); 120 | break; 121 | } 122 | } 123 | 124 | 125 | function getHackerspacesOrgJson() { 126 | global $removeOlderThen, $testrun, $maxcount, $wikiMessage; 127 | 128 | $req_results = 50; 129 | $req_page = 0; 130 | $now = date_create(date('Y-m-d\TH:i:s')); 131 | 132 | $statistics = array('total'=>0,'down'=>0,'manual'=>0,'active'=>0,'inactive'=>0,'skipped'=>0); 133 | 134 | message('#### Check hackerspaces on '.date('Y-m-d H:i').' ####'); 135 | 136 | $result = getPageHackerspacesOrg($req_results,$req_page); 137 | 138 | while (isset($result) && count($result)==50) { 139 | 140 | foreach ($result as $space) { 141 | $wikiMessage = ''; 142 | 143 | $fullname = $space['fulltext']; 144 | $lastupdate = date_create(date("Y-m-d\TH:i:s", $space['printouts']['Modification date'][0]['timestamp'])); 145 | $interval = date_diff($now, $lastupdate)->format('%a'); //in days 146 | 147 | $source = $space['fullurl']; 148 | 149 | echo '.......... '.PHP_EOL; 150 | message('** Space '.$fullname.' Last modified on wiki '.$lastupdate->format('Y-m-d').' - days '.$interval.' ago ** '.$source,4); 151 | 152 | // $url = (isset($space['printouts']['Website'][0])) ?? $space['printouts']['Website'][0] : null; 153 | $url = $space['printouts']['Website'][0] ?? null; 154 | 155 | message('Url = '.$url); 156 | 157 | //only check if no modification in last year 158 | if ($interval > 365 ) { 159 | $statistics['total']+=1; 160 | 161 | $email = $space['printouts']['Email'][0] ?? ''; 162 | 163 | if (isset($space['printouts']['Residencies Contact'][0])) { 164 | $email .= $space['printouts']['Residencies Contact'][0] ??''; 165 | echo 'Found Residencies mail '. $space['printouts']['Residencies Contact'][0]; 166 | }; 167 | $email = str_replace('mailto:', '' ,$email); 168 | 169 | 170 | $siteUp = getCurl($url,null,60); //wait long time for responce 171 | 172 | if ($siteUp['error']==0 or empty($url)) { 173 | //clear all dates 174 | $checkDate = array(); 175 | 176 | if (empty($url)) { 177 | message('No site, check on dates.'); 178 | } else { 179 | 180 | message('Site up check on RSS dates.'); 181 | 182 | $namefound = substr_count(strtoupper($siteUp['result']),str_replace('_',' ',strtoupper($fullname))); 183 | message('Found name on homepage '.$namefound.' times.',0); 184 | 185 | if ($namefound>0 && !empty($siteUp['result'])) { 186 | 187 | $siteFeed = getDateSiteAlternativeLink($siteUp['result'],$url); 188 | 189 | //some rss feeds return current datetime, exclude these 190 | if (!empty($siteFeed)) { 191 | $siteFeedDate = date_create($siteFeed); 192 | 193 | $intervalSiteDays = date_diff($now, $siteFeedDate)->format('%a'); 194 | 195 | if ($intervalSiteDays>1) { 196 | $checkDate['altLink'] = $siteFeed; 197 | message('Alternative Link (rss) :'.$checkDate['altLink'],0); 198 | }; 199 | }; 200 | }; 201 | }; 202 | 203 | //get from http header - disabled, to many false positives 204 | // if ($siteUp['lastmodified']!='') { 205 | // $checkDate['httpLastModified'] = $siteUp['lastmodified']; 206 | // message('Site Last Modified (http headers) '.$siteUp['lastmodified']); 207 | // } 208 | 209 | //check wiki 210 | if (isset($space['printouts']['Wiki'][0])) { 211 | $hackerspaceWiki = $space['printouts']['Wiki'][0]; 212 | $checkDate['wiki'] = getDateLastWikiEdit($hackerspaceWiki); 213 | message('Wiki '.$checkDate['wiki'].' - '.$space['printouts']['Wiki'][0]); 214 | } 215 | 216 | //check twitter, not usable since Musk took over 217 | // if (isset($space['printouts']['Twitter'][0])) { 218 | // $checkDate['twitter'] = getDateLastTweet($space['printouts']['Twitter'][0] ); 219 | // message('Twitter '.$checkDate['twitter'].' - '.$space['printouts']['Twitter'][0]); 220 | // } 221 | 222 | //check twitter via nitter 223 | if (isset($space['printouts']['Twitter'][0])) { 224 | $checkDate['twitter'] = getDateLastTweetNitter($space['printouts']['Twitter'][0] ); 225 | message('Twitter/nitter '.$checkDate['twitter'].' - '.$space['printouts']['Twitter'][0]); 226 | } 227 | 228 | //check Fediverse/Mastadon 229 | if (isset($space['printouts']['Fediverse'][0])) { 230 | $checkDate['fediverse'] = getDateLastFediverse($space['printouts']['Fediverse'][0] ); 231 | message('Fediverse '.$checkDate['fediverse'].' - '.$space['printouts']['Fediverse'][0]); 232 | } 233 | 234 | //check spaceAPI 235 | if (isset($space['printouts']['SpaceAPI'][0])) { 236 | $checkDate['spaceapi'] = getDataLastSpacaAPI($space['printouts']['SpaceAPI'][0]); 237 | message('SpaceAPI '.$checkDate['spaceapi'] ); 238 | } 239 | 240 | //mailinglist 241 | if (isset($space['printouts']['Mailinglist'][0])) { 242 | $checkDate['mailman'] = getDateLastMailManPost($space['printouts']['Mailinglist'][0]); 243 | message('Mailinglist '.$checkDate['mailman'].' - '.$space['printouts']['Mailinglist'][0]); 244 | } 245 | 246 | //newsfeed, asume rss/xml athom file 247 | if (isset($space['printouts']['Newsfeed'][0])) { 248 | $checkDate['newsfeed'] = getDateNewsFeed($space['printouts']['Newsfeed'][0]); 249 | message('Newsfeed '.$checkDate['newsfeed'].' - '.$space['printouts']['Newsfeed'][0]); 250 | } 251 | 252 | //calender feed 253 | if (isset($space['printouts']['CalendarFeed'][0])) { 254 | $checkDate['calender'] = getDataLastCalenderFeed($space['printouts']['CalendarFeed'][0]); 255 | message('Calenderfeed '.$checkDate['calender'].' - '.$space['printouts']['CalendarFeed'][0]); 256 | } 257 | 258 | //placeholder for facebook check 259 | 260 | //check if lon/lat is within normal range 261 | if (isset($space['printouts']['Location'][0])) { 262 | $location = $space['printouts']['Location'][0]; 263 | //message('lat' . $location['lat '] . '/ lon ' . $location['lon']); 264 | if ($location['lon'] < -180 or $location['lon'] > 180 or $location['lat'] < -90 or $location['lat'] > 90 265 | ) { 266 | message('Wrong lat\lon is : [ lat ' . $location['lat'] . '/ lon ' . $location['lon']); 267 | //sendErrorLonLatEmail($email, $fullname, $url, $location); 268 | } 269 | }; 270 | 271 | //do all the check 272 | $lastUpdateDate = 0; 273 | foreach ($checkDate as $datesource => $date) { 274 | if ($date > $lastUpdateDate) { 275 | $lastUpdateDate = $date; 276 | }; 277 | }; 278 | message('Last Activity was on '. $lastUpdateDate,2); 279 | 280 | if ($lastUpdateDate==0) { 281 | $statistics['manual']+=1; 282 | 283 | message('No activity for space, manual check.',5); 284 | 285 | updateDatabase($source,$fullname,0,'manual'); 286 | 287 | } elseif ($removeOlderThen > $lastUpdateDate) { 288 | $statistics['inactive']+=1; 289 | 290 | message('No activity for space, set to inactive.',5); 291 | message('Send email to '.$email,4); 292 | 293 | if (!$testrun) { 294 | sendEmail($email,$fullname,$source); 295 | updateHackerspaceWiki($fullname,'inactive');//Step5 296 | } 297 | updateDatabase($source,$fullname,0,'inactive'); 298 | 299 | } else { 300 | $statistics['active']+=1; 301 | 302 | message('Space still active, update wiki page ',4); 303 | if (!$testrun) { 304 | updateHackerspaceWiki($fullname,'update');//Step5 305 | } 306 | updateDatabase($source,$fullname,0,'active'); 307 | } 308 | 309 | } else { 310 | 311 | 312 | $result = updateDatabase($source,$fullname,$siteUp['error'],'down'); 313 | 314 | if (count($result)>0) { 315 | $string = ''; 316 | foreach ($result as $value) { 317 | $string .= $value[''].' ( Error '.$value['lastcurlerror'].'), '; 318 | } 319 | message('Site down, checked on dates : ' . $string); 320 | if (count($result)>3) { 321 | $statistics['down'] += 1; 322 | message('Set wiki to inactive, send email to ',$email); 323 | if (!$testrun) { 324 | sendEmail($email,$fullname,$source); 325 | updateHackerspaceWiki($fullname,'inactive'); 326 | } 327 | } else { 328 | $statistics['skipped'] += 1; 329 | } 330 | }; 331 | 332 | }; 333 | 334 | } else { 335 | $statistics['skipped']+=1; 336 | }; 337 | 338 | if ($maxcount!=0 and $statistics['total']>=$maxcount) { 339 | message('Processed :'.$statistics['total'].' Down :'.$statistics['down'].' Manual :'.$statistics['manual'].' Active :'.$statistics['active'].' Inactive :'.$statistics['inactive'].' Skipped :'.$statistics['skipped']); 340 | return; 341 | }; 342 | 343 | 344 | }; 345 | 346 | 347 | $req_page++; 348 | $result = getPageHackerspacesOrg($req_results,$req_page); 349 | }; 350 | //all done.. 351 | message('Processed :'.$statistics['total'].' Down :'.$statistics['down'].' Manual :'.$statistics['manual'].' Active :'.$statistics['active'].' Inactive :'.$statistics['inactive'].' Skipped :'.$statistics['skipped']); 352 | }; 353 | 354 | 355 | function getPageHackerspacesOrg($req_results,$req_page) { 356 | global $testrun; 357 | 358 | $offset = $req_page*$req_results; 359 | 360 | if ($testrun) { 361 | $sorting = 'desc'; //for testing, newest first 362 | } else { 363 | $sorting = 'asc'; 364 | }; 365 | 366 | $wikiDate = date("d-20M-20Y",mktime(0, 0, 0, date("m"), date("d")-11, date("Y"))); 367 | $url = "https://wiki.hackerspaces.org/w/index.php?title=Special:Ask&x=-5B-5BCategory%3AHackerspace-5D-5D-20-5B-5BHackerspace-20status%3A%3Aactive-5D-5D-20-5B-5BHas-20coordinates%3A%3A%2B-5D-5D-20-5B-5BModification-20date%3A%3A%E2%89%A4$wikiDate-5D-5D%2F-3F-23%2F-3FModification-20date%2F-3FEmail%2F-3FWebsite%2F-3FWiki%2F-3FCity%2F-3FPhone%2F-3FNumber-20of-20members%2F-3FSpaceAPI%2F-3FLocation%2F-3FCalendarFeed%2F-3FNewsfeed%2F-3FTwitter%2F-3FFediverse%2F-3FFacebook%2F-3FEmail%2F-3FMailinglist&format=json&limit=$req_results&offset=$offset&link=all&headers=show&searchlabel=JSON&class=sortable+wikitable+smwtable&sort=Modification+date&order=$sorting&mainlabel=&prettyprint=true&unescape=true"; 368 | 369 | $getWikiJsonResult = getJSON($url); 370 | 371 | if ($getWikiJsonResult['error']!=0){ 372 | // var_dump($getWikiJsonResult['json']); 373 | message(' Error while get wiki json '.$getWikiJsonResult['error']); 374 | return null; 375 | } 376 | 377 | return $getWikiJsonResult['json']['results']; 378 | }; 379 | 380 | 381 | function updateDatabase($wikiurl,$name ='',$lastcurlerror=0,$status='') { 382 | $db = MysqliDb::getInstance(); 383 | 384 | $data = array( 385 | "wikiurl" =>$wikiurl, 386 | "name" =>$name, 387 | "lastdataupdated" => $db->now(), 388 | "lastcurlerror" => $lastcurlerror, 389 | "status" => $status, 390 | ); 391 | 392 | if ($db->where("wikiurl",$wikiurl)->getOne("wikispace")) { 393 | $result = $db->update("wikispace", $data); 394 | } else { 395 | $result = $db->insert("wikispace", $data); 396 | } 397 | 398 | 399 | 400 | 401 | if ($db->getLastErrno() !== 0) { 402 | echo 'Update failed. Error: '. $db->getLastError(); 403 | } 404 | 405 | return $db->get("wikispace", "wikiurl = $wikiurl"); 406 | }; 407 | 408 | // Step 1: GET request to fetch login token 409 | function getLoginToken() { 410 | global $wikiApi ; 411 | 412 | $params = [ 413 | "action" => "query", 414 | "meta" => "tokens", 415 | "type" => "login", 416 | "format" => "json" 417 | ]; 418 | 419 | $url = $wikiApi . "?" . http_build_query( $params ); 420 | 421 | $result = getJSON($url); 422 | 423 | return $result["json"]["query"]["tokens"]["logintoken"]; 424 | } 425 | 426 | // Step 2: POST request to log in. Use of main account for login is not 427 | // supported. Obtain credentials via Special:BotPasswords 428 | // (https://www.mediawiki.org/wiki/Special:BotPasswords) for lgname & lgpassword 429 | function loginRequest( $logintoken ) { 430 | global $wikiApi ; 431 | global $botUser; 432 | global $botPasswd; 433 | 434 | $params = [ 435 | "action" => "login", 436 | "lgname" => $botUser, 437 | "lgpassword" => $botPasswd, 438 | "lgtoken" => $logintoken, 439 | "format" => "json" 440 | ]; 441 | 442 | $url = $wikiApi;// . "?" . http_build_query( $params ); 443 | 444 | $result = getJSON($url,$params); 445 | } 446 | 447 | // Step 3: GET request to fetch CSRF token 448 | function getCSRFToken() { 449 | global $wikiApi ; 450 | 451 | $params = [ 452 | "action" => "query", 453 | "meta" => "tokens", 454 | "format" => "json" 455 | ]; 456 | 457 | $url = $wikiApi . "?" . http_build_query( $params ); 458 | 459 | $result = getJSON($url); 460 | 461 | return $result["json"]["query"]["tokens"]["csrftoken"]; 462 | } 463 | 464 | function getWikiPage($spaceURLname) { 465 | global $wikiApi ; 466 | 467 | $params = [ 468 | "action" => "parse", 469 | "page" => $spaceURLname, 470 | "prop" => "wikitext", 471 | "format" => "json" 472 | ]; 473 | 474 | $url = $wikiApi . "?" . http_build_query( $params ); 475 | 476 | $result = getJSON($url); 477 | return($result["json"]["parse"]["wikitext"]["*"]); 478 | } 479 | 480 | 481 | // Step 4: POST request to edit a page 482 | function updateHackerspaceWiki( $spaceURLname , $action ) { 483 | global $wikiApi, $csrf_Token, $wikiMessage; 484 | 485 | //https://wiki.hackerspaces.org/Special:ApiSandbox#action=edit&title=TkkrLab&appendtext=%22Hello%20World%22&format=json 486 | 487 | //get current page 488 | $wikitext = getWikiPage($spaceURLname); 489 | 490 | //check on current status, should be active. If not give ERROR 491 | $currentState = substr($wikitext, strpos($wikitext, '|status=')+8,6); 492 | if ($currentState != 'active') { 493 | message('ERROR: Wiki for '.$spaceURLname.' is '.$currentState.'. No changes made ',5); 494 | return; 495 | } 496 | 497 | if ($action == 'inactive') { 498 | $newpage = str_replace('|status=active','|status=suspected inactive',$wikitext); 499 | } elseif ($action == 'update') { 500 | $newpage = $wikitext; 501 | } else { 502 | message('No hs wiki action defined!!',5); 503 | } 504 | 505 | $newpage .= "\n"; 506 | 507 | $params = [ 508 | "action" => "edit", 509 | "title" => $spaceURLname, 510 | "text" => $newpage, 511 | "token" => $csrf_Token, 512 | "summary" => "Update to $action by wikibot", 513 | "bot" => true, 514 | "format" => "json" 515 | ]; 516 | 517 | $url = $wikiApi ;// . "?" . http_build_query( $params ); 518 | $result = getJSON($url,$params); 519 | 520 | // if (isset($result['json']['error']) or $result['json']['edit']['result']=='Failure') { 521 | // var_dump( $result['json']['error']); 522 | // } 523 | 524 | //solve captcha 525 | if ($result['json']['edit']['result']=='Failure' and isset($result['json']['edit']['captcha']) ) { 526 | 527 | echo 'Solve wiki captcha'; 528 | 529 | $captchparams = [ 530 | "captchaid" => $result["json"]['edit']['captcha']['id'], 531 | "captchaword" => getCaptchaAnswer($result["json"]['edit']['captcha']['question']), 532 | ]; 533 | $params = array_merge($params,$captchparams); 534 | 535 | $url = $wikiApi ;// . "?" . http_build_query( $params ); 536 | 537 | $result = getJSON($url,$params); 538 | 539 | // if (isset($result['json']['error']) or $result['json']['edit']['result']=='Failure') { 540 | // var_dump( $result); 541 | // }; 542 | } 543 | 544 | //clear chache / purge 545 | //https://www.mediawiki.org/wiki/API:Purge 546 | $params = [ 547 | "action" => "purge", 548 | "titles" => $spaceURLname, 549 | "format" => "json" 550 | ]; 551 | 552 | $url = $wikiApi ;// . "?" . http_build_query( $params ); 553 | $result = getJSON($url,$params); 554 | 555 | } 556 | 557 | 558 | // Step 4: POST request to logout 559 | function logoutRequest( $csrftoken ) { 560 | global $wikiApi; 561 | $params = [ 562 | "action" => "logout", 563 | "token" => $csrftoken, 564 | "format" => "json", 565 | ]; 566 | 567 | 568 | $url = $wikiApi;// . "?" . http_build_query( $params ); 569 | 570 | $result = getJSON($url,$params); 571 | } 572 | 573 | function getCaptchaAnswer($question) { 574 | switch ($question) { 575 | case "What does the quote on the top of the List of Events page say?": 576 | return 'To become great, you must stand on the shoulders of giants.'; 577 | break; 578 | case "Where is hackerspaces.org currently hosted at? Hint: Read the Disclaimers (bottom of page)": 579 | return "Nessus"; 580 | break; 581 | case "What is the name of our IRC channel on libera? Hint: Read the Communication page": 582 | return "#hackerspaces"; 583 | break; 584 | case "This website is for whom? Hint: Read the frontpage": 585 | return "Anyone and Everyone"; 586 | break; 587 | case "Hacker______?": 588 | return "spaces"; 589 | break; 590 | default: 591 | message("CaptchaAnswer not found for quesion :".$question,5); 592 | } 593 | } 594 | 595 | function message($message,$lineloglevel=0) { 596 | global $loglevel; 597 | global $loglevelfile; 598 | global $log_path; 599 | global $wikiMessage; 600 | 601 | if ($lineloglevel >= $loglevel) { 602 | echo $message.PHP_EOL; 603 | }; 604 | 605 | $wikiMessage .= $message.PHP_EOL; 606 | 607 | if ($lineloglevel >= $loglevelfile) { 608 | // 609 | if(!file_exists ( $log_path.'wikilog.txt' )) { 610 | $message = "Map all spaces error log, see also FAQ\nError 0-99 Curl\nError 100-999 http\nError 1000 no valid json\nError 1001 dupe\nError 2000 > ssl error\n\n".$message; 611 | } 612 | 613 | $fp = fopen($log_path.'wikilog.txt', 'a'); 614 | fwrite($fp,$message.PHP_EOL); 615 | fclose($fp); 616 | }; 617 | } 618 | 619 | function getDataLastSpacaAPI($spaceapiurl) { 620 | $json = getJSON($spaceapiurl); 621 | if (isset($json['json']['state']['lastchange']) and $json['json']['state']['lastchange']!=0) { 622 | return date("Y-m-d H:i:s",$json['json']['state']['lastchange']); 623 | } else { 624 | return null; 625 | } 626 | } 627 | 628 | //no longer used since Must took over 629 | // function getDateLastTweet($user) { 630 | // global $twitter; 631 | 632 | // $tuser = @end(explode('/', $user)); 633 | 634 | // $url = 'https://api.twitter.com/1.1/statuses/user_timeline.json'; 635 | // $getfield = "?screen_name=$tuser&count=1"; 636 | // $requestMethod = 'GET'; 637 | 638 | // $result = json_decode($twitter->setGetfield($getfield) 639 | // ->buildOauth($url, $requestMethod) 640 | // ->performRequest(),JSON_OBJECT_AS_ARRAY); 641 | 642 | // if(isset($result[0]['created_at']) or $tuser='') { 643 | // return date("Y-m-d H:i",strtotime($result[0]['created_at'])); 644 | // } else { 645 | // return null; //timeline empty 646 | // } 647 | 648 | // }; 649 | 650 | function getDateLastTweetNitter($user) { 651 | global $nitterhosts,$nitterworks; 652 | 653 | if ($nitterworks =='') { 654 | foreach($nitterhosts as $host) { 655 | $result = getCurl($host.'tkkrlab/rss'); 656 | if ($result['error']==0) { 657 | $nitterworks = $host; 658 | echo 'Nitter up : '.$host; 659 | break; 660 | } 661 | } 662 | } 663 | 664 | $tuser = substr(parse_url($user, PHP_URL_PATH),1); 665 | $lastdate = 0; 666 | $result = getCurl("$nitterworks$tuser/rss"); 667 | if ($result['error']==0) { 668 | $xml = simplexml_load_string($result['result'],'SimpleXMLElement',LIBXML_NOCDATA|LIBXML_NOERROR); 669 | $json = json_encode($xml); 670 | $array = json_decode($json,TRUE); 671 | foreach($array['channel']['item'] as $rssItem) { 672 | if (isset($rssItem['pubDate'])) { 673 | $rssDate = strtotime($rssItem['pubDate']); 674 | if (empty($lastdate) or $rssDate > $lastdate) { 675 | $lastdate = $rssDate; 676 | } 677 | } else { 678 | echo "Error in feed $nitterworks$tuser\n"; 679 | } 680 | } 681 | return date("Y-m-d H:i:s",$lastdate); 682 | } else { 683 | return null; 684 | } 685 | }; 686 | 687 | function getDateLastFediverse($fediverse) { 688 | 689 | $result = getCurl("$fediverse.rss"); 690 | if ($result['error']==0) { 691 | $xml = simplexml_load_string($result['result']); 692 | $json = json_encode($xml); 693 | $array = json_decode($json,TRUE); 694 | foreach($array['channel']['item'] as $rssItem) { 695 | $rssDate = strtotime($rssItem['pubDate']); 696 | if (empty($lastdate) or $rssDate > $lastdate) { 697 | $lastdate = $rssDate; 698 | } 699 | } 700 | echo "Datum voor $fediverse ".date("Y-m-d H:i:s",$lastdate)."\n"; 701 | return date("Y-m-d H:i:s",$lastdate); 702 | } else { 703 | return null; 704 | } 705 | }; 706 | 707 | 708 | 709 | 710 | 711 | function getDateLastWikiEdit($wiki) { 712 | message('Wiki on '.$wiki); 713 | $result = getCurl($wiki); 714 | if ($result['error'] == 0 ) { 715 | $wikiDomain = parse_url($wiki, PHP_URL_SCHEME).'://'.parse_url($wiki, PHP_URL_HOST); 716 | return getDateSiteAlternativeLink($result['result'],$wikiDomain); 717 | } else { 718 | message('Wiki not found, error '.$result['error']); 719 | return null; 720 | } 721 | } 722 | 723 | function getDateNewsFeed($feed) { 724 | $result = getCurl($feed); 725 | 726 | if (!empty($result['result']) && $result['error']==0) { 727 | 728 | if ( (substr($result['result'],0,4)=='channel->lastBuildDate)) { 732 | return date("Y-m-d H:i",strtotime($xml->channel->lastBuildDate[0])); 733 | } elseif (!empty($xml->channel->pubDate)) { 734 | return date("Y-m-d H:i",strtotime($xml->channel->pubDate[0])); 735 | } elseif (!empty($xml->entry->published)) { 736 | return date("Y-m-d H:i",strtotime($xml->entry->published[0])); 737 | } else { 738 | return null; 739 | } 740 | } 741 | } else { 742 | message('Newsfeed no RSS feed '.$feed.' Error :'.$result['error']); 743 | return null; 744 | } 745 | 746 | } 747 | 748 | function getDateLastMailManPost($mailinglist){ 749 | //if mailman 750 | if (strpos($mailinglist,'/mailman/listinfo/')>0) { 751 | $pos =strpos($mailinglist,'/mailman/listinfo/'); 752 | $mailman = substr($mailinglist,0,$pos).'/pipermail/'.substr($mailinglist,$pos+18); 753 | $result = getCurl($mailman); 754 | if ($result['error']==0 && $result['result']) { 755 | $foundDate=0; 756 | preg_match_all('/(.*?)<\/td>/i', $result['result'], $matches, PREG_SET_ORDER, 0); 757 | if (isset($matches[3][0])){ 758 | $lastArchive = substr($matches[3][0],4,-6); 759 | $foundDate = strtotime($lastArchive); 760 | } 761 | if ($foundDate !=0 and $result['error']==0) { 762 | return date("Y-m-d H:i",$foundDate); 763 | } else { 764 | return null; 765 | } 766 | } 767 | 768 | } elseif(strpos($mailinglist,'groups.google.com')>0) { 769 | //convert to https 770 | if (substr($mailinglist,0,5)=='http:') { 771 | $mailinglist = 'https:'.substr($mailinglist,5); 772 | } 773 | //convert url to rss feed 774 | if (strpos($mailinglist,'/group/')>0) { 775 | $googlefeed= str_replace('/group/','/forum/feed/',$mailinglist).'/msgs/rss_v2_0.xml?num=1'; 776 | } else { 777 | $googlefeed= str_replace('/#!forum/','/feed/',$mailinglist).'/msgs/rss_v2_0.xml?num=1'; 778 | } 779 | $result = getCurl($googlefeed); 780 | if ($result['error']==0 && $result['result']) { 781 | if (substr($result['result'],0,4)=='channel->item->pubDate)); 784 | } else { 785 | return null; 786 | } 787 | } 788 | } else { 789 | return null; 790 | } 791 | } 792 | 793 | function getDataLastCalenderFeed($ical) { 794 | $result = getCurl($ical); 795 | if (!empty($result['result']) && $result['error'] == 0) { 796 | $file = str_getcsv($result['result'],"\n"); 797 | $foundEndDate = $foundStampDate = $lastStampDate= $lastEndDate = 0; 798 | foreach ($file as $line) { 799 | //if calender dont have CREATED entrys 800 | if (substr($line,0,6)=='DTEND:') { 801 | $foundEndDate = date("Y-m-d H:i",strtotime(substr($line,6))); 802 | } 803 | if ($foundEndDate > $lastEndDate) { 804 | $lastEndDate = $foundEndDate; 805 | } 806 | 807 | if (substr($line,0,8)=='DTSTAMP:') { 808 | $foundStampDate = date("Y-m-d H:i",strtotime(substr($line,8))); 809 | } 810 | if ($foundStampDate > $lastStampDate) { 811 | $lastStampDate = $foundStampDate; 812 | } 813 | } 814 | 815 | if (isset($lastStampDate)) { 816 | return $lastStampDate; 817 | } elseif(isset($lastEndDate)) { 818 | return $lastEndDate; 819 | } else { 820 | return null; 821 | } 822 | 823 | } 824 | } 825 | 826 | 827 | function getDateSiteAlternativeLink($site,$url) { 828 | $checkDate = array(); 829 | 830 | return null; 831 | 832 | if (empty($site)) { 833 | return null; 834 | } 835 | 836 | libxml_use_internal_errors(true); 837 | $DOMfile = new DomDocument(); 838 | $DOMfile->loadHTML($site); 839 | $xml = simplexml_import_dom($DOMfile); 840 | $generator = ''; 841 | 842 | //find generator 843 | foreach ($xml->head->meta as $value) { 844 | $array = (array)$value; 845 | if (isset($array['@attributes']['name']) && $array['@attributes']['name']=='generator') { 846 | $generator = $array['@attributes']['content']; 847 | } 848 | } 849 | //find alternative links 850 | foreach ($xml->head->link as $value) { 851 | $array = (array)$value; 852 | 853 | if (isset($array['@attributes']['type']) && ($array['@attributes']['rel']=='alternate' && ( 854 | $array['@attributes']['type'] == 'application/rss+xml' or 855 | $array['@attributes']['type'] == 'application/atom+xml') or 856 | $array['@attributes']['type'] == 'application/rsd+xml' 857 | )) { 858 | $link = $array['@attributes']['href']; 859 | if (substr($link,0,1)=='/') { 860 | $link = $url.$link; 861 | } 862 | 863 | if (substr($generator,0,9) == 'MediaWiki+++') { 864 | message('Proces MediaWiki'); 865 | 866 | $wikiFeed = parse_url($link, PHP_URL_SCHEME).'://'.parse_url($link, PHP_URL_HOST).'/'.parse_url($link, PHP_URL_PATH); 867 | $wikiFeed .= '?days=9999&limit=1&action=feedrecentchanges&feedformat=atom'; 868 | $result = getCurl($wikiFeed); 869 | if ($result['error']==0) { 870 | $feedRecentChanges = simplexml_load_string($result['result']); 871 | $foundDate = $feedRecentChanges->entry->updated; 872 | if (!empty($foundDate)) { 873 | return date("Y-m-d H:i",strtotime($foundDate)); 874 | }; 875 | } else { 876 | message('Error on '.$wikiFeed.' Error : '.$result['error']); 877 | } 878 | } elseif ($generator == 'DokuWiki') { 879 | message('Proces DokuWiki +++'); 880 | $result = getCurl($link); 881 | if ($result['error']==0) { 882 | try { 883 | $xmlfeed = simplexml_load_string($result['result']); 884 | if (!($xmlfeed === false)) { 885 | $dc = $xmlfeed->item->children('http://purl.org/dc/elements/1.1/'); 886 | $foundDate = $dc->date; 887 | } else { 888 | message('Error in XML DokuWiki ' . $link); 889 | } 890 | } catch (exception $e) { 891 | message('Error in XML DokuWiki ' . $link); 892 | } 893 | 894 | if (!empty($foundDate)) { 895 | return date("Y-m-d H:i",strtotime($dc->date)); 896 | } else { 897 | message('No date found for '.$link); 898 | } 899 | } 900 | } elseif (substr($generator,0,9) == 'WordPress') { 901 | message('Proces Wordpress'); 902 | if (parse_url($link, PHP_URL_PATH) != '/comments/feed/' ) { 903 | $foundDate = getDateNewsFeed($link); 904 | if (!empty($foundDate)) { 905 | $checkDate[$link]=date("Y-m-d H:i",strtotime($foundDate)); 906 | } 907 | } 908 | } else { 909 | //channel lastBuildDate 910 | $foundDate = getDateNewsFeed($link); 911 | if (!empty($foundDate)) { 912 | $checkDate[$link]=date("Y-m-d H:i",strtotime($foundDate)); 913 | } 914 | } 915 | } 916 | } 917 | 918 | //do all the check 919 | $lastUpdateDate = 0; 920 | foreach ($checkDate as $datesource => $date) { 921 | if ($date > $lastUpdateDate) { 922 | $lastUpdateDate = $date; 923 | message('RSS Feed : '.$generator.' date : '.$date.' source : '.$datesource); 924 | }; 925 | }; 926 | 927 | return $lastUpdateDate; 928 | 929 | }; 930 | 931 | 932 | function sendEmail($email,$fullname,$url) { 933 | if (!filter_var($email, FILTER_VALIDATE_EMAIL)) { 934 | message("ERROR Sendmail : Email $email not valid",5); 935 | return false; 936 | } 937 | 938 | $wikiMessage = "Send email to : ". $email; 939 | 940 | $headers = "From:Dave Borghuis \r\nMIME-Version: 1.0\r\nContent-type: text/html; charset=iso-8859-1"; 941 | $mailmessage = "Hello,
Wiki entry for $fullname has been changed. We asume that your hacerspace is no longer active. If this is not the case go to the wiki and change the status and add additional information if possible.
More information about this proces can be found on Mapall site
Do not reply to this mail, it wil not be read.\nLog of our checks : \n".$wikiMessage; 942 | $mailsend = mail( 943 | $email, 944 | 'Hackerspaces.org entry for '.$fullname , 945 | $mailmessage, 946 | $headers 947 | ); 948 | 949 | if (!$mailsend) { 950 | message('Error : Email not send!! '.$email,5); 951 | return false; 952 | } 953 | }; 954 | 955 | function sendErrorLonLatEmail($email, $fullname, $url, $location) 956 | { 957 | // if ($testrun) { 958 | // $email = 'dave@daveborghuis.nl'; 959 | // } 960 | if (!filter_var($email, FILTER_VALIDATE_EMAIL)) { 961 | message("ERROR Sendmail : Email $email not valid", 5); 962 | return false; 963 | } 964 | 965 | // $wikiMessage = "Send email to : " . $email; 966 | 967 | $headers = "From:Dave Borghuis \r\nMIME-Version: 1.0\r\nContent-type: text/html; charset=iso-8859-1"; 968 | $mailmessage = "Hello,
Wiki entry for $fullname has incorrect location data. Probaly the longatude and latatude may be swaped. Please check this data and change it on your wiki page accordingly."; 969 | $mailsend = mail( 970 | $email, 971 | 'Location data on Hackerspaces.org entry for ' . $fullname, 972 | $mailmessage, 973 | $headers 974 | ); 975 | 976 | if (!$mailsend) { 977 | message('Error : Email not send!! ' . $email, 5); 978 | return false; 979 | } 980 | }; 981 | 982 | 983 | ?> 984 | -------------------------------------------------------------------------------- /public_html/.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "name": "Listen for Xdebug", 9 | "type": "php", 10 | "request": "launch", 11 | "port": 9003 12 | }, 13 | { 14 | "name": "Launch currently open script", 15 | "type": "php", 16 | "request": "launch", 17 | "program": "${file}", 18 | "cwd": "${fileDirname}", 19 | "port": 0, 20 | "runtimeArgs": [ 21 | "-dxdebug.start_with_request=yes" 22 | ], 23 | "env": { 24 | "XDEBUG_MODE": "debug,develop", 25 | "XDEBUG_CONFIG": "client_port=${port}" 26 | } 27 | }, 28 | { 29 | "name": "Launch Built-in web server", 30 | "type": "php", 31 | "request": "launch", 32 | "runtimeArgs": [ 33 | "-dxdebug.mode=debug", 34 | "-dxdebug.start_with_request=yes", 35 | "-S", 36 | "localhost:0" 37 | ], 38 | "program": "", 39 | "cwd": "${workspaceRoot}", 40 | "port": 9003, 41 | "serverReadyAction": { 42 | "pattern": "Development Server \\(http://localhost:([0-9]+)\\) started", 43 | "uriFormat": "http://localhost:%s", 44 | "action": "openExternally" 45 | } 46 | } 47 | ] 48 | } -------------------------------------------------------------------------------- /public_html/about.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 19 | Map all spaces - About 20 | 21 | 22 | 23 | 26 |
27 |
28 |

About

29 | This site is created by Dave Borghuis just for fun. If you like it consider to buy me virtual coffie / Club-Mate aka small donation. Most easy is to use paypal (zeno4ever).
30 |

Can i help you?

31 | Cool, thats nice of you. Check the FAQ how you can contribute to this project. 32 |
33 |
34 | -------------------------------------------------------------------------------- /public_html/css/leaflet-search.css: -------------------------------------------------------------------------------- 1 | 2 | .leaflet-container .leaflet-control-search { 3 | position:relative; 4 | float:left; 5 | background:#fff; 6 | color:#1978cf; 7 | border: 2px solid rgba(0,0,0,0.2); 8 | background-clip: padding-box; 9 | -moz-border-radius: 4px; 10 | -webkit-border-radius: 4px; 11 | border-radius: 4px; 12 | background-color: rgba(255, 255, 255, 0.8); 13 | z-index:1000; 14 | margin-left: 10px; 15 | margin-top: 10px; 16 | } 17 | .leaflet-control-search.search-exp {/*expanded*/ 18 | background: #fff; 19 | border: 2px solid rgba(0,0,0,0.2); 20 | background-clip: padding-box; 21 | } 22 | .leaflet-control-search .search-input { 23 | display:block; 24 | float:left; 25 | background: #fff; 26 | border:1px solid #666; 27 | border-radius:2px; 28 | height:22px; 29 | padding:0 20px 0 2px; 30 | margin:4px 0 4px 4px; 31 | } 32 | .leaflet-control-search.search-load .search-input { 33 | background: url('/image/loader.gif') no-repeat center right #fff; 34 | } 35 | .leaflet-control-search.search-load .search-cancel { 36 | visibility:hidden; 37 | } 38 | .leaflet-control-search .search-cancel { 39 | display:block; 40 | width:22px; 41 | height:22px; 42 | position:absolute; 43 | right:28px; 44 | margin:6px 0; 45 | background: url('/image/search-icon.png') no-repeat 0 -46px; 46 | text-decoration:none; 47 | filter: alpha(opacity=80); 48 | opacity: 0.8; 49 | } 50 | .leaflet-control-search .search-cancel:hover { 51 | filter: alpha(opacity=100); 52 | opacity: 1; 53 | } 54 | .leaflet-control-search .search-cancel span { 55 | display:none;/* comment for cancel button imageless */ 56 | font-size:18px; 57 | line-height:20px; 58 | color:#ccc; 59 | font-weight:bold; 60 | } 61 | .leaflet-control-search .search-cancel:hover span { 62 | color:#aaa; 63 | } 64 | .leaflet-control-search .search-button { 65 | display:block; 66 | float:left; 67 | width:30px; 68 | height:30px; 69 | background: url('/image/search-icon.png') no-repeat 4px 4px #fff; 70 | border-radius:4px; 71 | } 72 | .leaflet-control-search .search-button:hover { 73 | background: url('/image/search-icon.png') no-repeat 4px -20px #fafafa; 74 | } 75 | .leaflet-control-search .search-tooltip { 76 | position:absolute; 77 | top:100%; 78 | left:0; 79 | float:left; 80 | list-style: none; 81 | padding-left: 0; 82 | min-width:120px; 83 | max-height:122px; 84 | box-shadow: 1px 1px 6px rgba(0,0,0,0.4); 85 | background-color: rgba(0, 0, 0, 0.25); 86 | z-index:1010; 87 | overflow-y:auto; 88 | overflow-x:hidden; 89 | cursor: pointer; 90 | } 91 | .leaflet-control-search .search-tip { 92 | margin:2px; 93 | padding:2px 4px; 94 | display:block; 95 | color:black; 96 | background: #eee; 97 | border-radius:.25em; 98 | text-decoration:none; 99 | white-space:nowrap; 100 | vertical-align:center; 101 | } 102 | .leaflet-control-search .search-button:hover { 103 | background-color: #f4f4f4; 104 | } 105 | .leaflet-control-search .search-tip-select, 106 | .leaflet-control-search .search-tip:hover { 107 | background-color: #fff; 108 | } 109 | .leaflet-control-search .search-alert { 110 | cursor:pointer; 111 | clear:both; 112 | font-size:.75em; 113 | margin-bottom:5px; 114 | padding:0 .25em; 115 | color:#e00; 116 | font-weight:bold; 117 | border-radius:.25em; 118 | } 119 | 120 | 121 | -------------------------------------------------------------------------------- /public_html/css/screen.css: -------------------------------------------------------------------------------- 1 | #map { 2 | width: 800px; 3 | height: 600px; 4 | border: 1px solid #ccc; 5 | } 6 | 7 | #progress { 8 | display: none; 9 | position: absolute; 10 | z-index: 1000; 11 | left: 400px; 12 | top: 300px; 13 | width: 200px; 14 | height: 20px; 15 | margin-top: -20px; 16 | margin-left: -100px; 17 | background-color: #fff; 18 | background-color: rgba(255, 255, 255, 0.7); 19 | border-radius: 4px; 20 | padding: 2px; 21 | } 22 | 23 | #progress-bar { 24 | width: 0; 25 | height: 100%; 26 | background-color: #76A6FC; 27 | border-radius: 4px; 28 | } 29 | -------------------------------------------------------------------------------- /public_html/css/style.css: -------------------------------------------------------------------------------- 1 | /* 2 | Version: 1.0.1 3 | Author:Dave Borghuis aka zeno4ever 4 | Author URI: http://daveborghuis.nl/ 5 | Text Domain: basic-child 6 | Template: basic 7 | */ 8 | body { 9 | font-family: "Lucida Sans Unicode", "Lucida Grande", sans-serif; 10 | } 11 | 12 | #content { 13 | margin-top: 50px; 14 | } 15 | 16 | #map { 17 | width: 100%; 18 | height: 100%; 19 | float: left; 20 | } 21 | 22 | /* For all navagation */ 23 | nav { 24 | position: fixed; 25 | height: 50px; 26 | top: 0; 27 | right: 0; 28 | left: 0; 29 | font-size: 16px; 30 | line-height: 1em; 31 | } 32 | 33 | .menu ul { 34 | list-style-type: none; 35 | margin: 0; 36 | padding: 0; 37 | overflow: hidden; 38 | background-color: #333; 39 | color: #111; 40 | } 41 | 42 | .menu li { 43 | float: left; 44 | } 45 | 46 | .menu li a { 47 | display: block; 48 | color: white; 49 | text-align: center; 50 | padding: 14px 16px; 51 | text-decoration: none; 52 | } 53 | 54 | /* Change the link color to #111 (black) on hover */ 55 | .menu li a:hover { 56 | background-color: #111; 57 | } 58 | 59 | .active { 60 | background-color: #555; 61 | } 62 | 63 | .menu github li a:hover { 64 | width: 25px; 65 | height: 25px; 66 | padding-left: 50px; 67 | float:right; 68 | } 69 | 70 | /* Legend on footer of page*/ 71 | .legend { 72 | height: 20px; 73 | padding-top: 20px; 74 | padding-bottom: 20px; 75 | padding-left: 50px; 76 | position: fixed; 77 | left: 0; 78 | bottom: 0px; 79 | width: 100%; 80 | background-color: #444; 81 | color: white; 82 | } 83 | 84 | .legend ul { 85 | display: flex; 86 | justify-content: center; 87 | align-items: center; 88 | list-style-type: none; 89 | margin: 0; 90 | padding: 0; 91 | height: 100%; 92 | } 93 | 94 | .legend li { 95 | height: 100%; 96 | } 97 | 98 | .legend li a { 99 | /* display: block; 100 | */ text-align: center; 101 | padding: 14px 16px; 102 | text-decoration: none; 103 | } 104 | 105 | /* icon image is 30px - 70px*/ 106 | .legend img { 107 | height: 50px; 108 | float: left; 109 | padding: 0 5px; 110 | } 111 | 112 | /* https://jsfiddle.net/agorshkov23/5osbcpyc/ */ 113 | body > div.container { 114 | position: fixed; 115 | top: calc(40px + 1em); 116 | bottom: calc(40px + 1em); 117 | right: 0; 118 | left: 0; 119 | vertical-align: top; 120 | } 121 | 122 | .content { 123 | padding: 20px 50px 50px 30px; 124 | } 125 | 126 | .pwform { 127 | float: right; 128 | padding-top: 10px; 129 | } 130 | 131 | /* hackerspace wiki consensus */ 132 | .wiki table { 133 | border: 0p; 134 | } 135 | .wiki th { 136 | color: white; 137 | background: black; 138 | border-left: 2ex solid #000; 139 | } 140 | 141 | .wiki th td { 142 | Font-family: "Lucida Sans Unicode", "Lucida Grande", sans-serif; 143 | text-align: left; 144 | } 145 | .wiki th:last-child { 146 | border-left: 0 solid #000; 147 | background:#000 148 | } 149 | .wiki td:last-child { 150 | border-left: 0; 151 | } 152 | .wiki tr:last-child td { 153 | border-top: 0 solid; 154 | } 155 | 156 | .wiki tr:nth-child(odd) {background: #EEE;} 157 | .wiki tr:nth-child(even) {background: #CCC} 158 | 159 | 160 | 161 | /* one space status */ 162 | .status { 163 | margin: 10px; 164 | padding: 8px; 165 | text-align: center; 166 | border-radius: 15px; 167 | } 168 | .other { 169 | text-align: center; 170 | font-size: small; 171 | font-weight: lighter; 172 | display: block; 173 | } 174 | .open { 175 | background-color: green; 176 | color: white; 177 | } 178 | .closed { 179 | background-color: red; 180 | color: white; 181 | } 182 | .spaceimage { 183 | max-width: 100%; 184 | align-content: center; 185 | } 186 | 187 | 188 | /* heatmap */ 189 | #header { 190 | margin-top: 70px; 191 | } 192 | 193 | table { 194 | border-collapse: separate; 195 | background: #ddd; 196 | border-spacing: 2px; 197 | } 198 | th, td { 199 | padding: 0.5ex; 200 | } 201 | td { 202 | font-family: monospace; 203 | text-align: center; 204 | } 205 | tr:first-child th { 206 | position: relative; 207 | /* left: -19px; */ 208 | } 209 | 210 | tr:first-child th:last-child { 211 | position: static; 212 | 213 | } 214 | th:first-child { 215 | text-align: left; 216 | 217 | } 218 | tr:last-child td { 219 | border-top: 2ex solid #ddd 220 | } 221 | td:last-child, th:last-child { 222 | border-left: 2ex solid #ddd; 223 | } 224 | -------------------------------------------------------------------------------- /public_html/dave.php: -------------------------------------------------------------------------------- 1 | query('SELECT * FROM spaces where name="TkkrLab"'); 25 | $row = $result->fetch_assoc(); 26 | 27 | if ($mysqli->connect_error) { 28 | echo 'Errno: '.$mysqli->connect_errno; 29 | echo '
'; 30 | echo 'Error: '.$mysqli->connect_error; 31 | exit(); 32 | } 33 | 34 | echo 'Success: A proper connection to MySQL was made.'; 35 | echo '
'; 36 | echo 'Host information: '.$mysqli->host_info; 37 | echo '
'; 38 | echo 'Protocol version: '.$mysqli->protocol_version; 39 | 40 | var_dump($row); 41 | 42 | $mysqli->close(); 43 | 44 | // $bla = getImageNodes() 45 | 46 | // /** 47 | // * Get all image nodes. 48 | // * 49 | // * @param \DOMNode $node The \DOMDocument instance 50 | // * @param boolean $strict If the document has to be valid 51 | // * 52 | // * @return \DOMNode 53 | // */ 54 | // public function getImageNodes(\DOMNode $node, $strict = true): \DOMNode 55 | // { 56 | // // ... 57 | // } 58 | 59 | 60 | ?> -------------------------------------------------------------------------------- /public_html/dist/MarkerCluster.Default.css: -------------------------------------------------------------------------------- 1 | .marker-cluster-small { 2 | background-color: rgba(30, 30, 30, 0.1); 3 | } 4 | .marker-cluster-small div { 5 | background-color: rgba(30, 30, 30, 0.6); 6 | } 7 | 8 | .marker-cluster-medium { 9 | background-color: rgba(10, 10, 10, 0.2); 10 | } 11 | .marker-cluster-medium div { 12 | background-color: rgba(10, 10, 10, 0.6); 13 | } 14 | 15 | .marker-cluster-large { 16 | background-color: rgba(5, 5, 5, 0.2); 17 | } 18 | .marker-cluster-large div { 19 | background-color: rgba(5, 5, 5, 0.6); 20 | } 21 | 22 | /* IE 6-8 fallback colors */ 23 | .leaflet-oldie .marker-cluster-small { 24 | background-color: rgb(181, 226, 140); 25 | } 26 | .leaflet-oldie .marker-cluster-small div { 27 | background-color: rgb(110, 204, 57); 28 | } 29 | 30 | .leaflet-oldie .marker-cluster-medium { 31 | background-color: rgb(241, 211, 87); 32 | } 33 | .leaflet-oldie .marker-cluster-medium div { 34 | background-color: rgb(240, 194, 12); 35 | } 36 | 37 | .leaflet-oldie .marker-cluster-large { 38 | background-color: rgb(253, 156, 115); 39 | } 40 | .leaflet-oldie .marker-cluster-large div { 41 | background-color: rgb(241, 128, 23); 42 | } 43 | 44 | .marker-cluster { 45 | background-clip: padding-box; 46 | border-radius: 20px; 47 | } 48 | .marker-cluster div { 49 | width: 30px; 50 | height: 30px; 51 | margin-left: 5px; 52 | margin-top: 5px; 53 | 54 | text-align: center; 55 | border-radius: 15px; 56 | font: 12px "Helvetica Neue", Arial, Helvetica, sans-serif; 57 | color: white; 58 | } 59 | .marker-cluster span { 60 | line-height: 30px; 61 | } -------------------------------------------------------------------------------- /public_html/dist/MarkerCluster.css: -------------------------------------------------------------------------------- 1 | .leaflet-cluster-anim .leaflet-marker-icon, .leaflet-cluster-anim .leaflet-marker-shadow { 2 | -webkit-transition: -webkit-transform 0.3s ease-out, opacity 0.3s ease-in; 3 | -moz-transition: -moz-transform 0.3s ease-out, opacity 0.3s ease-in; 4 | -o-transition: -o-transform 0.3s ease-out, opacity 0.3s ease-in; 5 | transition: transform 0.3s ease-out, opacity 0.3s ease-in; 6 | } 7 | 8 | .leaflet-cluster-spider-leg { 9 | /* stroke-dashoffset (duration and function) should match with leaflet-marker-icon transform in order to track it exactly */ 10 | -webkit-transition: -webkit-stroke-dashoffset 0.3s ease-out, -webkit-stroke-opacity 0.3s ease-in; 11 | -moz-transition: -moz-stroke-dashoffset 0.3s ease-out, -moz-stroke-opacity 0.3s ease-in; 12 | -o-transition: -o-stroke-dashoffset 0.3s ease-out, -o-stroke-opacity 0.3s ease-in; 13 | transition: stroke-dashoffset 0.3s ease-out, stroke-opacity 0.3s ease-in; 14 | } 15 | -------------------------------------------------------------------------------- /public_html/dist/leaflet.featuregroup.subgroup.js: -------------------------------------------------------------------------------- 1 | /*! 2 | Leaflet.FeatureGroup.SubGroup 1.0.2+00bb0d4 3 | (c) 2015-2017 Boris Seang 4 | License BSD-2-Clause 5 | */ 6 | !function(e,r){"function"==typeof define&&define.amd?define(["leaflet"],r):r("object"==typeof module&&module.exports?require("leaflet"):e.L)}(this,function(e){e.FeatureGroup.SubGroup=e.FeatureGroup.extend({initialize:function(r,t){e.FeatureGroup.prototype.initialize.call(this,t),this.setParentGroup(r)},setParentGroup:function(r){var t=r instanceof e.LayerGroup;return this._parentGroup=r,this.onAdd=t?"function"==typeof r.addLayers?this._onAddToGroupBatch:this._onAddToGroup:this._onAddToMap,this.onRemove=t?"function"==typeof r.removeLayers?this._onRemoveFromGroupBatch:this._onRemoveFromGroup:this._onRemoveFromMap,this.addLayer=t?this._addLayerToGroup:this._addLayerToMap,this.removeLayer=t?this._removeLayerFromGroup:this._removeLayerFromMap,this},setParentGroupSafe:function(e){var r=this._map;return r&&r.removeLayer(this),this.setParentGroup(e),r&&r.addLayer(this),this},getParentGroup:function(){return this._parentGroup},_onAddToGroupBatch:function(e){var r=this.getLayers();this._map=e,this._parentGroup.addLayers(r)},_onRemoveFromGroupBatch:function(){var e=this.getLayers();this._parentGroup.removeLayers(e),this._map=null},_onAddToGroup:function(e){var r=this._parentGroup;this._map=e,this.eachLayer(r.addLayer,r)},_onRemoveFromGroup:function(){var e=this._parentGroup;this.eachLayer(e.removeLayer,e),this._map=null},_onAddToMap:e.FeatureGroup.prototype.onAdd,_onRemoveFromMap:e.FeatureGroup.prototype.onRemove,_addLayerToGroup:function(e){if(this.hasLayer(e))return this;e.addEventParent(this);var r=this.getLayerId(e);return this._layers[r]=e,this._map&&this._parentGroup.addLayer(e),this.fire("layeradd",{layer:e})},_removeLayerFromGroup:function(e){if(!this.hasLayer(e))return this;var r=e in this._layers?e:this.getLayerId(e);return e=this._layers[r],e.removeEventParent(this),this._map&&e&&this._parentGroup.removeLayer(e),delete this._layers[r],this.fire("layerremove",{layer:e})},_addLayerToMap:e.FeatureGroup.prototype.addLayer,_removeLayerFromMap:e.FeatureGroup.prototype.removeLayer}),e.featureGroup.subGroup=function(r,t){return new e.FeatureGroup.SubGroup(r,t)}}); -------------------------------------------------------------------------------- /public_html/dist/leaflet.markercluster.js: -------------------------------------------------------------------------------- 1 | !function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t((e.Leaflet=e.Leaflet||{},e.Leaflet.markercluster=e.Leaflet.markercluster||{}))}(this,function(e){"use strict";var t=L.MarkerClusterGroup=L.FeatureGroup.extend({options:{maxClusterRadius:80,iconCreateFunction:null,clusterPane:L.Marker.prototype.options.pane,spiderfyOnMaxZoom:!0,showCoverageOnHover:!0,zoomToBoundsOnClick:!0,singleMarkerMode:!1,disableClusteringAtZoom:null,removeOutsideVisibleBounds:!0,animate:!0,animateAddingMarkers:!1,spiderfyDistanceMultiplier:1,spiderLegPolylineOptions:{weight:1.5,color:"#222",opacity:.5},chunkedLoading:!1,chunkInterval:200,chunkDelay:50,chunkProgress:null,polygonOptions:{}},initialize:function(e){L.Util.setOptions(this,e),this.options.iconCreateFunction||(this.options.iconCreateFunction=this._defaultIconCreateFunction),this._featureGroup=L.featureGroup(),this._featureGroup.addEventParent(this),this._nonPointGroup=L.featureGroup(),this._nonPointGroup.addEventParent(this),this._inZoomAnimation=0,this._needsClustering=[],this._needsRemoving=[],this._currentShownBounds=null,this._queue=[],this._childMarkerEventHandlers={dragstart:this._childMarkerDragStart,move:this._childMarkerMoved,dragend:this._childMarkerDragEnd};var t=L.DomUtil.TRANSITION&&this.options.animate;L.extend(this,t?this._withAnimation:this._noAnimation),this._markerCluster=t?L.MarkerCluster:L.MarkerClusterNonAnimated},addLayer:function(e){if(e instanceof L.LayerGroup)return this.addLayers([e]);if(!e.getLatLng)return this._nonPointGroup.addLayer(e),this.fire("layeradd",{layer:e}),this;if(!this._map)return this._needsClustering.push(e),this.fire("layeradd",{layer:e}),this;if(this.hasLayer(e))return this;this._unspiderfy&&this._unspiderfy(),this._addLayer(e,this._maxZoom),this.fire("layeradd",{layer:e}),this._topClusterLevel._recalculateBounds(),this._refreshClustersIcons();var t=e,i=this._zoom;if(e.__parent)for(;t.__parent._zoom>=i;)t=t.__parent;return this._currentShownBounds.contains(t.getLatLng())&&(this.options.animateAddingMarkers?this._animationAddLayer(e,t):this._animationAddLayerNonAnimated(e,t)),this},removeLayer:function(e){return e instanceof L.LayerGroup?this.removeLayers([e]):e.getLatLng?this._map?e.__parent?(this._unspiderfy&&(this._unspiderfy(),this._unspiderfyLayer(e)),this._removeLayer(e,!0),this.fire("layerremove",{layer:e}),this._topClusterLevel._recalculateBounds(),this._refreshClustersIcons(),e.off(this._childMarkerEventHandlers,this),this._featureGroup.hasLayer(e)&&(this._featureGroup.removeLayer(e),e.clusterShow&&e.clusterShow()),this):this:(!this._arraySplice(this._needsClustering,e)&&this.hasLayer(e)&&this._needsRemoving.push({layer:e,latlng:e._latlng}),this.fire("layerremove",{layer:e}),this):(this._nonPointGroup.removeLayer(e),this.fire("layerremove",{layer:e}),this)},addLayers:function(e,t){if(!L.Util.isArray(e))return this.addLayer(e);var i,n=this._featureGroup,r=this._nonPointGroup,s=this.options.chunkedLoading,o=this.options.chunkInterval,a=this.options.chunkProgress,h=e.length,l=0,u=!0;if(this._map){var _=(new Date).getTime(),d=L.bind(function(){for(var c=(new Date).getTime();h>l;l++){if(s&&0===l%200){var p=(new Date).getTime()-c;if(p>o)break}if(i=e[l],i instanceof L.LayerGroup)u&&(e=e.slice(),u=!1),this._extractNonGroupLayers(i,e),h=e.length;else if(i.getLatLng){if(!this.hasLayer(i)&&(this._addLayer(i,this._maxZoom),t||this.fire("layeradd",{layer:i}),i.__parent&&2===i.__parent.getChildCount())){var f=i.__parent.getAllChildMarkers(),m=f[0]===i?f[1]:f[0];n.removeLayer(m)}}else r.addLayer(i),t||this.fire("layeradd",{layer:i})}a&&a(l,h,(new Date).getTime()-_),l===h?(this._topClusterLevel._recalculateBounds(),this._refreshClustersIcons(),this._topClusterLevel._recursivelyAddChildrenToMap(null,this._zoom,this._currentShownBounds)):setTimeout(d,this.options.chunkDelay)},this);d()}else for(var c=this._needsClustering;h>l;l++)i=e[l],i instanceof L.LayerGroup?(u&&(e=e.slice(),u=!1),this._extractNonGroupLayers(i,e),h=e.length):i.getLatLng?this.hasLayer(i)||c.push(i):r.addLayer(i);return this},removeLayers:function(e){var t,i,n=e.length,r=this._featureGroup,s=this._nonPointGroup,o=!0;if(!this._map){for(t=0;n>t;t++)i=e[t],i instanceof L.LayerGroup?(o&&(e=e.slice(),o=!1),this._extractNonGroupLayers(i,e),n=e.length):(this._arraySplice(this._needsClustering,i),s.removeLayer(i),this.hasLayer(i)&&this._needsRemoving.push({layer:i,latlng:i._latlng}),this.fire("layerremove",{layer:i}));return this}if(this._unspiderfy){this._unspiderfy();var a=e.slice(),h=n;for(t=0;h>t;t++)i=a[t],i instanceof L.LayerGroup?(this._extractNonGroupLayers(i,a),h=a.length):this._unspiderfyLayer(i)}for(t=0;n>t;t++)i=e[t],i instanceof L.LayerGroup?(o&&(e=e.slice(),o=!1),this._extractNonGroupLayers(i,e),n=e.length):i.__parent?(this._removeLayer(i,!0,!0),this.fire("layerremove",{layer:i}),r.hasLayer(i)&&(r.removeLayer(i),i.clusterShow&&i.clusterShow())):(s.removeLayer(i),this.fire("layerremove",{layer:i}));return this._topClusterLevel._recalculateBounds(),this._refreshClustersIcons(),this._topClusterLevel._recursivelyAddChildrenToMap(null,this._zoom,this._currentShownBounds),this},clearLayers:function(){return this._map||(this._needsClustering=[],this._needsRemoving=[],delete this._gridClusters,delete this._gridUnclustered),this._noanimationUnspiderfy&&this._noanimationUnspiderfy(),this._featureGroup.clearLayers(),this._nonPointGroup.clearLayers(),this.eachLayer(function(e){e.off(this._childMarkerEventHandlers,this),delete e.__parent},this),this._map&&this._generateInitialClusters(),this},getBounds:function(){var e=new L.LatLngBounds;this._topClusterLevel&&e.extend(this._topClusterLevel._bounds);for(var t=this._needsClustering.length-1;t>=0;t--)e.extend(this._needsClustering[t].getLatLng());return e.extend(this._nonPointGroup.getBounds()),e},eachLayer:function(e,t){var i,n,r,s=this._needsClustering.slice(),o=this._needsRemoving;for(this._topClusterLevel&&this._topClusterLevel.getAllChildMarkers(s),n=s.length-1;n>=0;n--){for(i=!0,r=o.length-1;r>=0;r--)if(o[r].layer===s[n]){i=!1;break}i&&e.call(t,s[n])}this._nonPointGroup.eachLayer(e,t)},getLayers:function(){var e=[];return this.eachLayer(function(t){e.push(t)}),e},getLayer:function(e){var t=null;return e=parseInt(e,10),this.eachLayer(function(i){L.stamp(i)===e&&(t=i)}),t},hasLayer:function(e){if(!e)return!1;var t,i=this._needsClustering;for(t=i.length-1;t>=0;t--)if(i[t]===e)return!0;for(i=this._needsRemoving,t=i.length-1;t>=0;t--)if(i[t].layer===e)return!1;return!(!e.__parent||e.__parent._group!==this)||this._nonPointGroup.hasLayer(e)},zoomToShowLayer:function(e,t){"function"!=typeof t&&(t=function(){});var i=function(){!e._icon&&!e.__parent._icon||this._inZoomAnimation||(this._map.off("moveend",i,this),this.off("animationend",i,this),e._icon?t():e.__parent._icon&&(this.once("spiderfied",t,this),e.__parent.spiderfy()))};e._icon&&this._map.getBounds().contains(e.getLatLng())?t():e.__parent._zoomt;t++)n=this._needsRemoving[t],n.newlatlng=n.layer._latlng,n.layer._latlng=n.latlng;for(t=0,i=this._needsRemoving.length;i>t;t++)n=this._needsRemoving[t],this._removeLayer(n.layer,!0),n.layer._latlng=n.newlatlng;this._needsRemoving=[],this._zoom=Math.round(this._map._zoom),this._currentShownBounds=this._getExpandedVisibleBounds(),this._map.on("zoomend",this._zoomEnd,this),this._map.on("moveend",this._moveEnd,this),this._spiderfierOnAdd&&this._spiderfierOnAdd(),this._bindEvents(),i=this._needsClustering,this._needsClustering=[],this.addLayers(i,!0)},onRemove:function(e){e.off("zoomend",this._zoomEnd,this),e.off("moveend",this._moveEnd,this),this._unbindEvents(),this._map._mapPane.className=this._map._mapPane.className.replace(" leaflet-cluster-anim",""),this._spiderfierOnRemove&&this._spiderfierOnRemove(),delete this._maxLat,this._hideCoverage(),this._featureGroup.remove(),this._nonPointGroup.remove(),this._featureGroup.clearLayers(),this._map=null},getVisibleParent:function(e){for(var t=e;t&&!t._icon;)t=t.__parent;return t||null},_arraySplice:function(e,t){for(var i=e.length-1;i>=0;i--)if(e[i]===t)return e.splice(i,1),!0},_removeFromGridUnclustered:function(e,t){for(var i=this._map,n=this._gridUnclustered,r=Math.floor(this._map.getMinZoom());t>=r&&n[t].removeObject(e,i.project(e.getLatLng(),t));t--);},_childMarkerDragStart:function(e){e.target.__dragStart=e.target._latlng},_childMarkerMoved:function(e){if(!this._ignoreMove&&!e.target.__dragStart){var t=e.target._popup&&e.target._popup.isOpen();this._moveChild(e.target,e.oldLatLng,e.latlng),t&&e.target.openPopup()}},_moveChild:function(e,t,i){e._latlng=t,this.removeLayer(e),e._latlng=i,this.addLayer(e)},_childMarkerDragEnd:function(e){var t=e.target.__dragStart;delete e.target.__dragStart,t&&this._moveChild(e.target,t,e.target._latlng)},_removeLayer:function(e,t,i){var n=this._gridClusters,r=this._gridUnclustered,s=this._featureGroup,o=this._map,a=Math.floor(this._map.getMinZoom());t&&this._removeFromGridUnclustered(e,this._maxZoom);var h,l=e.__parent,u=l._markers;for(this._arraySplice(u,e);l&&(l._childCount--,l._boundsNeedUpdate=!0,!(l._zoomt?"small":100>t?"medium":"large",new L.DivIcon({html:"
"+t+"
",className:"marker-cluster"+i,iconSize:new L.Point(40,40)})},_bindEvents:function(){var e=this._map,t=this.options.spiderfyOnMaxZoom,i=this.options.showCoverageOnHover,n=this.options.zoomToBoundsOnClick;(t||n)&&this.on("clusterclick",this._zoomOrSpiderfy,this),i&&(this.on("clustermouseover",this._showCoverage,this),this.on("clustermouseout",this._hideCoverage,this),e.on("zoomend",this._hideCoverage,this))},_zoomOrSpiderfy:function(e){for(var t=e.layer,i=t;1===i._childClusters.length;)i=i._childClusters[0];i._zoom===this._maxZoom&&i._childCount===t._childCount&&this.options.spiderfyOnMaxZoom?t.spiderfy():this.options.zoomToBoundsOnClick&&t.zoomToBounds(),e.originalEvent&&13===e.originalEvent.keyCode&&this._map._container.focus()},_showCoverage:function(e){var t=this._map;this._inZoomAnimation||(this._shownPolygon&&t.removeLayer(this._shownPolygon),e.layer.getChildCount()>2&&e.layer!==this._spiderfied&&(this._shownPolygon=new L.Polygon(e.layer.getConvexHull(),this.options.polygonOptions),t.addLayer(this._shownPolygon)))},_hideCoverage:function(){this._shownPolygon&&(this._map.removeLayer(this._shownPolygon),this._shownPolygon=null)},_unbindEvents:function(){var e=this.options.spiderfyOnMaxZoom,t=this.options.showCoverageOnHover,i=this.options.zoomToBoundsOnClick,n=this._map;(e||i)&&this.off("clusterclick",this._zoomOrSpiderfy,this),t&&(this.off("clustermouseover",this._showCoverage,this),this.off("clustermouseout",this._hideCoverage,this),n.off("zoomend",this._hideCoverage,this))},_zoomEnd:function(){this._map&&(this._mergeSplitClusters(),this._zoom=Math.round(this._map._zoom),this._currentShownBounds=this._getExpandedVisibleBounds())},_moveEnd:function(){if(!this._inZoomAnimation){var e=this._getExpandedVisibleBounds();this._topClusterLevel._recursivelyRemoveChildrenFromMap(this._currentShownBounds,Math.floor(this._map.getMinZoom()),this._zoom,e),this._topClusterLevel._recursivelyAddChildrenToMap(null,Math.round(this._map._zoom),e),this._currentShownBounds=e}},_generateInitialClusters:function(){var e=Math.ceil(this._map.getMaxZoom()),t=Math.floor(this._map.getMinZoom()),i=this.options.maxClusterRadius,n=i;"function"!=typeof i&&(n=function(){return i}),null!==this.options.disableClusteringAtZoom&&(e=this.options.disableClusteringAtZoom-1),this._maxZoom=e,this._gridClusters={},this._gridUnclustered={};for(var r=e;r>=t;r--)this._gridClusters[r]=new L.DistanceGrid(n(r)),this._gridUnclustered[r]=new L.DistanceGrid(n(r));this._topClusterLevel=new this._markerCluster(this,t-1)},_addLayer:function(e,t){var i,n,r=this._gridClusters,s=this._gridUnclustered,o=Math.floor(this._map.getMinZoom());for(this.options.singleMarkerMode&&this._overrideMarkerIcon(e),e.on(this._childMarkerEventHandlers,this);t>=o;t--){i=this._map.project(e.getLatLng(),t);var a=r[t].getNearObject(i);if(a)return a._addChild(e),e.__parent=a,void 0;if(a=s[t].getNearObject(i)){var h=a.__parent;h&&this._removeLayer(a,!1);var l=new this._markerCluster(this,t,a,e);r[t].addObject(l,this._map.project(l._cLatLng,t)),a.__parent=l,e.__parent=l;var u=l;for(n=t-1;n>h._zoom;n--)u=new this._markerCluster(this,n,u),r[n].addObject(u,this._map.project(a.getLatLng(),n));return h._addChild(u),this._removeFromGridUnclustered(a,t),void 0}s[t].addObject(e,i)}this._topClusterLevel._addChild(e),e.__parent=this._topClusterLevel},_refreshClustersIcons:function(){this._featureGroup.eachLayer(function(e){e instanceof L.MarkerCluster&&e._iconNeedsUpdate&&e._updateIcon()})},_enqueue:function(e){this._queue.push(e),this._queueTimeout||(this._queueTimeout=setTimeout(L.bind(this._processQueue,this),300))},_processQueue:function(){for(var e=0;ee?(this._animationStart(),this._animationZoomOut(this._zoom,e)):this._moveEnd()},_getExpandedVisibleBounds:function(){return this.options.removeOutsideVisibleBounds?L.Browser.mobile?this._checkBoundsMaxLat(this._map.getBounds()):this._checkBoundsMaxLat(this._map.getBounds().pad(1)):this._mapBoundsInfinite},_checkBoundsMaxLat:function(e){var t=this._maxLat;return void 0!==t&&(e.getNorth()>=t&&(e._northEast.lat=1/0),e.getSouth()<=-t&&(e._southWest.lat=-1/0)),e},_animationAddLayerNonAnimated:function(e,t){if(t===e)this._featureGroup.addLayer(e);else if(2===t._childCount){t._addToMap();var i=t.getAllChildMarkers();this._featureGroup.removeLayer(i[0]),this._featureGroup.removeLayer(i[1])}else t._updateIcon()},_extractNonGroupLayers:function(e,t){var i,n=e.getLayers(),r=0;for(t=t||[];r=0;i--)o=h[i],n.contains(o._latlng)||r.removeLayer(o)}),this._forceLayout(),this._topClusterLevel._recursivelyBecomeVisible(n,t),r.eachLayer(function(e){e instanceof L.MarkerCluster||!e._icon||e.clusterShow()}),this._topClusterLevel._recursively(n,e,t,function(e){e._recursivelyRestoreChildPositions(t)}),this._ignoreMove=!1,this._enqueue(function(){this._topClusterLevel._recursively(n,e,s,function(e){r.removeLayer(e),e.clusterShow()}),this._animationEnd()})},_animationZoomOut:function(e,t){this._animationZoomOutSingle(this._topClusterLevel,e-1,t),this._topClusterLevel._recursivelyAddChildrenToMap(null,t,this._getExpandedVisibleBounds()),this._topClusterLevel._recursivelyRemoveChildrenFromMap(this._currentShownBounds,Math.floor(this._map.getMinZoom()),e,this._getExpandedVisibleBounds())},_animationAddLayer:function(e,t){var i=this,n=this._featureGroup;n.addLayer(e),t!==e&&(t._childCount>2?(t._updateIcon(),this._forceLayout(),this._animationStart(),e._setPos(this._map.latLngToLayerPoint(t.getLatLng())),e.clusterHide(),this._enqueue(function(){n.removeLayer(e),e.clusterShow(),i._animationEnd()})):(this._forceLayout(),i._animationStart(),i._animationZoomOutSingle(t,this._map.getMaxZoom(),this._zoom)))}},_animationZoomOutSingle:function(e,t,i){var n=this._getExpandedVisibleBounds(),r=Math.floor(this._map.getMinZoom());e._recursivelyAnimateChildrenInAndAddSelfToMap(n,r,t+1,i);var s=this;this._forceLayout(),e._recursivelyBecomeVisible(n,i),this._enqueue(function(){if(1===e._childCount){var o=e._markers[0];this._ignoreMove=!0,o.setLatLng(o.getLatLng()),this._ignoreMove=!1,o.clusterShow&&o.clusterShow()}else e._recursively(n,i,r,function(e){e._recursivelyRemoveChildrenFromMap(n,r,t+1)});s._animationEnd()})},_animationEnd:function(){this._map&&(this._map._mapPane.className=this._map._mapPane.className.replace(" leaflet-cluster-anim","")),this._inZoomAnimation--,this.fire("animationend")},_forceLayout:function(){L.Util.falseFn(document.body.offsetWidth)}}),L.markerClusterGroup=function(e){return new L.MarkerClusterGroup(e)};var i=L.MarkerCluster=L.Marker.extend({options:L.Icon.prototype.options,initialize:function(e,t,i,n){L.Marker.prototype.initialize.call(this,i?i._cLatLng||i.getLatLng():new L.LatLng(0,0),{icon:this,pane:e.options.clusterPane}),this._group=e,this._zoom=t,this._markers=[],this._childClusters=[],this._childCount=0,this._iconNeedsUpdate=!0,this._boundsNeedUpdate=!0,this._bounds=new L.LatLngBounds,i&&this._addChild(i),n&&this._addChild(n)},getAllChildMarkers:function(e,t){e=e||[];for(var i=this._childClusters.length-1;i>=0;i--)this._childClusters[i].getAllChildMarkers(e);for(var n=this._markers.length-1;n>=0;n--)t&&this._markers[n].__dragStart||e.push(this._markers[n]);return e},getChildCount:function(){return this._childCount},zoomToBounds:function(e){for(var t,i=this._childClusters.slice(),n=this._group._map,r=n.getBoundsZoom(this._bounds),s=this._zoom+1,o=n.getZoom();i.length>0&&r>s;){s++;var a=[];for(t=0;ts?this._group._map.setView(this._latlng,s):o>=r?this._group._map.setView(this._latlng,o+1):this._group._map.fitBounds(this._bounds,e)},getBounds:function(){var e=new L.LatLngBounds;return e.extend(this._bounds),e},_updateIcon:function(){this._iconNeedsUpdate=!0,this._icon&&this.setIcon(this)},createIcon:function(){return this._iconNeedsUpdate&&(this._iconObj=this._group.options.iconCreateFunction(this),this._iconNeedsUpdate=!1),this._iconObj.createIcon()},createShadow:function(){return this._iconObj.createShadow()},_addChild:function(e,t){this._iconNeedsUpdate=!0,this._boundsNeedUpdate=!0,this._setClusterCenter(e),e instanceof L.MarkerCluster?(t||(this._childClusters.push(e),e.__parent=this),this._childCount+=e._childCount):(t||this._markers.push(e),this._childCount++),this.__parent&&this.__parent._addChild(e,!0)},_setClusterCenter:function(e){this._cLatLng||(this._cLatLng=e._cLatLng||e._latlng)},_resetBounds:function(){var e=this._bounds;e._southWest&&(e._southWest.lat=1/0,e._southWest.lng=1/0),e._northEast&&(e._northEast.lat=-1/0,e._northEast.lng=-1/0)},_recalculateBounds:function(){var e,t,i,n,r=this._markers,s=this._childClusters,o=0,a=0,h=this._childCount;if(0!==h){for(this._resetBounds(),e=0;e=0;i--)n=r[i],n._icon&&(n._setPos(t),n.clusterHide())},function(e){var i,n,r=e._childClusters;for(i=r.length-1;i>=0;i--)n=r[i],n._icon&&(n._setPos(t),n.clusterHide())})},_recursivelyAnimateChildrenInAndAddSelfToMap:function(e,t,i,n){this._recursively(e,n,t,function(r){r._recursivelyAnimateChildrenIn(e,r._group._map.latLngToLayerPoint(r.getLatLng()).round(),i),r._isSingleParent()&&i-1===n?(r.clusterShow(),r._recursivelyRemoveChildrenFromMap(e,t,i)):r.clusterHide(),r._addToMap()})},_recursivelyBecomeVisible:function(e,t){this._recursively(e,this._group._map.getMinZoom(),t,null,function(e){e.clusterShow()})},_recursivelyAddChildrenToMap:function(e,t,i){this._recursively(i,this._group._map.getMinZoom()-1,t,function(n){if(t!==n._zoom)for(var r=n._markers.length-1;r>=0;r--){var s=n._markers[r];i.contains(s._latlng)&&(e&&(s._backupLatlng=s.getLatLng(),s.setLatLng(e),s.clusterHide&&s.clusterHide()),n._group._featureGroup.addLayer(s))}},function(t){t._addToMap(e)})},_recursivelyRestoreChildPositions:function(e){for(var t=this._markers.length-1;t>=0;t--){var i=this._markers[t];i._backupLatlng&&(i.setLatLng(i._backupLatlng),delete i._backupLatlng)}if(e-1===this._zoom)for(var n=this._childClusters.length-1;n>=0;n--)this._childClusters[n]._restorePosition();else for(var r=this._childClusters.length-1;r>=0;r--)this._childClusters[r]._recursivelyRestoreChildPositions(e)},_restorePosition:function(){this._backupLatlng&&(this.setLatLng(this._backupLatlng),delete this._backupLatlng)},_recursivelyRemoveChildrenFromMap:function(e,t,i,n){var r,s;this._recursively(e,t-1,i-1,function(e){for(s=e._markers.length-1;s>=0;s--)r=e._markers[s],n&&n.contains(r._latlng)||(e._group._featureGroup.removeLayer(r),r.clusterShow&&r.clusterShow())},function(e){for(s=e._childClusters.length-1;s>=0;s--)r=e._childClusters[s],n&&n.contains(r._latlng)||(e._group._featureGroup.removeLayer(r),r.clusterShow&&r.clusterShow())})},_recursively:function(e,t,i,n,r){var s,o,a=this._childClusters,h=this._zoom;if(h>=t&&(n&&n(this),r&&h===i&&r(this)),t>h||i>h)for(s=a.length-1;s>=0;s--)o=a[s],o._boundsNeedUpdate&&o._recalculateBounds(),e.intersects(o._bounds)&&o._recursively(e,t,i,n,r)},_isSingleParent:function(){return this._childClusters.length>0&&this._childClusters[0]._childCount===this._childCount}});L.Marker.include({clusterHide:function(){var e=this.options.opacity;return this.setOpacity(0),this.options.opacity=e,this},clusterShow:function(){return this.setOpacity(this.options.opacity)}}),L.DistanceGrid=function(e){this._cellSize=e,this._sqCellSize=e*e,this._grid={},this._objectPoint={}},L.DistanceGrid.prototype={addObject:function(e,t){var i=this._getCoord(t.x),n=this._getCoord(t.y),r=this._grid,s=r[n]=r[n]||{},o=s[i]=s[i]||[],a=L.Util.stamp(e);this._objectPoint[a]=t,o.push(e)},updateObject:function(e,t){this.removeObject(e),this.addObject(e,t)},removeObject:function(e,t){var i,n,r=this._getCoord(t.x),s=this._getCoord(t.y),o=this._grid,a=o[s]=o[s]||{},h=a[r]=a[r]||[];for(delete this._objectPoint[L.Util.stamp(e)],i=0,n=h.length;n>i;i++)if(h[i]===e)return h.splice(i,1),1===n&&delete a[r],!0},eachObject:function(e,t){var i,n,r,s,o,a,h,l=this._grid;for(i in l){o=l[i];for(n in o)for(a=o[n],r=0,s=a.length;s>r;r++)h=e.call(t,a[r]),h&&(r--,s--)}},getNearObject:function(e){var t,i,n,r,s,o,a,h,l=this._getCoord(e.x),u=this._getCoord(e.y),_=this._objectPoint,d=this._sqCellSize,c=null;for(t=u-1;u+1>=t;t++)if(r=this._grid[t])for(i=l-1;l+1>=i;i++)if(s=r[i])for(n=0,o=s.length;o>n;n++)a=s[n],h=this._sqDist(_[L.Util.stamp(a)],e),(d>h||d>=h&&null===c)&&(d=h,c=a);return c},_getCoord:function(e){var t=Math.floor(e/this._cellSize);return isFinite(t)?t:e},_sqDist:function(e,t){var i=t.x-e.x,n=t.y-e.y;return i*i+n*n}},function(){L.QuickHull={getDistant:function(e,t){var i=t[1].lat-t[0].lat,n=t[0].lng-t[1].lng;return n*(e.lat-t[0].lat)+i*(e.lng-t[0].lng)},findMostDistantPointFromBaseLine:function(e,t){var i,n,r,s=0,o=null,a=[];for(i=t.length-1;i>=0;i--)n=t[i],r=this.getDistant(n,e),r>0&&(a.push(n),r>s&&(s=r,o=n));return{maxPoint:o,newPoints:a}},buildConvexHull:function(e,t){var i=[],n=this.findMostDistantPointFromBaseLine(e,t);return n.maxPoint?(i=i.concat(this.buildConvexHull([e[0],n.maxPoint],n.newPoints)),i=i.concat(this.buildConvexHull([n.maxPoint,e[1]],n.newPoints))):[e[0]]},getConvexHull:function(e){var t,i=!1,n=!1,r=!1,s=!1,o=null,a=null,h=null,l=null,u=null,_=null;for(t=e.length-1;t>=0;t--){var d=e[t];(i===!1||d.lat>i)&&(o=d,i=d.lat),(n===!1||d.latr)&&(h=d,r=d.lng),(s===!1||d.lng=0;t--)e=i[t].getLatLng(),n.push(e);return L.QuickHull.getConvexHull(n)}}),L.MarkerCluster.include({_2PI:2*Math.PI,_circleFootSeparation:25,_circleStartAngle:0,_spiralFootSeparation:28,_spiralLengthStart:11,_spiralLengthFactor:5,_circleSpiralSwitchover:9,spiderfy:function(){if(this._group._spiderfied!==this&&!this._group._inZoomAnimation){var e,t=this.getAllChildMarkers(null,!0),i=this._group,n=i._map,r=n.latLngToLayerPoint(this._latlng);this._group._unspiderfy(),this._group._spiderfied=this,t.length>=this._circleSpiralSwitchover?e=this._generatePointsSpiral(t.length,r):(r.y+=10,e=this._generatePointsCircle(t.length,r)),this._animationSpiderfy(t,e)}},unspiderfy:function(e){this._group._inZoomAnimation||(this._animationUnspiderfy(e),this._group._spiderfied=null)},_generatePointsCircle:function(e,t){var i,n,r=this._group.options.spiderfyDistanceMultiplier*this._circleFootSeparation*(2+e),s=r/this._2PI,o=this._2PI/e,a=[];for(s=Math.max(s,35),a.length=e,i=0;e>i;i++)n=this._circleStartAngle+i*o,a[i]=new L.Point(t.x+s*Math.cos(n),t.y+s*Math.sin(n))._round();return a},_generatePointsSpiral:function(e,t){var i,n=this._group.options.spiderfyDistanceMultiplier,r=n*this._spiralLengthStart,s=n*this._spiralFootSeparation,o=n*this._spiralLengthFactor*this._2PI,a=0,h=[];for(h.length=e,i=e;i>=0;i--)e>i&&(h[i]=new L.Point(t.x+r*Math.cos(a),t.y+r*Math.sin(a))._round()),a+=s/r+5e-4*i,r+=o/a;return h},_noanimationUnspiderfy:function(){var e,t,i=this._group,n=i._map,r=i._featureGroup,s=this.getAllChildMarkers(null,!0);for(i._ignoreMove=!0,this.setOpacity(1),t=s.length-1;t>=0;t--)e=s[t],r.removeLayer(e),e._preSpiderfyLatlng&&(e.setLatLng(e._preSpiderfyLatlng),delete e._preSpiderfyLatlng),e.setZIndexOffset&&e.setZIndexOffset(0),e._spiderLeg&&(n.removeLayer(e._spiderLeg),delete e._spiderLeg);i.fire("unspiderfied",{cluster:this,markers:s}),i._ignoreMove=!1,i._spiderfied=null}}),L.MarkerClusterNonAnimated=L.MarkerCluster.extend({_animationSpiderfy:function(e,t){var i,n,r,s,o=this._group,a=o._map,h=o._featureGroup,l=this._group.options.spiderLegPolylineOptions;for(o._ignoreMove=!0,i=0;i=0;i--)a=u.layerPointToLatLng(t[i]),n=e[i],n._preSpiderfyLatlng=n._latlng,n.setLatLng(a),n.clusterShow&&n.clusterShow(),p&&(r=n._spiderLeg,s=r._path,s.style.strokeDashoffset=0,r.setStyle({opacity:m}));this.setOpacity(.3),l._ignoreMove=!1,setTimeout(function(){l._animationEnd(),l.fire("spiderfied",{cluster:h,markers:e})},200)},_animationUnspiderfy:function(e){var t,i,n,r,s,o,a=this,h=this._group,l=h._map,u=h._featureGroup,_=e?l._latLngToNewLayerPoint(this._latlng,e.zoom,e.center):l.latLngToLayerPoint(this._latlng),d=this.getAllChildMarkers(null,!0),c=L.Path.SVG;for(h._ignoreMove=!0,h._animationStart(),this.setOpacity(1),i=d.length-1;i>=0;i--)t=d[i],t._preSpiderfyLatlng&&(t.closePopup(),t.setLatLng(t._preSpiderfyLatlng),delete t._preSpiderfyLatlng,o=!0,t._setPos&&(t._setPos(_),o=!1),t.clusterHide&&(t.clusterHide(),o=!1),o&&u.removeLayer(t),c&&(n=t._spiderLeg,r=n._path,s=r.getTotalLength()+.1,r.style.strokeDashoffset=s,n.setStyle({opacity:0})));h._ignoreMove=!1,setTimeout(function(){var e=0;for(i=d.length-1;i>=0;i--)t=d[i],t._spiderLeg&&e++;for(i=d.length-1;i>=0;i--)t=d[i],t._spiderLeg&&(t.clusterShow&&t.clusterShow(),t.setZIndexOffset&&t.setZIndexOffset(0),e>1&&u.removeLayer(t),l.removeLayer(t._spiderLeg),delete t._spiderLeg);h._animationEnd(),h.fire("unspiderfied",{cluster:a,markers:d})},200)}}),L.MarkerClusterGroup.include({_spiderfied:null,unspiderfy:function(){this._unspiderfy.apply(this,arguments)},_spiderfierOnAdd:function(){this._map.on("click",this._unspiderfyWrapper,this),this._map.options.zoomAnimation&&this._map.on("zoomstart",this._unspiderfyZoomStart,this),this._map.on("zoomend",this._noanimationUnspiderfy,this),L.Browser.touch||this._map.getRenderer(this)},_spiderfierOnRemove:function(){this._map.off("click",this._unspiderfyWrapper,this),this._map.off("zoomstart",this._unspiderfyZoomStart,this),this._map.off("zoomanim",this._unspiderfyZoomAnim,this),this._map.off("zoomend",this._noanimationUnspiderfy,this),this._noanimationUnspiderfy() 2 | },_unspiderfyZoomStart:function(){this._map&&this._map.on("zoomanim",this._unspiderfyZoomAnim,this)},_unspiderfyZoomAnim:function(e){L.DomUtil.hasClass(this._map._mapPane,"leaflet-touching")||(this._map.off("zoomanim",this._unspiderfyZoomAnim,this),this._unspiderfy(e))},_unspiderfyWrapper:function(){this._unspiderfy()},_unspiderfy:function(e){this._spiderfied&&this._spiderfied.unspiderfy(e)},_noanimationUnspiderfy:function(){this._spiderfied&&this._spiderfied._noanimationUnspiderfy()},_unspiderfyLayer:function(e){e._spiderLeg&&(this._featureGroup.removeLayer(e),e.clusterShow&&e.clusterShow(),e.setZIndexOffset&&e.setZIndexOffset(0),this._map.removeLayer(e._spiderLeg),delete e._spiderLeg)}}),L.MarkerClusterGroup.include({refreshClusters:function(e){return e?e instanceof L.MarkerClusterGroup?e=e._topClusterLevel.getAllChildMarkers():e instanceof L.LayerGroup?e=e._layers:e instanceof L.MarkerCluster?e=e.getAllChildMarkers():e instanceof L.Marker&&(e=[e]):e=this._topClusterLevel.getAllChildMarkers(),this._flagParentsIconsNeedUpdate(e),this._refreshClustersIcons(),this.options.singleMarkerMode&&this._refreshSingleMarkerModeMarkers(e),this},_flagParentsIconsNeedUpdate:function(e){var t,i;for(t in e)for(i=e[t].__parent;i;)i._iconNeedsUpdate=!0,i=i.__parent},_refreshSingleMarkerModeMarkers:function(e){var t,i;for(t in e)i=e[t],this.hasLayer(i)&&i.setIcon(this._overrideMarkerIcon(i))}}),L.Marker.include({refreshIconOptions:function(e,t){var i=this.options.icon;return L.setOptions(i,e),this.setIcon(i),t&&this.__parent&&this.__parent._group.refreshClusters(this),this}}),e.MarkerClusterGroup=t,e.MarkerCluster=i}); 3 | //# sourceMappingURL=leaflet.markercluster.js.map -------------------------------------------------------------------------------- /public_html/dist/leaflet.markercluster.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"sources":["../src/MarkerClusterGroup.js","../src/MarkerCluster.js","../src/MarkerOpacity.js","../src/DistanceGrid.js","../src/MarkerCluster.QuickHull.js","../src/MarkerCluster.Spiderfier.js","../src/MarkerClusterGroup.Refresh.js"],"names":[],"mappings":"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","file":"dist/leaflet.markercluster.js"} -------------------------------------------------------------------------------- /public_html/dist/leaflet.spin.min.js: -------------------------------------------------------------------------------- 1 | !function(n,i){"function"==typeof define&&define.amd?define(["leaflet","spin.js"],function(i,t){n(i,t)}):"object"==typeof exports?module.exports=function(i,t){return void 0===i&&(i=require("leaflet")),void 0===t&&(t=require("spin.js")),n(i,t),i}:void 0!==i&&i.L&&i.Spinner&&n(i.L,i.Spinner)}(function(n,i){var t={spin:function(n,t){n?(this._spinner||(this._spinner=new i(t).spin(this._container),this._spinning=0),this._spinning++):--this._spinning<=0&&this._spinner&&(this._spinner.stop(),this._spinner=null)}},e=function(){this.on("layeradd",function(n){n.layer.loading&&this.spin(!0),"function"==typeof n.layer.on&&(n.layer.on("data:loading",function(){this.spin(!0)},this),n.layer.on("data:loaded",function(){this.spin(!1)},this))},this),this.on("layerremove",function(n){n.layer.loading&&this.spin(!1),"function"==typeof n.layer.on&&(n.layer.off("data:loaded"),n.layer.off("data:loading"))},this)} 2 | n.Map.include(t),n.Map.addInitHook(e)},window) 3 | -------------------------------------------------------------------------------- /public_html/dist/spin.js: -------------------------------------------------------------------------------- 1 | //fgnass.github.com/spin.js#v1.2.5 2 | (function(window, document, undefined) { 3 | 4 | /** 5 | * Copyright (c) 2011 Felix Gnass [fgnass at neteye dot de] 6 | * Licensed under the MIT license 7 | */ 8 | 9 | var prefixes = ['webkit', 'Moz', 'ms', 'O']; /* Vendor prefixes */ 10 | var animations = {}; /* Animation rules keyed by their name */ 11 | var useCssAnimations; 12 | 13 | /** 14 | * Utility function to create elements. If no tag name is given, 15 | * a DIV is created. Optionally properties can be passed. 16 | */ 17 | function createEl(tag, prop) { 18 | var el = document.createElement(tag || 'div'); 19 | var n; 20 | 21 | for(n in prop) { 22 | el[n] = prop[n]; 23 | } 24 | return el; 25 | } 26 | 27 | /** 28 | * Appends children and returns the parent. 29 | */ 30 | function ins(parent /* child1, child2, ...*/) { 31 | for (var i=1, n=arguments.length; i> 1) : o.left+mid) + 'px', 163 | top: (o.top == 'auto' ? tp.y-ep.y + (target.offsetHeight >> 1) : o.top+mid) + 'px' 164 | }); 165 | } 166 | 167 | el.setAttribute('aria-role', 'progressbar'); 168 | self.lines(el, self.opts); 169 | 170 | if (!useCssAnimations) { 171 | // No CSS animation support, use setTimeout() instead 172 | var i = 0; 173 | var fps = o.fps; 174 | var f = fps/o.speed; 175 | var ostep = (1-o.opacity)/(f*o.trail / 100); 176 | var astep = f/o.lines; 177 | 178 | !function anim() { 179 | i++; 180 | for (var s=o.lines; s; s--) { 181 | var alpha = Math.max(1-(i+s*astep)%f * ostep, o.opacity); 182 | self.opacity(el, o.lines-s, alpha, o); 183 | } 184 | self.timeout = self.el && setTimeout(anim, ~~(1000/fps)); 185 | }(); 186 | } 187 | return self; 188 | }, 189 | stop: function() { 190 | var el = this.el; 191 | if (el) { 192 | clearTimeout(this.timeout); 193 | if (el.parentNode) el.parentNode.removeChild(el); 194 | this.el = undefined; 195 | } 196 | return this; 197 | }, 198 | lines: function(el, o) { 199 | var i = 0; 200 | var seg; 201 | 202 | function fill(color, shadow) { 203 | return css(createEl(), { 204 | position: 'absolute', 205 | width: (o.length+o.width) + 'px', 206 | height: o.width + 'px', 207 | background: color, 208 | boxShadow: shadow, 209 | transformOrigin: 'left', 210 | transform: 'rotate(' + ~~(360/o.lines*i+o.rotate) + 'deg) translate(' + o.radius+'px' +',0)', 211 | borderRadius: (o.width>>1) + 'px' 212 | }); 213 | } 214 | for (; i < o.lines; i++) { 215 | seg = css(createEl(), { 216 | position: 'absolute', 217 | top: 1+~(o.width/2) + 'px', 218 | transform: o.hwaccel ? 'translate3d(0,0,0)' : '', 219 | opacity: o.opacity, 220 | animation: useCssAnimations && addAnimation(o.opacity, o.trail, i, o.lines) + ' ' + 1/o.speed + 's linear infinite' 221 | }); 222 | if (o.shadow) ins(seg, css(fill('#000', '0 0 4px ' + '#000'), {top: 2+'px'})); 223 | ins(el, ins(seg, fill(o.color, '0 0 1px rgba(0,0,0,.1)'))); 224 | } 225 | return el; 226 | }, 227 | opacity: function(el, i, val) { 228 | if (i < el.childNodes.length) el.childNodes[i].style.opacity = val; 229 | } 230 | }); 231 | 232 | ///////////////////////////////////////////////////////////////////////// 233 | // VML rendering for IE 234 | ///////////////////////////////////////////////////////////////////////// 235 | 236 | /** 237 | * Check and init VML support 238 | */ 239 | !function() { 240 | 241 | function vml(tag, attr) { 242 | return createEl('<' + tag + ' xmlns="urn:schemas-microsoft.com:vml" class="spin-vml">', attr); 243 | } 244 | 245 | var s = css(createEl('group'), {behavior: 'url(#default#VML)'}); 246 | 247 | if (!vendor(s, 'transform') && s.adj) { 248 | 249 | // VML support detected. Insert CSS rule ... 250 | sheet.addRule('.spin-vml', 'behavior:url(#default#VML)'); 251 | 252 | Spinner.prototype.lines = function(el, o) { 253 | var r = o.length+o.width; 254 | var s = 2*r; 255 | 256 | function grp() { 257 | return css(vml('group', {coordsize: s +' '+s, coordorigin: -r +' '+-r}), {width: s, height: s}); 258 | } 259 | 260 | var margin = -(o.width+o.length)*2+'px'; 261 | var g = css(grp(), {position: 'absolute', top: margin, left: margin}); 262 | 263 | var i; 264 | 265 | function seg(i, dx, filter) { 266 | ins(g, 267 | ins(css(grp(), {rotation: 360 / o.lines * i + 'deg', left: ~~dx}), 268 | ins(css(vml('roundrect', {arcsize: 1}), { 269 | width: r, 270 | height: o.width, 271 | left: o.radius, 272 | top: -o.width>>1, 273 | filter: filter 274 | }), 275 | vml('fill', {color: o.color, opacity: o.opacity}), 276 | vml('stroke', {opacity: 0}) // transparent stroke to fix color bleeding upon opacity change 277 | ) 278 | ) 279 | ); 280 | } 281 | 282 | if (o.shadow) { 283 | for (i = 1; i <= o.lines; i++) { 284 | seg(i, -2, 'progid:DXImageTransform.Microsoft.Blur(pixelradius=2,makeshadow=1,shadowopacity=.3)'); 285 | } 286 | } 287 | for (i = 1; i <= o.lines; i++) seg(i); 288 | return ins(el, g); 289 | }; 290 | Spinner.prototype.opacity = function(el, i, val, o) { 291 | var c = el.firstChild; 292 | o = o.shadow && o.lines || 0; 293 | if (c && i+o < c.childNodes.length) { 294 | c = c.childNodes[i+o]; c = c && c.firstChild; c = c && c.firstChild; 295 | if (c) c.opacity = val; 296 | } 297 | }; 298 | } 299 | else { 300 | useCssAnimations = vendor(s, 'animation'); 301 | } 302 | }(); 303 | 304 | window.Spinner = Spinner; 305 | 306 | })(window, document); 307 | -------------------------------------------------------------------------------- /public_html/dist/spin.min.js: -------------------------------------------------------------------------------- 1 | //fgnass.github.com/spin.js#v1.2.5 2 | (function(a,b,c){function g(a,c){var d=b.createElement(a||"div"),e;for(e in c)d[e]=c[e];return d}function h(a){for(var b=1,c=arguments.length;b>1):c.left+e)+"px",top:(c.top=="auto"?i.y-h.y+(a.offsetHeight>>1):c.top+e)+"px"})),d.setAttribute("aria-role","progressbar"),b.lines(d,b.opts);if(!f){var j=0,k=c.fps,m=k/c.speed,o=(1-c.opacity)/(m*c.trail/100),p=m/c.lines;!function q(){j++;for(var a=c.lines;a;a--){var e=Math.max(1-(j+a*p)%m*o,c.opacity);b.opacity(d,c.lines-a,e,c)}b.timeout=b.el&&setTimeout(q,~~(1e3/k))}()}return b},stop:function(){var a=this.el;return a&&(clearTimeout(this.timeout),a.parentNode&&a.parentNode.removeChild(a),this.el=c),this},lines:function(a,b){function e(a,d){return l(g(),{position:"absolute",width:b.length+b.width+"px",height:b.width+"px",background:a,boxShadow:d,transformOrigin:"left",transform:"rotate("+~~(360/b.lines*c+b.rotate)+"deg) translate("+b.radius+"px"+",0)",borderRadius:(b.width>>1)+"px"})}var c=0,d;for(;c',b)}var b=l(g("group"),{behavior:"url(#default#VML)"});!k(b,"transform")&&b.adj?(i.addRule(".spin-vml","behavior:url(#default#VML)"),p.prototype.lines=function(b,c){function f(){return l(a("group",{coordsize:e+" "+e,coordorigin:-d+" "+ -d}),{width:e,height:e})}function k(b,e,g){h(i,h(l(f(),{rotation:360/c.lines*b+"deg",left:~~e}),h(l(a("roundrect",{arcsize:1}),{width:d,height:c.width,left:c.radius,top:-c.width>>1,filter:g}),a("fill",{color:c.color,opacity:c.opacity}),a("stroke",{opacity:0}))))}var d=c.length+c.width,e=2*d,g=-(c.width+c.length)*2+"px",i=l(f(),{position:"absolute",top:g,left:g}),j;if(c.shadow)for(j=1;j<=c.lines;j++)k(j,-2,"progid:DXImageTransform.Microsoft.Blur(pixelradius=2,makeshadow=1,shadowopacity=.3)");for(j=1;j<=c.lines;j++)k(j);return h(b,i)},p.prototype.opacity=function(a,b,c,d){var e=a.firstChild;d=d.shadow&&d.lines||0,e&&b+d 2 | 3 | 4 | 13 | 14 | 15 | 16 | 17 | 27 | Map all spaces - FAQ 28 | 29 | 30 | 31 | 34 |
35 |
36 |

FAQ

37 |

What is this site?

38 |
A dynamic map of all hacker/maker spaces and fablabs. There is already some maps out there, but you have to register. After a while the map wil be outdated, so for this map there are several 'live' sources used to be always up-to-date.
39 |

What sources are used?

40 |
There are tree main sources of data : 41 |
    42 |
  • SpaceApi directory 43 |
    Read API directory and check every hackerspace json (if space is open or closed). Updated every hour. (Last update 44 | )
    45 |
  • 46 |
  • fablabs.oi FabLab list 47 |
    Fablab should have status 'active'. Updated weekly. (Last update 48 | )
    49 |
  • 50 |
  • fablabs quebec FabLab list 51 |
    Updated weekly. (Last update 52 | )
    53 |
  • 54 |
  • hackerspace.org semantic data
    Only added to map when space is active, has more then 1 member and site is online. Extra check if a wiki entry is als added by API or Fablab, if so remove wiki entry from map. (Duplicate = 2 entrys are less then 200m apart and name match for 45% or more) Updated weekly. (Last update 55 | )
    56 |
  • 57 |
58 |
59 | Every 1th of the month the temporary database and logfiles are removed and fill again from the sources.
60 | If a site couldn't load (http error etc.) it will retry in increasingly delays, first after 4 hours, then 1 day, 4 days, 8 days. If after 8 days the site still couldn't read it will be skipped till 1th of next month. 61 | Still don't see why your site is not included? Check our error log if we encountered some kind of error. 62 |

What are the meaning of the error codes ?

63 |
I use the following (internal) error codes :
64 |
    65 |
  • Error 0-99 => Curl errors
  • 66 |
  • Error 100-999 => HTTP errors
  • 67 |
  • Error 1000 => No valid json
  • 68 |
  • Error 1001 => Duplicate entry wiki/api/fablab data
  • 69 |
  • Error >2000 => SSL certificate errors, subtract 2000 to get original error no.
  • 70 |
71 |
72 |
73 |

Can you update my entry?

74 |
I don't keep a database of your data, I update this every day/week/month from the mentioned datasources. If you want to see where I got your data from, click on your icon and select 'source'. That should bring you to the data source where you can view your data. Allow at least 24 hours to update on this map. 75 |
76 |

Can i help you?

77 |
Cool, thats nice of you. You could help me several ways : 78 |
    79 |
  • Buy me a drink at one of the events I visit (hackercampings in NL/DE or CCC congress).
  • 80 |
  • If an entry is invalid (e.g. hackerspace close) edit this at the source. You can easily find this to click on the icon and select 'source', this will redirect you straigt to source of the data. See also the 'Hackerspace Census' page.
  • 81 |
  • Contribute with your knowledge, sourcecode of this project can be found at Github
  • 82 |
83 |
84 |
85 |
86 | -------------------------------------------------------------------------------- /public_html/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zeno4ever/map-all-spaces/8e7180830711f2d75c14d677372a6399a483027f/public_html/favicon.ico -------------------------------------------------------------------------------- /public_html/heatmap/colors.inc.php: -------------------------------------------------------------------------------- 1 | $g) { 29 | $h += 360; 30 | } 31 | break; 32 | 33 | case $g: 34 | $h = 60 * ( ( $b - $r ) / $d + 2 ); 35 | break; 36 | 37 | case $b: 38 | $h = 60 * ( ( $r - $g ) / $d + 4 ); 39 | break; 40 | } 41 | } 42 | 43 | return array($h, $l, $s); 44 | }; 45 | 46 | function hslToRgb( $h, $l, $s ){ 47 | $r; 48 | $g; 49 | $b; 50 | 51 | $c = ( 1 - abs( 2 * $l - 1 ) ) * $s; 52 | $x = $c * ( 1 - abs( fmod( ( $h / 60 ), 2 ) - 1 ) ); 53 | $m = $l - ( $c / 2 ); 54 | 55 | if ( $h < 60 ) { 56 | $r = $c; 57 | $g = $x; 58 | $b = 0; 59 | } else if ( $h < 120 ) { 60 | $r = $x; 61 | $g = $c; 62 | $b = 0; 63 | } else if ( $h < 180 ) { 64 | $r = 0; 65 | $g = $c; 66 | $b = $x; 67 | } else if ( $h < 240 ) { 68 | $r = 0; 69 | $g = $x; 70 | $b = $c; 71 | } else if ( $h < 300 ) { 72 | $r = $x; 73 | $g = 0; 74 | $b = $c; 75 | } else { 76 | $r = $c; 77 | $g = 0; 78 | $b = $x; 79 | } 80 | 81 | $r = ( $r + $m ) * 255; 82 | $g = ( $g + $m ) * 255; 83 | $b = ( $b + $m ) * 255; 84 | 85 | return array( floor( $r ), floor( $g ), floor( $b ) ); 86 | }; 87 | -------------------------------------------------------------------------------- /public_html/heatmap/index.css: -------------------------------------------------------------------------------- 1 | /* The overlay effect (full height and width) - lays on top of the container and over the image */ 2 | .overlay { 3 | position: absolute; 4 | bottom: 0; 5 | left: 0; 6 | right: 0; 7 | background-color: #008CBA; 8 | overflow: hidden; 9 | width: 100%; 10 | height: 100%; 11 | transform: scale(0); 12 | transition: .3s ease; 13 | } 14 | 15 | /* When you mouse over the container, the overlay text will "zoom" in display */ 16 | .container:hover .overlay { 17 | transform: scale(1); 18 | } 19 | 20 | body { font-family: sans-serif; 21 | line-height: 2em; 22 | } 23 | 24 | .letters { text-align: justify; } 25 | 26 | a { color: black; font-style: bold; padding: .4rem .7rem } 27 | 28 | .letter { 29 | margin-top: 8em; 30 | /*float: left; 31 | clear: left; 32 | width: 2rem; 33 | margin: 3rem; */ 34 | font-size: 300%; 35 | } 36 | -------------------------------------------------------------------------------- /public_html/heatmap/index.php: -------------------------------------------------------------------------------- 1 | 2 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 23 |
24 |

SpaceAPI heatmap

25 |
26 |

This website retrieves the open/closed status of all hackerspaces registered at spaceapi.io.
27 | This data is then displayed in a heatmap.
28 | Each space is polled 6 times per hour (every 10 minutes).

29 |

The heatmaps was original created by www.vanheusden.com and moved in 20 Sept. 2021 to mapall.space. 30 |

31 | 32 |

Data quality: rawQuery ($sql); 35 | if ($db->getLastErrno() !== 0) { 36 | echo "Error: ". $db->getLastError(); 37 | } 38 | print sprintf('%.2f%%', $result[0]['q']); 39 | ?> (100% is all space-api calls succeeded)

40 |

Percentage of the spaces that are open:

41 |

Click on a hackerspace name to open the heatmap-view.

42 | 43 |

rawQuery ($sql); 46 | if ($db->getLastErrno() !== 0) { 47 | echo "Error: ". $db->getLastError(); 48 | } 49 | $col = 1; 50 | foreach ($result as $letters ) { 51 | ?>style="background-color: #606060;" style="background-color: #808080;" >

61 | 62 | rawQuery ($sql); 65 | if ($db->getLastErrno() !== 0) { 66 | echo "Error: ". $db->getLastError(); 67 | } 68 | 69 | $p = 'a'; 70 | print '

'; 71 | 72 | // while ($space = $result->fetch_assoc()) { 73 | foreach ($result as $space ) { 74 | $name = $space['name']; 75 | $first = strtolower(substr($name, 0, 1)); 76 | if ($first != $p) { 77 | $p = $first; 78 | print "\n


$first
"; 79 | } 80 | 81 | $lns = $space['lns']; 82 | 83 | //meer dan 6 maanden geen update/wijziging open status 84 | if (isset($space['lastupdated']) && strtotime($space['lastupdated']) < strtotime("-6 Month") ) { 85 | $lns = 3; 86 | }; 87 | 88 | 89 | ?> 90 | 91 |

92 |
93 | Color meanings : Now open   Closed   Inactive (longer 6 months) 94 |
95 |

See this page to see a global open/closed percentage for all spaces.

96 |
97 |
98 | 99 |

(C) 2014-2020 by www.vanheusden.com & 2021- by Dave Borghuis

100 | 101 | 102 | -------------------------------------------------------------------------------- /public_html/heatmap/json.php: -------------------------------------------------------------------------------- 1 | rawQuery ( $sql); 30 | if ($db->getLastErrno() !== 0) { 31 | echo "Error $space. Error: ". $db->getLastError().' '.__LINE__; 32 | } 33 | $row = $result[0]; 34 | $their_tz = $row['timezone']; 35 | if ($their_tz == null or $their_tz == '') 36 | $their_tz = 'Europe/Amsterdam'; 37 | 38 | $time = new \DateTime('now', new DateTimeZone($their_tz)); 39 | $timezoneOffset = $time->format('P'); 40 | 41 | $j = json_decode($row['sa'], TRUE); 42 | $s = "closed"; 43 | 44 | if (isset($j['state']['open']) && $j['state']['open']==true) { 45 | $s ='open'; 46 | } else if (isset($j['open'])&& $j['open']==true) { 47 | $s ='open'; 48 | }; 49 | 50 | if($period == 'EVERYTHING') { 51 | $query = 'SELECT DAYOFWEEK(tts) as dayofweek, HOUR(tts) as hour, SUM(open) / COUNT(*) AS open FROM (SELECT CONVERT_TZ(ts, "SYSTEM", "' . $timezoneOffset . '") AS tts, open FROM ' . $table . ') AS i GROUP BY dayofweek, hour'; 52 | } else { 53 | $query = 'SELECT DAYOFWEEK(tts) as dayofweek, HOUR(tts) as hour, SUM(open) / COUNT(*) AS open FROM (SELECT CONVERT_TZ(ts, "SYSTEM", "' . $timezoneOffset . '") AS tts, open FROM ' . $table . ') AS i WHERE tts >= DATE_SUB(CURRENT_TIMESTAMP(), INTERVAL '.$countdays.' DAY) GROUP BY dayofweek, hour'; 54 | } 55 | 56 | for ($i = 0; $i < 24; $i++) { 57 | $avgs[$i] = $avgsn[$i] = 0; 58 | } 59 | 60 | $result = $db->rawQuery ($query); 61 | if ($db->getLastErrno() !== 0) { 62 | echo "Error $space. Error: ". $db->getLastError().' '.__LINE__; 63 | exit; 64 | } 65 | 66 | foreach ($result as $row ) { 67 | $h = $row['hour']; 68 | 69 | $counts[$row['dayofweek']][$h] = $row['open']; 70 | 71 | $avgs[$h] += $row['open']; 72 | $avgsn[$h]++; 73 | } 74 | 75 | for ($i = 0; $i < 24; $i++) { 76 | $h = $i; 77 | if ($avgsn[$i] != 0) { 78 | $counts['avg'][$h] = $avgs[$i] / $avgsn[$i]; 79 | } else { 80 | $counts['avg'][$h] = 0; 81 | }; 82 | } 83 | 84 | foreach ($counts as $d => $v) { 85 | $t = 0; 86 | for ($i = 0; $i < 24; $i++) 87 | $t += $counts[$d][$i]??0; 88 | $counts[$d][24] = $t / 24; 89 | } 90 | 91 | $result = array(); 92 | $result['copyright'] = 'v1 (C) by Folkert van Heusden '; 93 | $result['copyright'] .= ' v2 (C) by Dave Borghuis'; 94 | $result['license'] = 'This work is licensed under a Creative Commons Attribution-NoDerivatives 4.0 International License.'; 95 | $result['data'] = $counts; 96 | $result['tz'] = $their_tz; 97 | $result['period'] = strtolower($period); 98 | $result['space-state'] = $s; 99 | 100 | header('Access-Control-Allow-Origin: *'); 101 | header('Content-Type: application/json; charset=UTF-8'); 102 | echo json_encode($result); 103 | exit; -------------------------------------------------------------------------------- /public_html/heatmap/open.php: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 18 | 19 |
20 |

SpaceAPI

21 |
22 | rawQuery ( $sql); 26 | if ($db->getLastErrno() !== 0) { 27 | echo "Error $space. Error: ". $db->getLastError().' '.__LINE__; 28 | exit; 29 | } 30 | 31 | $n = 0; 32 | foreach ($result as $space ) { 33 | $key = $space['key']; 34 | $name = $space['name']; 35 | $url = $space['url']; 36 | 37 | $sql2 = 'SELECT sum(open)/count(*) * 100 as open from data_' . $key; 38 | $result2 = $db->rawQuery ( $sql2); 39 | if ($db->getLastErrno() !== 0) { 40 | echo "Error $space. Error: ". $db->getLastError().' '.__LINE__; 41 | exit; 42 | } 43 | $row2 = $result2[0]; 44 | 45 | $data[] = [$name, $row2['open']]; 46 | 47 | $n++; 48 | } 49 | 50 | function sortByOrder($a, $b) 51 | { 52 | return $b[1] - $a[1]; 53 | } 54 | 55 | usort($data, 'sortByOrder'); 56 | 57 | ?> 58 | 59 | 60 | 61 | 62 | 63 | = 100 || $data[$i][1] <= 0.0) 66 | continue; 67 | 68 | ?> 69 | 70 | 71 | 74 |
spacepercentage open
'.$data[$i][0]; ?>%
75 | 76 |

77 |
78 | 79 |
80 |
81 | 82 |

(C) 2014-2020 by www.vanheusden.com & 2021- by Dave Borghuis

83 | 84 | 85 | -------------------------------------------------------------------------------- /public_html/heatmap/opendata-logo.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zeno4ever/map-all-spaces/8e7180830711f2d75c14d677372a6399a483027f/public_html/heatmap/opendata-logo.jpg -------------------------------------------------------------------------------- /public_html/heatmap/sakura.css: -------------------------------------------------------------------------------- 1 | /* Sakura.css v1.0.0 2 | * ================ 3 | * Minimal css theme. 4 | * Project: https://github.com/oxalorg/sakura 5 | */ 6 | /* Body */ 7 | html { 8 | font-size: 62.5%; 9 | font-family: serif; } 10 | 11 | body { 12 | font-size: 1.8rem; 13 | line-height: 1.618; 14 | max-width: 38em; 15 | margin: auto; 16 | color: #4a4a4a; 17 | padding: 13px; } 18 | 19 | @media (max-width: 684px) { 20 | body { 21 | font-size: 1.53rem; } } 22 | 23 | @media (max-width: 382px) { 24 | body { 25 | font-size: 1.35rem; } } 26 | 27 | h1, h2, h3, h4, h5, h6 { 28 | line-height: 1.1; 29 | font-family: Verdana, Geneva, sans-serif; 30 | font-weight: 700; 31 | overflow-wrap: break-word; 32 | word-wrap: break-word; 33 | -ms-word-break: break-all; 34 | word-break: break-word; 35 | -ms-hyphens: auto; 36 | -moz-hyphens: auto; 37 | -webkit-hyphens: auto; 38 | hyphens: auto; } 39 | 40 | h1 { 41 | font-size: 2.35em; } 42 | 43 | h2 { 44 | font-size: 2.00em; } 45 | 46 | h3 { 47 | font-size: 1.75em; } 48 | 49 | h4 { 50 | font-size: 1.5em; } 51 | 52 | h5 { 53 | font-size: 1.25em; } 54 | 55 | h6 { 56 | font-size: 1em; } 57 | 58 | small, sub, sup { 59 | font-size: 75%; } 60 | 61 | hr { 62 | border-color: #2c8898; } 63 | 64 | a { 65 | text-decoration: none; 66 | color: #2c8898; } 67 | a:hover { 68 | color: #982c61; 69 | border-bottom: 2px solid #4a4a4a; } 70 | 71 | ul { 72 | padding-left: 1.4em; } 73 | 74 | li { 75 | margin-bottom: 0.4em; } 76 | 77 | blockquote { 78 | font-style: italic; 79 | margin-left: 1.5em; 80 | padding-left: 1em; 81 | border-left: 3px solid #2c8898; } 82 | 83 | img { 84 | max-width: 100%; } 85 | 86 | /* Pre and Code */ 87 | pre { 88 | background-color: #f1f1f1; 89 | display: block; 90 | padding: 1em; 91 | overflow-x: auto; } 92 | 93 | code { 94 | font-size: 0.9em; 95 | padding: 0 0.5em; 96 | background-color: #f1f1f1; 97 | white-space: pre-wrap; } 98 | 99 | pre > code { 100 | padding: 0; 101 | background-color: transparent; 102 | white-space: pre; } 103 | 104 | /* Tables */ 105 | table { 106 | text-align: justify; 107 | width: 100%; 108 | border-collapse: collapse; } 109 | 110 | td, th { 111 | padding: 0.5em; 112 | border-bottom: 1px solid #f1f1f1; } 113 | 114 | /* Buttons, forms and input */ 115 | input, textarea { 116 | border: 1px solid #4a4a4a; } 117 | input:focus, textarea:focus { 118 | border: 1px solid #2c8898; } 119 | 120 | textarea { 121 | width: 100%; } 122 | 123 | .button, button, input[type="submit"], input[type="reset"], input[type="button"] { 124 | display: inline-block; 125 | padding: 5px 10px; 126 | text-align: center; 127 | text-decoration: none; 128 | white-space: nowrap; 129 | background-color: #2c8898; 130 | color: #f9f9f9; 131 | border-radius: 1px; 132 | border: 1px solid #2c8898; 133 | cursor: pointer; 134 | box-sizing: border-box; } 135 | .button[disabled], button[disabled], input[type="submit"][disabled], input[type="reset"][disabled], input[type="button"][disabled] { 136 | cursor: default; 137 | opacity: .5; } 138 | .button:focus, .button:hover, button:focus, button:hover, input[type="submit"]:focus, input[type="submit"]:hover, input[type="reset"]:focus, input[type="reset"]:hover, input[type="button"]:focus, input[type="button"]:hover { 139 | background-color: #982c61; 140 | border-color: #982c61; 141 | color: #f9f9f9; 142 | outline: 0; } 143 | 144 | textarea, select, input[type] { 145 | color: #4a4a4a; 146 | padding: 6px 10px; 147 | /* The 6px vertically centers text on FF, ignored by Webkit */ 148 | margin-bottom: 10px; 149 | background-color: #f1f1f1; 150 | border: 1px solid #f1f1f1; 151 | border-radius: 4px; 152 | box-shadow: none; 153 | box-sizing: border-box; } 154 | textarea:focus, select:focus, input[type]:focus { 155 | border: 1px solid #2c8898; 156 | outline: 0; } 157 | 158 | input[type="checkbox"]:focus { 159 | outline: 1px dotted #2c8898; } 160 | 161 | label, legend, fieldset { 162 | display: block; 163 | margin-bottom: .5rem; 164 | font-weight: 600; } 165 | -------------------------------------------------------------------------------- /public_html/heatmap/show.css: -------------------------------------------------------------------------------- 1 | table { 2 | border-collapse: separate; 3 | background: #ddd; 4 | border-spacing: 2px; 5 | } 6 | th, td { 7 | padding: 0.5ex; 8 | } 9 | td { 10 | font-family: monospace; 11 | text-align: center; 12 | } 13 | tr:first-child th { 14 | position: relative; 15 | left: -19px; 16 | } 17 | 18 | tr:first-child th:last-child { 19 | position: static; 20 | 21 | } 22 | th:first-child { 23 | text-align: left; 24 | 25 | } 26 | tr:last-child td { 27 | border-top: 2ex solid #ddd 28 | } 29 | td:last-child, th:last-child { 30 | border-left: 2ex solid #ddd; 31 | } 32 | -------------------------------------------------------------------------------- /public_html/heatmap/show.php: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | 9 | 10 | 13 | rawQuery ($sql); 20 | if ($db->getLastErrno() !== 0) { 21 | echo "Error: ". $db->getLastError(); 22 | } 23 | $row = $result[0]; 24 | $j = json_decode($row['sa'], TRUE); 25 | 26 | //Timezone calculations 27 | $their_tz = $row['timezone']; 28 | 29 | if ($their_tz == null or $their_tz == '') { 30 | $their_tz = 'Europe/Amsterdam'; 31 | } 32 | 33 | $country =''; 34 | if (isset($row['timezone_long'])) { 35 | $tz_j = json_decode($row['timezone_long'], TRUE); 36 | $country = $tz_j['countryName']; 37 | }; 38 | 39 | $time = new \DateTime('now', new DateTimeZone($their_tz)); 40 | $timezoneOffset = $time->format('P'); 41 | 42 | ?> 43 |

44 | 45 |

46 |

    47 | 48 |

  • 49 |
  • ()
    50 |
  • latitude: / longitude:
    51 | 52 |
  • timezone: 53 |

    '; 57 | echo '

    Timezone offset=' . $timezoneOffset.'

    '; 58 | 59 | $sql = 'SELECT DATE(ts) as datum, TIME(ts) as tijd, CONVERT_TZ(ts, "SYSTEM", "' . $timezoneOffset . '") AS tztijd, open FROM ' . $table . ' WHERE ts >= DATE_SUB(NOW(),INTERVAL 1 MONTH) ORDER BY ts'; 60 | // $result = $mysqli->query($sql); 61 | 62 | 63 | $result = $db->rawQuery ( $sql); 64 | if ($db->getLastErrno() !== 0) { 65 | echo "Error ". $db->getLastError().' '.__LINE__; 66 | } 67 | 68 | $lastrow=null; 69 | foreach ($result as $row ) { 70 | if ($lastrow['open'] !== $row['open']) { 71 | echo $row['datum'] . ' ' . $row['tijd'] . ' TZ -' . $row['tztijd'] . ' ' . $row['open'] . '

    '; 72 | }; 73 | $lastrow = $row; 74 | } 75 | echo 'Last entry : '. $lastrow['datum'] . ' ' . $lastrow['tijd'] . ' TZ -' . $lastrow['tztijd'] . ' ' . $lastrow['open'] . '

    '; 76 | echo '

'; 77 | } 78 | ?> 79 | 80 |   103 |

Last state change:

105 | 106 |

Data quality: (100% is all space-api calls succeeded)

107 | 108 | query($sql); 113 | 114 | $result = $db->rawQuery ( $sql); 115 | if ($db->getLastErrno() !== 0) { 116 | echo "Error ". $db->getLastError().' '.__LINE__; 117 | } 118 | $row = $result[0]; 119 | // $row = $result->fetch_assoc(); 120 | ?> 121 |

Percentage open:

122 | 123 |

last week (7 days)

= DATE_SUB(CURRENT_TIMESTAMP(), INTERVAL 7 DAY) GROUP BY dayofweek, hour'); 125 | ?> 126 |

last month (31 days)

= DATE_SUB(CURRENT_TIMESTAMP(), INTERVAL 31 DAY) GROUP BY dayofweek, hour'); 128 | ?> 129 |

this year (365 days)

= DATE_SUB(CURRENT_TIMESTAMP(), INTERVAL 365 DAY) GROUP BY dayofweek, hour'); 131 | ?> 132 |

everything

rawQuery ( $query); 143 | if ($db->getLastErrno() !== 0) { 144 | echo "Error: ". $db->getLastError().' '.__LINE__; 145 | exit; 146 | }; 147 | 148 | foreach ($results as $row ) { 149 | $h = $row['hour']; 150 | 151 | $counts[$row['dayofweek']][$h] = $row['open']; 152 | 153 | $avgs[$h] += $row['open']; 154 | $avgsn[$h]++; 155 | } 156 | 157 | for ($i = 0; $i < 24; $i++) { 158 | $h = $i; 159 | if ($avgsn[$i] != 0) { 160 | $counts['avg'][$h] = $avgs[$i] / $avgsn[$i]; 161 | } else { 162 | $counts['avg'][$h] = 0; 163 | } 164 | } 165 | 166 | foreach ($counts as $d => $v) { 167 | $t = 0; 168 | for ($i = 0; $i < 24; $i++) { 169 | if (!array_key_exists($i, $counts[$d])) { 170 | $counts[$d][$i] = 0; 171 | } 172 | $t += $counts[$d][$i]; 173 | } 174 | $counts[$d][24] = $t / 24; 175 | } 176 | 177 | ?> $v) { 189 | ?> 190 | 191 | 212 |
avg
'avg',1 => 'Sun',2 => 'Mon',3 => 'Tue' ,4 => 'Wed' ,5 =>'Thu' ,6 =>'Fri' ,7=>'Sat'); 193 | print $dayArray[$d]; 194 | ?>
213 | 217 | 218 |
219 |
220 |

221 |
222 |
223 | 224 |

(C) 2014-2020 by www.vanheusden.com & 2021- by Dave Borghuis

225 | 226 | 227 | 228 | 229 | -------------------------------------------------------------------------------- /public_html/heatmap/stats.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 |

SpaceAPI statistics

14 | 15 | query($sql); 18 | ?> 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | fetch_assoc()) { 29 | $j = json_decode($row['sa'], TRUE); 30 | $sql = 'SELECT sum(open) * 100 / count(*) as openp, count(*) AS n FROM data_' . $row['key']; 31 | $result2 = $mysqli->query($sql); 32 | $row2 = $result2->fetch_assoc(); 33 | ?> 34 | 35 | 36 | 37 | 38 | 39 | 45 | 46 | 47 |
spaceopen%recordsdata age
48 | 49 |
50 |
51 | 52 |

(C) 2014-2020 by www.vanheusden.com & 2021- by Dave Borghuis

53 | 54 | 55 | -------------------------------------------------------------------------------- /public_html/hswikilist.php: -------------------------------------------------------------------------------- 1 | 12 | 13 | 14 | 15 | 16 | 17 | Map all spaces - Hackerspace Censes 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 42 | 43 | 44 | 45 |
46 | 49 |
50 | 54 | 55 | 56 | 57 | '; 58 | } 59 | ?> 60 |
61 | 62 |
63 | The wiki is a very nice tool so everyone can add their own space. The challenge is that to ensure that the entry remain up to date in order for the list to remain accurate and relevant. To solve this some people from hackerspace.org started the Hackerspace Census to check all entry's and set them to 'closed' when hackerspace doesn't exist any more. 64 |

65 | I created a script that tries to automate this. For every entry it wil: 66 |

    67 |
  • Check if site is still up. After 3 failed tests the entry wil be set to 'suspected inactive' and a email will be send.
  • 68 |
  • Check if there is recent activity on one of the following places : Site (via rss feeds), Twitter, Mailinglist, Wiki, SpaceAPI, Newsfeed and Calenderfeed. If this is the case the entry on wiki is updated with remark that this is checked. If the activity is to long ago (> 2 years) entry will be set to status 'suspected inactive' and email will be send.
  • 69 |
  • Left over are the entrys that have to be checked manual.
  • 70 |
71 | Below you will find a list of all of the hackerspace entities that have to be manually checked. If you want to help - pick an entry and figure out whether the hackerspace is still open or not (social media, website, etc). If it still active, add (hidden) text to the wiki like: '<!-- set to $status for $reason, Checked by person $yourname on $date -->' or set the status to 'closed' or 'suspected inactive'. To do this you'll need to create a Hackerspaces.org wiki log in and then you can edit any pages. 72 | rawQuery("SELECT DISTINCT name,wikiurl FROM wikispace WHERE status = 'manual' ORDER BY wikiurl;"); 75 | 76 | echo '

Right now there are ' . count($result) . ' hackerspaces to be check manualy.

'; 77 | if ($result) { 78 | shuffle($result); 79 | echo ''; 80 | foreach ($result as $space) { 81 | echo ''; 82 | echo ''; 83 | 84 | //if loged in 85 | if ($validUser) { 86 | echo ''; 87 | echo ''; 88 | echo ''; 89 | echo ''; 90 | } else { 91 | echo ''; 92 | } 93 | }; 94 | echo '
Hackerspace Name
' . $space['name'] . '
'; 95 | } else { 96 | echo '

All done, excelent!

'; 97 | } 98 | ?> 99 |
100 |
101 | 102 | 103 | -------------------------------------------------------------------------------- /public_html/image/fablab.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zeno4ever/map-all-spaces/8e7180830711f2d75c14d677372a6399a483027f/public_html/image/fablab.png -------------------------------------------------------------------------------- /public_html/image/github-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zeno4ever/map-all-spaces/8e7180830711f2d75c14d677372a6399a483027f/public_html/image/github-white.png -------------------------------------------------------------------------------- /public_html/image/hs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zeno4ever/map-all-spaces/8e7180830711f2d75c14d677372a6399a483027f/public_html/image/hs.png -------------------------------------------------------------------------------- /public_html/image/hsWikiLogo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zeno4ever/map-all-spaces/8e7180830711f2d75c14d677372a6399a483027f/public_html/image/hsWikiLogo.png -------------------------------------------------------------------------------- /public_html/image/hs_black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zeno4ever/map-all-spaces/8e7180830711f2d75c14d677372a6399a483027f/public_html/image/hs_black.png -------------------------------------------------------------------------------- /public_html/image/hs_closed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zeno4ever/map-all-spaces/8e7180830711f2d75c14d677372a6399a483027f/public_html/image/hs_closed.png -------------------------------------------------------------------------------- /public_html/image/hs_open.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zeno4ever/map-all-spaces/8e7180830711f2d75c14d677372a6399a483027f/public_html/image/hs_open.png -------------------------------------------------------------------------------- /public_html/image/hslogo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zeno4ever/map-all-spaces/8e7180830711f2d75c14d677372a6399a483027f/public_html/image/hslogo.png -------------------------------------------------------------------------------- /public_html/image/loader.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zeno4ever/map-all-spaces/8e7180830711f2d75c14d677372a6399a483027f/public_html/image/loader.gif -------------------------------------------------------------------------------- /public_html/image/search-icon-mobile.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zeno4ever/map-all-spaces/8e7180830711f2d75c14d677372a6399a483027f/public_html/image/search-icon-mobile.png -------------------------------------------------------------------------------- /public_html/image/search-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zeno4ever/map-all-spaces/8e7180830711f2d75c14d677372a6399a483027f/public_html/image/search-icon.png -------------------------------------------------------------------------------- /public_html/index.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Map all spaces 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 48 |
49 |
50 |
51 | 199 |
200 |
    201 |
  • Source
  • 202 |
  • source spaceapiAPI open
  • 203 |
  • source spaceapiAPI closed
  • 204 |
  • source spaceapiAPI (unknown)
  • 205 |
  • source fablab.oiFablab
  • 206 |
  • source wiki.hackerspaces.orgWiki
  • 207 |
208 |
209 | 210 | 211 | -------------------------------------------------------------------------------- /public_html/login.php: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public_html/onespace.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | One Space Status 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 25 |
26 | 27 |

Select a space!

28 |
29 |
30 |
31 | 32 | 33 |
34 |
35 | This page is meant to be used as static page to view current the open/closed status of your selected page. It will be 36 | refreshed every 10 minutes. Once a space is selected the header and this text wil be disappear. 37 |
38 | 197 | 198 | 199 | 200 | -------------------------------------------------------------------------------- /public_html/spaceapi/index.php: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public_html/spaceapi/json.php: -------------------------------------------------------------------------------- 1 | 0) { 41 | echo '"lastchange": '. $spaceLastChange; 42 | } 43 | echo ' 44 | } 45 | '; 46 | 47 | ?> -------------------------------------------------------------------------------- /public_html/spaceapi/spacestatus.txt: -------------------------------------------------------------------------------- 1 | 1 -------------------------------------------------------------------------------- /public_html/testapi.php: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public_html/wikiupdate.php: -------------------------------------------------------------------------------- 1 | where("name", $hackerspace); 18 | $result = $db->update("wikispace", array('status'=>$status )); 19 | if ($db->getLastErrno() !== 0) { 20 | echo "GetOne $source. Error: ". $db->getLastError(); 21 | } 22 | 23 | //just to be sure, only when user is loged in 24 | if ($_COOKIE['wikipw'] == substr(sha1($wikiPasswd),0,20)) { 25 | updateOneHackerSpace($hackerspace,$action); 26 | } 27 | 28 | return true; 29 | ?> --------------------------------------------------------------------------------