├── docker ├── php.ini └── vhost.conf ├── VERSION ├── webroot ├── css │ ├── mapicoin-leboncoin.css │ ├── mapicoin-kijiji.css │ ├── ribbon.min.css │ ├── bootstrap-theme.min.css.map │ ├── mapicoin-mobile.css │ ├── mapicoin-page.css │ ├── mapicoin.css │ ├── bootstrap-theme.min.css │ └── sweetalert.css ├── robots.txt ├── favicon.ico ├── img │ ├── bg-h1.png │ ├── gpsloc.png │ ├── checkmark.png │ ├── chrome-64x64.png │ ├── firefox-64x64.png │ ├── mapicoin-logo.png │ ├── mapicoin-meta-og.png │ └── howto │ │ ├── mapicoin-how-to-1-min.png │ │ ├── mapicoin-how-to-2-min.png │ │ ├── mapicoin-how-to-3-min.png │ │ └── mapicoin-how-to-4-min.png ├── fonts │ ├── FontAwesome.otf │ ├── fontawesome-webfont.eot │ ├── fontawesome-webfont.ttf │ ├── fontawesome-webfont.woff │ ├── fontawesome-webfont.woff2 │ ├── glyphicons-halflings-regular.eot │ ├── glyphicons-halflings-regular.ttf │ ├── glyphicons-halflings-regular.woff │ └── glyphicons-halflings-regular.woff2 ├── plugins │ └── chrome-mapicoin.crx ├── inc │ ├── page-footer.inc.php │ ├── ga.inc.php │ ├── header.inc.php │ ├── navbar.inc.php │ ├── left-panel.inc.php │ └── modal.inc.php ├── js │ ├── npm.js │ ├── jquery.lazyload.min.js │ ├── geolocation-marker.min.js │ ├── mapicoin-tools.js │ ├── mapicoin.js │ ├── sweetalert.min.js │ └── mapicoin-map.js ├── stats.php ├── index.php ├── get-ads.php ├── comment-ca-marche.php └── mentions-legales.php ├── .env ├── .gitignore ├── Dockerfile ├── README.md ├── inc ├── parameters.sample.json ├── crawlers │ ├── GumtreeCrawler.class.php │ ├── CraigslistCrawler.class.php │ ├── Crawler.class.php │ ├── KijijiCrawler.class.php │ └── LeboncoinCrawler.class.php ├── config.inc.php ├── nginx-vhost.conf └── functions.inc.php └── docker-compose.yml /docker/php.ini: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /VERSION: -------------------------------------------------------------------------------- 1 | v1.11.2 2 | -------------------------------------------------------------------------------- /webroot/css/mapicoin-leboncoin.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /webroot/robots.txt: -------------------------------------------------------------------------------- 1 | User-agent: * 2 | Allow: / 3 | -------------------------------------------------------------------------------- /.env: -------------------------------------------------------------------------------- 1 | MYSQL_ROOT_PASSWORD=secret 2 | MYSQL_DATABASE=mapicoin -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ._* 2 | inc/parameters.json 3 | .env 4 | .DS_Store 5 | -------------------------------------------------------------------------------- /webroot/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ydalb/mapicoin/HEAD/webroot/favicon.ico -------------------------------------------------------------------------------- /webroot/img/bg-h1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ydalb/mapicoin/HEAD/webroot/img/bg-h1.png -------------------------------------------------------------------------------- /webroot/img/gpsloc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ydalb/mapicoin/HEAD/webroot/img/gpsloc.png -------------------------------------------------------------------------------- /webroot/img/checkmark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ydalb/mapicoin/HEAD/webroot/img/checkmark.png -------------------------------------------------------------------------------- /webroot/fonts/FontAwesome.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ydalb/mapicoin/HEAD/webroot/fonts/FontAwesome.otf -------------------------------------------------------------------------------- /webroot/img/chrome-64x64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ydalb/mapicoin/HEAD/webroot/img/chrome-64x64.png -------------------------------------------------------------------------------- /webroot/img/firefox-64x64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ydalb/mapicoin/HEAD/webroot/img/firefox-64x64.png -------------------------------------------------------------------------------- /webroot/img/mapicoin-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ydalb/mapicoin/HEAD/webroot/img/mapicoin-logo.png -------------------------------------------------------------------------------- /webroot/img/mapicoin-meta-og.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ydalb/mapicoin/HEAD/webroot/img/mapicoin-meta-og.png -------------------------------------------------------------------------------- /webroot/plugins/chrome-mapicoin.crx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ydalb/mapicoin/HEAD/webroot/plugins/chrome-mapicoin.crx -------------------------------------------------------------------------------- /webroot/fonts/fontawesome-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ydalb/mapicoin/HEAD/webroot/fonts/fontawesome-webfont.eot -------------------------------------------------------------------------------- /webroot/fonts/fontawesome-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ydalb/mapicoin/HEAD/webroot/fonts/fontawesome-webfont.ttf -------------------------------------------------------------------------------- /webroot/fonts/fontawesome-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ydalb/mapicoin/HEAD/webroot/fonts/fontawesome-webfont.woff -------------------------------------------------------------------------------- /webroot/fonts/fontawesome-webfont.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ydalb/mapicoin/HEAD/webroot/fonts/fontawesome-webfont.woff2 -------------------------------------------------------------------------------- /docker/vhost.conf: -------------------------------------------------------------------------------- 1 | 2 | DocumentRoot "/var/www/html/webroot" 3 | ServerName localhost 4 | 5 | -------------------------------------------------------------------------------- /webroot/img/howto/mapicoin-how-to-1-min.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ydalb/mapicoin/HEAD/webroot/img/howto/mapicoin-how-to-1-min.png -------------------------------------------------------------------------------- /webroot/img/howto/mapicoin-how-to-2-min.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ydalb/mapicoin/HEAD/webroot/img/howto/mapicoin-how-to-2-min.png -------------------------------------------------------------------------------- /webroot/img/howto/mapicoin-how-to-3-min.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ydalb/mapicoin/HEAD/webroot/img/howto/mapicoin-how-to-3-min.png -------------------------------------------------------------------------------- /webroot/img/howto/mapicoin-how-to-4-min.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ydalb/mapicoin/HEAD/webroot/img/howto/mapicoin-how-to-4-min.png -------------------------------------------------------------------------------- /webroot/fonts/glyphicons-halflings-regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ydalb/mapicoin/HEAD/webroot/fonts/glyphicons-halflings-regular.eot -------------------------------------------------------------------------------- /webroot/fonts/glyphicons-halflings-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ydalb/mapicoin/HEAD/webroot/fonts/glyphicons-halflings-regular.ttf -------------------------------------------------------------------------------- /webroot/fonts/glyphicons-halflings-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ydalb/mapicoin/HEAD/webroot/fonts/glyphicons-halflings-regular.woff -------------------------------------------------------------------------------- /webroot/fonts/glyphicons-halflings-regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ydalb/mapicoin/HEAD/webroot/fonts/glyphicons-halflings-regular.woff2 -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM php:7-apache 2 | 3 | #RUN apt-get update && apt-get install -y libmemcached-dev 4 | RUN docker-php-ext-install mysqli 5 | 6 | RUN a2enmod rewrite -------------------------------------------------------------------------------- /webroot/css/mapicoin-kijiji.css: -------------------------------------------------------------------------------- 1 | #navbar { 2 | background:-webkit-linear-gradient(top, #c3e779 0%, #a5d14a 100%); 3 | border:solid 1px #84a444; 4 | } 5 | h1 a span { 6 | color:#696969; 7 | } 8 | .media-price { 9 | color:#84a444; 10 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | #Mapicoin 2 | 3 | 4 | Pour lancer un serveur local vous pouvez utilisez docker. 5 | https://docs.docker.com/compose/overview/ 6 | 7 | Lancer la commande suivante pour lancer un serveur: 8 | 9 | # docker-compose up -d 10 | 11 | Testez: http://localhost:8080 12 | 13 | La Base de données MySQL est actuellement pas implémentée dans docker (@TODO) 14 | -------------------------------------------------------------------------------- /inc/parameters.sample.json: -------------------------------------------------------------------------------- 1 | { 2 | "mysql": { 3 | "host" : "localhost", 4 | "port" : 3306, 5 | "login" : "mapicoin", 6 | "password" : "pleasechangeme", 7 | "database" : "mapicoin", 8 | "charset" : "utf8" 9 | }, 10 | 11 | "api" : { 12 | "google_server_key": "", 13 | "google_browser_key": "" 14 | } 15 | } -------------------------------------------------------------------------------- /webroot/inc/page-footer.inc.php: -------------------------------------------------------------------------------- 1 | 11 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '2' 2 | 3 | services: 4 | db: 5 | image: mysql:latest 6 | env_file: .env 7 | pma: 8 | image: phpmyadmin/phpmyadmin 9 | env_file: .env 10 | ports: 11 | - "1987:80" 12 | links: 13 | - db:database 14 | web: 15 | build: . 16 | depends_on: 17 | - db 18 | ports: 19 | - "8080:80" 20 | volumes: 21 | - ./docker/vhost.conf:/etc/apache2/sites-enabled/mapicoin.conf 22 | - .:/var/www/html 23 | -------------------------------------------------------------------------------- /webroot/inc/ga.inc.php: -------------------------------------------------------------------------------- 1 | 2 | 11 | -------------------------------------------------------------------------------- /webroot/js/npm.js: -------------------------------------------------------------------------------- 1 | // This file is autogenerated via the `commonjs` Grunt task. You can require() this file in a CommonJS environment. 2 | require('../../js/transition.js') 3 | require('../../js/alert.js') 4 | require('../../js/button.js') 5 | require('../../js/carousel.js') 6 | require('../../js/collapse.js') 7 | require('../../js/dropdown.js') 8 | require('../../js/modal.js') 9 | require('../../js/tooltip.js') 10 | require('../../js/popover.js') 11 | require('../../js/scrollspy.js') 12 | require('../../js/tab.js') 13 | require('../../js/affix.js') -------------------------------------------------------------------------------- /webroot/inc/header.inc.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /inc/crawlers/GumtreeCrawler.class.php: -------------------------------------------------------------------------------- 1 | validateURL($url)) { 12 | return false; 13 | } 14 | // $url = replace_get_parameter($url, 'o', self::$_PAGE_PATTERN); 15 | return parent::__construct($url); 16 | } 17 | 18 | protected function validateURL($url) { 19 | // if (preg_match('#^https?://www\.leboncoin\.fr/.+#i', $url)) { 20 | // return true; 21 | // } 22 | return false; 23 | } 24 | 25 | /** 26 | * Return ad info from DOMElement (using xpath) 27 | * @return array 28 | */ 29 | public function getAdInfo(DOMElement $domElement) { 30 | $return = [ 31 | 'url' => null, 32 | 'title' => null, 33 | 'picture' => null, 34 | 'picture_count' => null, 35 | 'location' => null, 36 | 'price' => null, 37 | 'date' => null, 38 | 'pro' => null, 39 | ]; 40 | return $return; 41 | } 42 | 43 | /** 44 | * Return DOMElements of all ads based on a xpath 45 | * @return array(DOMElement, ...) 46 | */ 47 | public function getAds() { 48 | return false; 49 | } 50 | 51 | } -------------------------------------------------------------------------------- /inc/crawlers/CraigslistCrawler.class.php: -------------------------------------------------------------------------------- 1 | validateURL($url)) { 12 | return false; 13 | } 14 | // $url = replace_get_parameter($url, 'o', self::$_PAGE_PATTERN); 15 | return parent::__construct($url); 16 | } 17 | 18 | protected function validateURL($url) { 19 | // if (preg_match('#^https?://www\.leboncoin\.fr/.+#i', $url)) { 20 | // return true; 21 | // } 22 | return false; 23 | } 24 | 25 | /** 26 | * Return ad info from DOMElement (using xpath) 27 | * @return array 28 | */ 29 | public function getAdInfo(DOMElement $domElement) { 30 | $return = [ 31 | 'url' => null, 32 | 'title' => null, 33 | 'picture' => null, 34 | 'picture_count' => null, 35 | 'location' => null, 36 | 'price' => null, 37 | 'date' => null, 38 | 'pro' => null, 39 | ]; 40 | return $return; 41 | } 42 | 43 | /** 44 | * Return DOMElements of all ads based on a xpath 45 | * @return array(DOMElement, ...) 46 | */ 47 | public function getAds() { 48 | return false; 49 | } 50 | 51 | } -------------------------------------------------------------------------------- /webroot/inc/navbar.inc.php: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /webroot/css/ribbon.min.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * "Fork me on GitHub" CSS ribbon v0.2.0 | MIT License 3 | * https://github.com/simonwhitaker/github-fork-ribbon-css 4 | */.github-fork-ribbon{width:12.1em;height:12.1em;position:absolute;overflow:hidden;top:0;right:0;z-index:9999;pointer-events:none;font-size:13px;text-decoration:none;text-indent:-999999px}.github-fork-ribbon.fixed{position:fixed}.github-fork-ribbon:before,.github-fork-ribbon:after{position:absolute;display:block;width:15.38em;height:2.25em;top:3.23em;right:-3.23em;-webkit-transform:rotate(45deg);-moz-transform:rotate(45deg);-ms-transform:rotate(45deg);-o-transform:rotate(45deg);transform:rotate(45deg)}.github-fork-ribbon:before{content:"";padding:.38em 0;background-color:#a00;background-image:-webkit-gradient(linear,left top,left bottom,from(rgba(0,0,0,0)),to(rgba(0,0,0,0.15)));background-image:-webkit-linear-gradient(top,rgba(0,0,0,0),rgba(0,0,0,0.15));background-image:-moz-linear-gradient(top,rgba(0,0,0,0),rgba(0,0,0,0.15));background-image:-ms-linear-gradient(top,rgba(0,0,0,0),rgba(0,0,0,0.15));background-image:-o-linear-gradient(top,rgba(0,0,0,0),rgba(0,0,0,0.15));background-image:linear-gradient(to bottom,rgba(0,0,0,0),rgba(0,0,0,0.15));-webkit-box-shadow:0 .15em .23em 0 rgba(0,0,0,0.5);-moz-box-shadow:0 .15em .23em 0 rgba(0,0,0,0.5);box-shadow:0 .15em .23em 0 rgba(0,0,0,0.5);pointer-events:auto}.github-fork-ribbon:after{content:attr(title);color:#fff;font:700 1em "Helvetica Neue",Helvetica,Arial,sans-serif;line-height:1.54em;text-decoration:none;text-shadow:0 -.08em rgba(0,0,0,0.5);text-align:center;text-indent:0;padding:.15em 0;margin:.15em 0;border-width:.08em 0;border-style:dotted;border-color:#fff;border-color:rgba(255,255,255,0.7)}.github-fork-ribbon.left-top,.github-fork-ribbon.left-bottom{right:auto;left:0}.github-fork-ribbon.left-bottom,.github-fork-ribbon.right-bottom{top:auto;bottom:0}.github-fork-ribbon.left-top:before,.github-fork-ribbon.left-top:after,.github-fork-ribbon.left-bottom:before,.github-fork-ribbon.left-bottom:after{right:auto;left:-3.23em}.github-fork-ribbon.left-bottom:before,.github-fork-ribbon.left-bottom:after,.github-fork-ribbon.right-bottom:before,.github-fork-ribbon.right-bottom:after{top:auto;bottom:3.23em}.github-fork-ribbon.left-top:before,.github-fork-ribbon.left-top:after,.github-fork-ribbon.right-bottom:before,.github-fork-ribbon.right-bottom:after{-webkit-transform:rotate(-45deg);-moz-transform:rotate(-45deg);-ms-transform:rotate(-45deg);-o-transform:rotate(-45deg);transform:rotate(-45deg)} -------------------------------------------------------------------------------- /webroot/stats.php: -------------------------------------------------------------------------------- 1 | 8 | 9 | 10 | 11 | <?= $title ?> - Mapicoin 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 | 44 | 45 |

Mapicoin :

46 | 47 |
48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 63 | 64 | 65 | 66 | 67 | 68 |
RechercheCompteurDernière recherche
61 | "> 62 |
69 |
70 | 71 |
72 | 73 | 74 | 75 |
76 | 77 | 78 | 79 | 80 | 81 | -------------------------------------------------------------------------------- /webroot/inc/left-panel.inc.php: -------------------------------------------------------------------------------- 1 | "10 km", 6 | "20" =>"20 km", 7 | "30" =>"30 km", 8 | "50" =>"50 km", 9 | "100" =>"100 km", 10 | "200" =>"200 km", 11 | "500" =>"500 km", 12 | ]; 13 | $_select_day = [ 14 | "1" => "1 jour", 15 | "3" => "3 jours", 16 | "5" => "5 jours", 17 | "7" => "1 semaine", 18 | "14" => "2 semaines", 19 | "30" => "1 mois", 20 | "60" => "2 mois", 21 | ]; 22 | ?> 23 | 24 | -------------------------------------------------------------------------------- /inc/config.inc.php: -------------------------------------------------------------------------------- 1 | [ 26 | 'country' => 'France', 27 | 'timezone' => 'Europe/Paris', 28 | 'map-center' => [47.351, 3.392], 29 | 'site-title' => "Mapicoin, votre recherche leboncoin.fr sur une carte", 30 | 'site-description' => "Visualiser les annonces d'une recherche leboncoin sur une carte ? C'est possible ! Grâce à Mapicoin, entrez votre lien de recherche et on s'occupe du reste !", 31 | // without .mapicoin.[fr|com] 32 | // default value anyway... 33 | 'mapicoin-domains' => [], 34 | 'currency' => [ 35 | 'symbol' => '€', 36 | 'position' => 'right' 37 | ] 38 | ], 39 | SITE_KIJIJI => [ 40 | 'country' => 'Canada', 41 | 'timezone' => 'Canada/Central', 42 | 'map-center' => [62.4, -96.80972222222222], 43 | 'site-title' => "Ads of Kijiji on a map | Mapicoin", 44 | 'site-description' => "View listings of Kijiji search on a map? It's possible ! With Mapicoin enter your search link and we will do the rest!", 45 | // without .mapicoin.[fr|com] 46 | 'mapicoin-domains' => [ 47 | 'kijiji', 48 | 'kijiji.dev', 49 | 'kijiji.dev2', 50 | ], 51 | 'currency' => [ 52 | 'symbol' => '$', 53 | 'position' => 'left' 54 | ] 55 | ], 56 | ]; 57 | 58 | 59 | // Dislay debug ? 60 | if (DEBUG) { 61 | ini_set('display_errors', 0); 62 | ini_set('display_startup_errors', 1); 63 | error_reporting(E_ALL); 64 | } 65 | 66 | 67 | // === 68 | // Parameters 69 | // === 70 | $parameters = __DIR__.'/parameters.json'; 71 | if (!file_exists($parameters)) { 72 | die("Couldn't find parameters.json"); 73 | } 74 | if (!($_CONFIG = json_decode(file_get_contents($parameters)))) { 75 | die("Couldn't read/decode parameters.json"); 76 | } 77 | $_MYSQLI = mysqli_connect( 78 | $_CONFIG->mysql->host, 79 | $_CONFIG->mysql->login, 80 | $_CONFIG->mysql->password, 81 | $_CONFIG->mysql->database, 82 | $_CONFIG->mysql->port 83 | ); 84 | $_MYSQLI->set_charset($_CONFIG->mysql->charset); 85 | // === 86 | // Requires 87 | // === 88 | require_once 'functions.inc.php'; 89 | 90 | $_SITE = detect_ads_site(); 91 | 92 | // Timezone 93 | date_default_timezone_set($_SITES[$_SITE]['timezone']); 94 | -------------------------------------------------------------------------------- /webroot/js/jquery.lazyload.min.js: -------------------------------------------------------------------------------- 1 | /*! Lazy Load 1.9.3 - MIT license - Copyright 2010-2013 Mika Tuupola */ 2 | !function(a,b,c,d){var e=a(b);a.fn.lazyload=function(f){function g(){var b=0;i.each(function(){var c=a(this);if(!j.skip_invisible||c.is(":visible"))if(a.abovethetop(this,j)||a.leftofbegin(this,j));else if(a.belowthefold(this,j)||a.rightoffold(this,j)){if(++b>j.failure_limit)return!1}else c.trigger("appear"),b=0})}var h,i=this,j={threshold:0,failure_limit:0,event:"scroll",effect:"show",container:b,data_attribute:"original",skip_invisible:!0,appear:null,load:null,placeholder:"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsQAAA7EAZUrDhsAAAANSURBVBhXYzh8+PB/AAffA0nNPuCLAAAAAElFTkSuQmCC"};return f&&(d!==f.failurelimit&&(f.failure_limit=f.failurelimit,delete f.failurelimit),d!==f.effectspeed&&(f.effect_speed=f.effectspeed,delete f.effectspeed),a.extend(j,f)),h=j.container===d||j.container===b?e:a(j.container),0===j.event.indexOf("scroll")&&h.bind(j.event,function(){return g()}),this.each(function(){var b=this,c=a(b);b.loaded=!1,(c.attr("src")===d||c.attr("src")===!1)&&c.is("img")&&c.attr("src",j.placeholder),c.one("appear",function(){if(!this.loaded){if(j.appear){var d=i.length;j.appear.call(b,d,j)}a("").bind("load",function(){var d=c.attr("data-"+j.data_attribute);c.hide(),c.is("img")?c.attr("src",d):c.css("background-image","url('"+d+"')"),c[j.effect](j.effect_speed),b.loaded=!0;var e=a.grep(i,function(a){return!a.loaded});if(i=a(e),j.load){var f=i.length;j.load.call(b,f,j)}}).attr("src",c.attr("data-"+j.data_attribute))}}),0!==j.event.indexOf("scroll")&&c.bind(j.event,function(){b.loaded||c.trigger("appear")})}),e.bind("resize",function(){g()}),/(?:iphone|ipod|ipad).*os 5/gi.test(navigator.appVersion)&&e.bind("pageshow",function(b){b.originalEvent&&b.originalEvent.persisted&&i.each(function(){a(this).trigger("appear")})}),a(c).ready(function(){g()}),this},a.belowthefold=function(c,f){var g;return g=f.container===d||f.container===b?(b.innerHeight?b.innerHeight:e.height())+e.scrollTop():a(f.container).offset().top+a(f.container).height(),g<=a(c).offset().top-f.threshold},a.rightoffold=function(c,f){var g;return g=f.container===d||f.container===b?e.width()+e.scrollLeft():a(f.container).offset().left+a(f.container).width(),g<=a(c).offset().left-f.threshold},a.abovethetop=function(c,f){var g;return g=f.container===d||f.container===b?e.scrollTop():a(f.container).offset().top,g>=a(c).offset().top+f.threshold+a(c).height()},a.leftofbegin=function(c,f){var g;return g=f.container===d||f.container===b?e.scrollLeft():a(f.container).offset().left,g>=a(c).offset().left+f.threshold+a(c).width()},a.inviewport=function(b,c){return!(a.rightoffold(b,c)||a.leftofbegin(b,c)||a.belowthefold(b,c)||a.abovethetop(b,c))},a.extend(a.expr[":"],{"below-the-fold":function(b){return a.belowthefold(b,{threshold:0})},"above-the-top":function(b){return!a.belowthefold(b,{threshold:0})},"right-of-screen":function(b){return a.rightoffold(b,{threshold:0})},"left-of-screen":function(b){return!a.rightoffold(b,{threshold:0})},"in-viewport":function(b){return a.inviewport(b,{threshold:0})},"above-the-fold":function(b){return!a.belowthefold(b,{threshold:0})},"right-of-fold":function(b){return a.rightoffold(b,{threshold:0})},"left-of-fold":function(b){return!a.rightoffold(b,{threshold:0})}})}(jQuery,window,document); -------------------------------------------------------------------------------- /webroot/inc/modal.inc.php: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /inc/nginx-vhost.conf: -------------------------------------------------------------------------------- 1 | server { 2 | listen 80; 3 | server_name mapicoin.fr www.mapicoin.fr mapicoin.com www.mapicoin.com; 4 | return 301 https://mapicoin.fr$request_uri; 5 | } 6 | server { 7 | listen 443 ssl http2; 8 | server_name www.mapicoin.fr mapicoin.com www.mapicoin.com; 9 | return 301 https://mapicoin.fr$request_uri; 10 | ssl_certificate /etc/letsencrypt/live/mapicoin.fr/fullchain.pem; 11 | ssl_certificate_key /etc/letsencrypt/live/mapicoin.fr/privkey.pem; 12 | ssl_dhparam /etc/ssl/private/dhparams.pem; 13 | 14 | ssl_protocols TLSv1 TLSv1.1 TLSv1.2; 15 | ssl_ciphers 'ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:ECDHE-RSA-DES-CBC3-SHA:ECDHE-ECDSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:AES:CAMELLIA:DES-CBC3-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA'; 16 | ssl_prefer_server_ciphers on; 17 | ssl_session_cache shared:SSL:10m; 18 | 19 | add_header Strict-Transport-Security max-age=15768000; 20 | ssl_stapling on; 21 | ssl_stapling_verify on; 22 | } 23 | server { 24 | listen 443 ssl http2; 25 | server_name mapicoin.fr; 26 | 27 | root /var/www/mapicoin; 28 | index index.php index.html; 29 | 30 | # logs 31 | error_log /var/log/nginx/mapicoin-error.log; 32 | access_log /var/log/nginx/mapicoin-access.log; 33 | 34 | ssl_certificate /etc/letsencrypt/live/mapicoin.fr/fullchain.pem; 35 | ssl_certificate_key /etc/letsencrypt/live/mapicoin.fr/privkey.pem; 36 | ssl_dhparam /etc/ssl/private/dhparams.pem; 37 | 38 | ssl_protocols TLSv1 TLSv1.1 TLSv1.2; 39 | ssl_ciphers 'ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:ECDHE-RSA-DES-CBC3-SHA:ECDHE-ECDSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:AES:CAMELLIA:DES-CBC3-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA'; 40 | ssl_prefer_server_ciphers on; 41 | ssl_session_cache shared:SSL:10m; 42 | 43 | add_header Strict-Transport-Security max-age=15768000; 44 | ssl_stapling on; 45 | ssl_stapling_verify on; 46 | 47 | location ~* \.(js|jpg|png|css)$ { 48 | root /var/www/mapicoin/; 49 | expires 30d; 50 | } 51 | 52 | location ~ \.php$ { 53 | try_files $uri =404; 54 | fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; 55 | include fastcgi_params; 56 | fastcgi_pass unix:/run/php/php7.0-fpm.sock; 57 | } 58 | 59 | location ~ /\. { 60 | deny all; 61 | } 62 | } -------------------------------------------------------------------------------- /inc/crawlers/Crawler.class.php: -------------------------------------------------------------------------------- 1 | url = $url; 18 | } 19 | 20 | /** 21 | * Validate an URL 22 | * @return boolean 23 | */ 24 | abstract protected function validateURL($url); 25 | /** 26 | * Return ad info from DOMElement (using xpath) 27 | * @return array 28 | */ 29 | abstract protected function getAdInfo(DOMElement $domElement); 30 | /** 31 | * Return DOMElements of all ads based on a xpath 32 | * @return array(DOMElement, ...) 33 | */ 34 | abstract protected function getAds(); 35 | /** 36 | * Return boolean is its a single-ad page 37 | * THIS IS FOR SINGLE AD PAGE 38 | * @return array(DOMElement, ...) 39 | */ 40 | abstract public function isSingleAdPage($url); 41 | /** 42 | * Return ad info from DOMElement (using xpath) 43 | * THIS IS FOR SINGLE AD PAGE 44 | * @return array 45 | */ 46 | abstract protected function getSingleAdInfo(); 47 | 48 | 49 | /** 50 | * Return content of tag 51 | */ 52 | public function fetchMainTitle() { 53 | if (!$this->domXpath) { 54 | return false; 55 | } 56 | return $this->domXpath->query( 57 | '//head/title/text()' 58 | )->item(0)->nodeValue; 59 | } 60 | 61 | /** 62 | * Return paginated URL depending on replace 63 | */ 64 | private function getPaginatedUrl($page = 1) { 65 | return str_replace(self::$_PAGE_PATTERN, $page, $this->url); 66 | } 67 | 68 | /** 69 | * Return HTML content of a given URL 70 | */ 71 | public function fetchURLContent($page = null) { 72 | if ($page !== null) { 73 | $url = $this->getPaginatedUrl($page); 74 | } else { 75 | $url = $this->url; 76 | } 77 | $headers = array( 78 | 'User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.2.12) Gecko/20101026 Firefox/3.6.12', 79 | 'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8', 80 | 'Accept-Language: en-us,en;q=0.5', 81 | //'Accept-Encoding: gzip,deflate', 82 | 'Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7', 83 | 'Keep-Alive: 115', 84 | 'Connection: keep-alive', 85 | ); 86 | 87 | $ch = curl_init(); 88 | curl_setopt($ch, CURLOPT_VERBOSE, false); 89 | curl_setopt($ch, CURLOPT_URL, $url); 90 | curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true); 91 | curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); 92 | curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 5); 93 | curl_setopt($ch, CURLOPT_HTTPHEADER, $headers); 94 | curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); 95 | 96 | $html = curl_exec($ch); 97 | 98 | // Conversion 99 | $html = iconv('UTF-8', 'UTF-8//IGNORE', utf8_encode($html)); 100 | 101 | $code = curl_getinfo($ch, CURLINFO_HTTP_CODE); 102 | curl_close($ch); 103 | 104 | if ($code != 200) { 105 | return false; 106 | } 107 | 108 | $dom = new DOMDocument(); 109 | $dom->loadHTML($html); 110 | $this->domXpath = new DomXPath($dom); 111 | 112 | return true; 113 | } 114 | 115 | } -------------------------------------------------------------------------------- /webroot/index.php: -------------------------------------------------------------------------------- 1 | <?php 2 | require_once '../inc/config.inc.php'; 3 | $center = $_SITES[$_SITE]['map-center'] ?? $_SITES[SITE_LEBONCOIN]['map-center']; 4 | $title = $_SITES[$_SITE]['site-title'] ?? $_SITES[SITE_LEBONCOIN]['site-title']; 5 | $description = $_SITES[$_SITE]['site-description'] ?? $_SITES[SITE_LEBONCOIN]['site-description']; 6 | ?> 7 | <!DOCTYPE html> 8 | <html lang="fr"> 9 | 10 | <head> 11 | 12 | <title><?= $title ?? 'Mapicoin' ?> 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 | 46 | 69 |
70 |
71 |
72 | 73 | 79 | 80 | 81 | 82 | 83 | 84 | -------------------------------------------------------------------------------- /webroot/js/geolocation-marker.min.js: -------------------------------------------------------------------------------- 1 | (function(){/* 2 | geolocation-marker version 2.0.4 3 | @copyright 2012, 2015 Chad Killingsworth 4 | @see https://github.com/ChadKillingsworth/geolocation-marker/blob/master/LICENSE.txt 5 | */ 6 | 'use strict';var b,d=this; 7 | function g(a,c,e){google.maps.MVCObject.call(this);this.a=this.b=null;this.g=-1;var f={clickable:!1,cursor:"pointer",draggable:!1,flat:!0,icon:{url:"https://chadkillingsworth.github.io/geolocation-marker/images/gpsloc.png",size:new google.maps.Size(34,34),scaledSize:new google.maps.Size(17,17),origin:new google.maps.Point(0,0),anchor:new google.maps.Point(8,8)},optimized:!1,position:new google.maps.LatLng(0,0),title:"Current location",zIndex:2};c&&(f=h(f,c));c={clickable:!1,radius:0,strokeColor:"1bb6ff", 8 | strokeOpacity:.4,fillColor:"61a0bf",fillOpacity:.4,strokeWeight:1,zIndex:1};e&&(c=h(c,e));this.b=new google.maps.Marker(f);this.a=new google.maps.Circle(c);google.maps.MVCObject.prototype.set.call(this,"accuracy",null);google.maps.MVCObject.prototype.set.call(this,"position",null);google.maps.MVCObject.prototype.set.call(this,"map",null);this.set("minimum_accuracy",null);this.set("position_options",{enableHighAccuracy:!0,maximumAge:1E3});this.a.bindTo("map",this.b);a&&this.f(a)} 9 | (function(){var a=google.maps.MVCObject;function c(){}c.prototype=a.prototype;g.prototype=new c;g.prototype.constructor=g;for(var e in a)if(d.Object.defineProperties){var f=d.Object.getOwnPropertyDescriptor(a,e);void 0!==f&&d.Object.defineProperty(g,e,f)}else g[e]=a[e]})();b=g.prototype;b.set=function(a,c){if(k.test(a))throw"'"+a+"' is a read-only property.";"map"===a?this.f(c):google.maps.MVCObject.prototype.set.call(this,a,c)};b.i=function(){return this.get("map")};b.l=function(){return this.get("position_options")}; 10 | b.w=function(a){this.set("position_options",a)};b.c=function(){return this.get("position")};b.m=function(){return this.get("position")?this.a.getBounds():null};b.j=function(){return this.get("accuracy")};b.h=function(){return this.get("minimum_accuracy")};b.v=function(a){this.set("minimum_accuracy",a)}; 11 | b.f=function(a){google.maps.MVCObject.prototype.set.call(this,"map",a);a?navigator.geolocation&&(this.g=navigator.geolocation.watchPosition(this.A.bind(this),this.o.bind(this),this.l())):(this.b.unbind("position"),this.a.unbind("center"),this.a.unbind("radius"),google.maps.MVCObject.prototype.set.call(this,"accuracy",null),google.maps.MVCObject.prototype.set.call(this,"position",null),navigator.geolocation.clearWatch(this.g),this.g=-1,this.b.setMap(a))};b.u=function(a){this.b.setOptions(h({},a))}; 12 | b.s=function(a){this.a.setOptions(h({},a))}; 13 | b.A=function(a){var c=new google.maps.LatLng(a.coords.latitude,a.coords.longitude),e=null==this.b.getMap();if(e){if(null!=this.h()&&a.coords.accuracy>this.h())return;this.b.setMap(this.i());this.b.bindTo("position",this);this.a.bindTo("center",this,"position");this.a.bindTo("radius",this,"accuracy")}this.j()!=a.coords.accuracy&&google.maps.MVCObject.prototype.set.call(this,"accuracy",a.coords.accuracy);!e&&null!=this.c()&&this.c().equals(c)||google.maps.MVCObject.prototype.set.call(this,"position", 14 | c)};b.o=function(a){google.maps.event.trigger(this,"geolocation_error",a)};function h(a,c){for(var e in c)!0!==l[e]&&(a[e]=c[e]);return a}var l={map:!0,position:!0,radius:!0},k=/^(?:position|accuracy)$/i;function m(){g.prototype.getAccuracy=g.prototype.j;g.prototype.getBounds=g.prototype.m;g.prototype.getMap=g.prototype.i;g.prototype.getMinimumAccuracy=g.prototype.h;g.prototype.getPosition=g.prototype.c;g.prototype.getPositionOptions=g.prototype.l;g.prototype.setCircleOptions=g.prototype.s;g.prototype.setMap=g.prototype.f;g.prototype.setMarkerOptions=g.prototype.u;g.prototype.setMinimumAccuracy=g.prototype.v;g.prototype.setPositionOptions=g.prototype.w;return g} 15 | "function"===typeof this.define&&this.define.amd?this.define([],m):"object"===typeof this.exports?this.module.exports=m():this.GeolocationMarker=m();}).call(this) 16 | //# sourceMappingURL=geolocation-marker.js.map 17 | -------------------------------------------------------------------------------- /inc/crawlers/KijijiCrawler.class.php: -------------------------------------------------------------------------------- 1 | validateURL($url)) { 12 | return false; 13 | } 14 | $url = replace_get_parameter($url, 'gpTopAds', 'y'); 15 | $url = $this->replacePageParameterWithPattern($url); 16 | return parent::__construct($url); 17 | } 18 | 19 | private function replacePageParameterWithPattern($url) { 20 | // http://www.kijiji.ca/b-cars-vehicles/fredericton/page-2/c27l1700018?gpTopAds=y 21 | if (preg_match('#/page-\d+/#i', $url)) { 22 | return preg_replace('#/page-\d+/#i', '/page-'.self::$_PAGE_PATTERN.'/', $url); 23 | } 24 | // http://www.kijiji.ca/b-cars-vehicles/fredericton/c27l1700018?gpTopAds=y 25 | $pos = strrpos($url, '/'); 26 | $url = substr($url, 0, $pos) 27 | . '/page-'.self::$_PAGE_PATTERN.'/' 28 | . substr($url, $pos +1); 29 | return $url; 30 | } 31 | 32 | protected function validateURL($url) { 33 | if (preg_match('#^https?://www\.kijiji\.ca/.+#i', $url)) { 34 | return true; 35 | } 36 | return false; 37 | } 38 | 39 | /** 40 | * Return ad info from DOMElement (using xpath) 41 | * @return array 42 | */ 43 | public function getAdInfo(DOMElement $domElement) { 44 | $return = [ 45 | 'url' => null, 46 | 'title' => null, 47 | 'picture' => null, 48 | 'picture_count' => null, 49 | 'location' => null, 50 | 'price' => null, 51 | 'date' => null, 52 | 'pro' => null, 53 | // Added later, see webroot/get-ads.php 54 | 'latlng' => null, 55 | ]; 56 | // url 57 | $tmp = $this->domXpath->query( 58 | './/div[@class="title"]/a/@href', 59 | $domElement 60 | ); 61 | $return['url'] = self::$_baseDomain.$tmp->item(0)->nodeValue; 62 | 63 | // title 64 | $tmp = $this->domXpath->query( 65 | './/div[@class="title"]/a/text()', 66 | $domElement 67 | ); 68 | $return['title'] = trim($tmp->item(0)->nodeValue); 69 | 70 | // picture 71 | $tmp = $this->domXpath->query( 72 | './/div[@class="left-col"]/div[@class="image"]/img/@src', 73 | $domElement 74 | ); 75 | $tmp = $tmp->item(0)->nodeValue; 76 | $return['picture'] = str_replace('http://', 'https://', $tmp); 77 | 78 | // picture_count 79 | $return['picture_count'] = null; 80 | 81 | // // pro 82 | // $tmp = $this->domXpath->query( 83 | // './/span[@class="ispro"]/text()', 84 | // $domElement 85 | // ); 86 | // $tmp = trim(@$tmp->item(0)->nodeValue ?? null); 87 | // $return['pro'] = preg_replace('#\s+#i', ' ', $tmp); 88 | 89 | // // location 90 | $tmp = $this->domXpath->query( 91 | './/div[@class="location"]/text()', 92 | $domElement 93 | ); 94 | $tmp = trim($tmp->item(0)->nodeValue); 95 | $return['location'] = $tmp; 96 | 97 | // price 98 | $tmp = $this->domXpath->query( 99 | './/div[@class="price"]/text()', 100 | $domElement 101 | ); 102 | $tmp = trim(@$tmp->item(0)->nodeValue ?? ''); 103 | $tmp = preg_replace('#\s+#i', ' ', $tmp); 104 | $return['price'] = $tmp != ' ' ? $tmp : ''; 105 | 106 | // date 107 | $tmp = $this->domXpath->query( 108 | './/span[@class="date-posted"]/text()', 109 | $domElement 110 | ); 111 | // var_dump($tmp->item(0)); 112 | // die; 113 | $return['date'] = trim($tmp->item(0)->nodeValue); 114 | $return['timestamp'] = $this->convertDateToTimestamp($return['date']); 115 | if ($return['date'] == '') { 116 | $return['date'] = 'Today'; 117 | } 118 | 119 | return $return; 120 | } 121 | 122 | /** 123 | * Return DOMElements of all ads based on a xpath 124 | * @return array(DOMElement, ...) 125 | */ 126 | public function getAds() { 127 | return $this->domXpath->query( 128 | '//div[@class="search-item regular-ad"]' 129 | ); 130 | } 131 | 132 | private function convertDateToTimestamp($date) { 133 | $tmp = explode('/', $date); 134 | if ($date == '' || strstr($date, ' ago') !== false || !isset($tmp[2])) { 135 | return time(); 136 | } 137 | return mktime(1, 1, 1, $tmp[1], $tmp[0], $tmp[2]); 138 | } 139 | 140 | } -------------------------------------------------------------------------------- /webroot/get-ads.php: -------------------------------------------------------------------------------- 1 | false, 49 | 'message' => 'ko', 50 | 'title' => "Liste des résultats", 51 | 'datas' => null, 52 | 'is_single' => false, 53 | ]; 54 | 55 | if (!$crawler) { 56 | $return['message'] = sprintf( 57 | "L'URL %s ne semble pas correspondre à une URL de liste de résultats du site d'annonces %s", 58 | $_URL, 59 | ucfirst($_SITE) 60 | ); 61 | exit(json_encode($return)); 62 | } 63 | 64 | // === 65 | // Records search (for stats) 66 | // === 67 | stats_add_search($_URL); 68 | 69 | 70 | 71 | // === 72 | // Let's single ad ! 73 | // === 74 | if ($crawler->isSingleAdPage($_URL)) { 75 | 76 | $return['is_single'] = true; 77 | 78 | $places = []; 79 | 80 | $crawler->fetchURLContent(null); 81 | // Re-try once if fail to fetch 82 | if (!$crawler) { 83 | sleep(1); 84 | $crawler->fetchURLContent(null); 85 | } 86 | 87 | // Fetch main title once 88 | $return['title'] = $crawler->fetchMainTitle(); 89 | 90 | $annonce = $crawler->getSingleAdInfo(); 91 | $datas[1] = $annonce; 92 | if ($annonce['location']) { 93 | $places[1] = $annonce['location']; 94 | } 95 | 96 | // === 97 | // Fetch lat & lng 98 | // === 99 | $latlng = convert_places_to_latlng($places); 100 | 101 | if (!$latlng) { 102 | $return['message'] = "Impossible de récupérer les coordonnées GPS des annonces."; 103 | exit(json_encode($return)); 104 | } 105 | foreach ($latlng as $k => $ll) { 106 | $datas[$k]['latlng'] = $ll; 107 | } 108 | 109 | } else { 110 | 111 | // === 112 | // Let's browse pages ! 113 | // === 114 | $datas = []; 115 | $title = null; 116 | for ($i = 1; $i <= MAX_PAGES_RETRIEVE; ++$i) { 117 | 118 | $places = []; 119 | 120 | $crawler->fetchURLContent($i); 121 | // Re-try once if fail to fetch 122 | if (!$crawler) { 123 | sleep(1); 124 | $crawler->fetchURLContent($i); 125 | } 126 | 127 | // Fetch main title once 128 | if (!$title) { 129 | $return['title'] = $crawler->fetchMainTitle(); 130 | } 131 | 132 | $annonces = $crawler->getAds(); 133 | 134 | if (!$annonces || $annonces->length == 0) { 135 | break; 136 | } 137 | foreach ($annonces as $j => $e) { 138 | // Key is very important as we are mixing result pages ($i, $j) 139 | $key = ($j + 1) * $i; 140 | $annonce = $crawler->getAdInfo($e); 141 | var_dump($annonce); 142 | 143 | $datas[$key] = $annonce; 144 | if ($annonce['location']) { 145 | $places[$key] = $annonce['location']; 146 | } 147 | } 148 | 149 | // === 150 | // Fetch lat & lng 151 | // === 152 | $latlng = convert_places_to_latlng($places); 153 | 154 | if (!$latlng) { 155 | $return['message'] = "Impossible de récupérer les coordonnées GPS des annonces."; 156 | exit(json_encode($return)); 157 | } 158 | foreach ($latlng as $k => $ll) { 159 | $datas[$k]['latlng'] = $ll; 160 | } 161 | 162 | } 163 | 164 | } 165 | 166 | // === 167 | // Get avergage price 168 | // === 169 | $return['average_price'] = get_average_price($datas); 170 | $return['currency'] = $_SITES[$_SITE]['currency']; 171 | 172 | 173 | 174 | 175 | // === 176 | // /END of script 177 | // === 178 | $return['status'] = true; 179 | $return['message'] = 'ok'; 180 | $return['datas'] = $datas; 181 | 182 | ob_clean(); 183 | header('Content-Type: application/json; charset=utf-8'); 184 | 185 | if (!($encode = json_encode($return))) { 186 | exit(json_encode([ 187 | 'status' => false, 188 | 'message' => json_last_error_msg(), 189 | ])); 190 | } 191 | 192 | exit($encode); 193 | 194 | -------------------------------------------------------------------------------- /webroot/css/bootstrap-theme.min.css.map: -------------------------------------------------------------------------------- 1 | {"version":3,"sources":["less/theme.less","less/mixins/vendor-prefixes.less","less/mixins/gradients.less","less/mixins/reset-filter.less"],"names":[],"mappings":";;;;AAmBA,YAAA,aAAA,UAAA,aAAA,aAAA,aAME,YAAA,EAAA,KAAA,EAAA,eC2CA,mBAAA,MAAA,EAAA,IAAA,EAAA,sBAAA,EAAA,IAAA,IAAA,iBACQ,WAAA,MAAA,EAAA,IAAA,EAAA,sBAAA,EAAA,IAAA,IAAA,iBDvCR,mBAAA,mBAAA,oBAAA,oBAAA,iBAAA,iBAAA,oBAAA,oBAAA,oBAAA,oBAAA,oBAAA,oBCsCA,mBAAA,MAAA,EAAA,IAAA,IAAA,iBACQ,WAAA,MAAA,EAAA,IAAA,IAAA,iBDlCR,qBAAA,sBAAA,sBAAA,uBAAA,mBAAA,oBAAA,sBAAA,uBAAA,sBAAA,uBAAA,sBAAA,uBAAA,+BAAA,gCAAA,6BAAA,gCAAA,gCAAA,gCCiCA,mBAAA,KACQ,WAAA,KDlDV,mBAAA,oBAAA,iBAAA,oBAAA,oBAAA,oBAuBI,YAAA,KAyCF,YAAA,YAEE,iBAAA,KAKJ,aErEI,YAAA,EAAA,IAAA,EAAA,KACA,iBAAA,iDACA,iBAAA,4CAAA,iBAAA,qEAEA,iBAAA,+CCnBF,OAAA,+GH4CA,OAAA,0DACA,kBAAA,SAuC2C,aAAA,QAA2B,aAAA,KArCtE,mBAAA,mBAEE,iBAAA,QACA,oBAAA,EAAA,MAGF,oBAAA,oBAEE,iBAAA,QACA,aAAA,QAMA,sBAAA,6BAAA,4BAAA,6BAAA,4BAAA,4BAAA,uBAAA,8BAAA,6BAAA,8BAAA,6BAAA,6BAAA,gCAAA,uCAAA,sCAAA,uCAAA,sCAAA,sCAME,iBAAA,QACA,iBAAA,KAgBN,aEtEI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDAEA,OAAA,+GCnBF,OAAA,0DH4CA,kBAAA,SACA,aAAA,QAEA,mBAAA,mBAEE,iBAAA,QACA,oBAAA,EAAA,MAGF,oBAAA,oBAEE,iBAAA,QACA,aAAA,QAMA,sBAAA,6BAAA,4BAAA,6BAAA,4BAAA,4BAAA,uBAAA,8BAAA,6BAAA,8BAAA,6BAAA,6BAAA,gCAAA,uCAAA,sCAAA,uCAAA,sCAAA,sCAME,iBAAA,QACA,iBAAA,KAiBN,aEvEI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDAEA,OAAA,+GCnBF,OAAA,0DH4CA,kBAAA,SACA,aAAA,QAEA,mBAAA,mBAEE,iBAAA,QACA,oBAAA,EAAA,MAGF,oBAAA,oBAEE,iBAAA,QACA,aAAA,QAMA,sBAAA,6BAAA,4BAAA,6BAAA,4BAAA,4BAAA,uBAAA,8BAAA,6BAAA,8BAAA,6BAAA,6BAAA,gCAAA,uCAAA,sCAAA,uCAAA,sCAAA,sCAME,iBAAA,QACA,iBAAA,KAkBN,UExEI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDAEA,OAAA,+GCnBF,OAAA,0DH4CA,kBAAA,SACA,aAAA,QAEA,gBAAA,gBAEE,iBAAA,QACA,oBAAA,EAAA,MAGF,iBAAA,iBAEE,iBAAA,QACA,aAAA,QAMA,mBAAA,0BAAA,yBAAA,0BAAA,yBAAA,yBAAA,oBAAA,2BAAA,0BAAA,2BAAA,0BAAA,0BAAA,6BAAA,oCAAA,mCAAA,oCAAA,mCAAA,mCAME,iBAAA,QACA,iBAAA,KAmBN,aEzEI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDAEA,OAAA,+GCnBF,OAAA,0DH4CA,kBAAA,SACA,aAAA,QAEA,mBAAA,mBAEE,iBAAA,QACA,oBAAA,EAAA,MAGF,oBAAA,oBAEE,iBAAA,QACA,aAAA,QAMA,sBAAA,6BAAA,4BAAA,6BAAA,4BAAA,4BAAA,uBAAA,8BAAA,6BAAA,8BAAA,6BAAA,6BAAA,gCAAA,uCAAA,sCAAA,uCAAA,sCAAA,sCAME,iBAAA,QACA,iBAAA,KAoBN,YE1EI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDAEA,OAAA,+GCnBF,OAAA,0DH4CA,kBAAA,SACA,aAAA,QAEA,kBAAA,kBAEE,iBAAA,QACA,oBAAA,EAAA,MAGF,mBAAA,mBAEE,iBAAA,QACA,aAAA,QAMA,qBAAA,4BAAA,2BAAA,4BAAA,2BAAA,2BAAA,sBAAA,6BAAA,4BAAA,6BAAA,4BAAA,4BAAA,+BAAA,sCAAA,qCAAA,sCAAA,qCAAA,qCAME,iBAAA,QACA,iBAAA,KA2BN,eAAA,WClCE,mBAAA,EAAA,IAAA,IAAA,iBACQ,WAAA,EAAA,IAAA,IAAA,iBD2CV,0BAAA,0BE3FI,iBAAA,QACA,iBAAA,oDACA,iBAAA,+CAAA,iBAAA,wEACA,iBAAA,kDACA,OAAA,+GF0FF,kBAAA,SAEF,yBAAA,+BAAA,+BEhGI,iBAAA,QACA,iBAAA,oDACA,iBAAA,+CAAA,iBAAA,wEACA,iBAAA,kDACA,OAAA,+GFgGF,kBAAA,SASF,gBE7GI,iBAAA,iDACA,iBAAA,4CACA,iBAAA,qEAAA,iBAAA,+CACA,OAAA,+GACA,OAAA,0DCnBF,kBAAA,SH+HA,cAAA,ICjEA,mBAAA,MAAA,EAAA,IAAA,EAAA,sBAAA,EAAA,IAAA,IAAA,iBACQ,WAAA,MAAA,EAAA,IAAA,EAAA,sBAAA,EAAA,IAAA,IAAA,iBD6DV,sCAAA,oCE7GI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SD2CF,mBAAA,MAAA,EAAA,IAAA,IAAA,iBACQ,WAAA,MAAA,EAAA,IAAA,IAAA,iBD0EV,cAAA,iBAEE,YAAA,EAAA,IAAA,EAAA,sBAIF,gBEhII,iBAAA,iDACA,iBAAA,4CACA,iBAAA,qEAAA,iBAAA,+CACA,OAAA,+GACA,OAAA,0DCnBF,kBAAA,SHkJA,cAAA,IAHF,sCAAA,oCEhII,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SD2CF,mBAAA,MAAA,EAAA,IAAA,IAAA,gBACQ,WAAA,MAAA,EAAA,IAAA,IAAA,gBDgFV,8BAAA,iCAYI,YAAA,EAAA,KAAA,EAAA,gBAKJ,qBAAA,kBAAA,mBAGE,cAAA,EAqBF,yBAfI,mDAAA,yDAAA,yDAGE,MAAA,KE7JF,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,UFqKJ,OACE,YAAA,EAAA,IAAA,EAAA,qBC3HA,mBAAA,MAAA,EAAA,IAAA,EAAA,sBAAA,EAAA,IAAA,IAAA,gBACQ,WAAA,MAAA,EAAA,IAAA,EAAA,sBAAA,EAAA,IAAA,IAAA,gBDsIV,eEtLI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SF8KF,aAAA,QAKF,YEvLI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SF8KF,aAAA,QAMF,eExLI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SF8KF,aAAA,QAOF,cEzLI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SF8KF,aAAA,QAeF,UEjMI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SFuMJ,cE3MI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SFwMJ,sBE5MI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SFyMJ,mBE7MI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SF0MJ,sBE9MI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SF2MJ,qBE/MI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SF+MJ,sBElLI,iBAAA,yKACA,iBAAA,oKACA,iBAAA,iKFyLJ,YACE,cAAA,IC9KA,mBAAA,EAAA,IAAA,IAAA,iBACQ,WAAA,EAAA,IAAA,IAAA,iBDgLV,wBAAA,8BAAA,8BAGE,YAAA,EAAA,KAAA,EAAA,QEnOE,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SFiOF,aAAA,QALF,+BAAA,qCAAA,qCAQI,YAAA,KAUJ,OCnME,mBAAA,EAAA,IAAA,IAAA,gBACQ,WAAA,EAAA,IAAA,IAAA,gBD4MV,8BE5PI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SFyPJ,8BE7PI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SF0PJ,8BE9PI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SF2PJ,2BE/PI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SF4PJ,8BEhQI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SF6PJ,6BEjQI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SFoQJ,MExQI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SFsQF,aAAA,QC3NA,mBAAA,MAAA,EAAA,IAAA,IAAA,gBAAA,EAAA,IAAA,EAAA,qBACQ,WAAA,MAAA,EAAA,IAAA,IAAA,gBAAA,EAAA,IAAA,EAAA"} -------------------------------------------------------------------------------- /webroot/js/mapicoin-tools.js: -------------------------------------------------------------------------------- 1 | function set_cookie(cname, cvalue, exdays) { 2 | var d = new Date(); 3 | d.setTime(d.getTime() + (exdays*24*60*60*1000)); 4 | var expires = "expires="+ d.toUTCString(); 5 | document.cookie = cname + "=" + cvalue + "; " + expires; 6 | } 7 | 8 | function get_cookie(cname) { 9 | var name = cname + "="; 10 | var ca = document.cookie.split(';'); 11 | for(var i = 0; i valeur (paramètres GET) 49 | * si reset = true, on repart sur une nouvelle URL 50 | * sinon, on prends les paramètres actuels et on surchage avec data 51 | */ 52 | function update_browser_url(data, reset) { 53 | // Get current GET parameters if reset = false 54 | var gets = []; 55 | if (reset == false) { 56 | location.search 57 | .substr(1) 58 | .split("&") 59 | .forEach(function (item) { 60 | tmp = item.split("="); 61 | var k = tmp.shift(); 62 | var v = tmp.join('='); 63 | gets[k] = v; 64 | } 65 | ); 66 | } 67 | // On surchage les paramètres GET 68 | for (var i in data) { 69 | gets[i] = data[i]; 70 | } 71 | // On reconstruit l'URL 72 | var url = ''; 73 | var ampersand = false; 74 | for (var i in gets) { 75 | if (gets[i] != '') { 76 | url += (ampersand ? '&' : '')+i+'='+gets[i]; 77 | ampersand = true; 78 | } 79 | } 80 | // On met à jour le navigateur 81 | window.history.pushState("", "", "./?"+url); 82 | } 83 | 84 | 85 | /** 86 | * Parse query strings (foo=bar&foo1=bar1) and return given parameter value 87 | */ 88 | function parse_query_strings(val) { 89 | var result = null, 90 | tmp = []; 91 | location.search 92 | .substr(1) 93 | .split("&") 94 | .forEach(function (item) { 95 | tmp = item.split("="); 96 | if (tmp[0] === val || typeof val == 'undefined') { 97 | tmp.shift(); 98 | result = tmp.join('='); 99 | } 100 | } 101 | ); 102 | return result; 103 | } 104 | 105 | 106 | /** 107 | * Detect mobile use 108 | */ 109 | function is_mobile() { 110 | if( navigator.userAgent.match(/Android/i) 111 | || navigator.userAgent.match(/webOS/i) 112 | || navigator.userAgent.match(/iPhone/i) 113 | || navigator.userAgent.match(/iPad/i) 114 | || navigator.userAgent.match(/iPod/i) 115 | || navigator.userAgent.match(/BlackBerry/i) 116 | || navigator.userAgent.match(/Windows Phone/i) 117 | ){ 118 | return true; 119 | } 120 | return false; 121 | } 122 | 123 | 124 | 125 | var QueryString = function () { 126 | // This function is anonymous, is executed immediately and 127 | // the return value is assigned to QueryString! 128 | var query_string = {}; 129 | var query = window.location.search.substring(1); 130 | var vars = query.split("&"); 131 | for (var i=0;i 3 ? j % 3 : 0; 163 | return s + (j ? i.substr(0, j) + t : "") + i.substr(j).replace(/(\d{3})(?=\d)/g, "$1" + t) + (c ? d + Math.abs(n - i).toFixed(c).slice(2) : ""); 164 | }; 165 | 166 | -------------------------------------------------------------------------------- /webroot/css/mapicoin-mobile.css: -------------------------------------------------------------------------------- 1 | @media only screen 2 | and (min-device-width : 1100px) { 3 | .hide-desktop { 4 | display:none !important; 5 | } 6 | } 7 | @media only screen 8 | and (max-device-width : 1100px) { 9 | #navbar #form-search { 10 | width:calc(100vw - 270px); 11 | position:relative; 12 | } 13 | #navbar #input-url { 14 | width:calc(100% - 65px); 15 | } 16 | #navbar #input-submit { 17 | width:60px; 18 | position:absolute; 19 | right:0px; 20 | top:9px; 21 | } 22 | .hide-tablet{ display:none !important; } 23 | } 24 | @media only screen 25 | and (max-device-width : 900px) { 26 | body { 27 | background:#ffe; 28 | } 29 | .hide-mobile { 30 | display:none !important; 31 | } 32 | #navbar #form-search { 33 | width:calc(100vw - 135px); 34 | } 35 | #container { 36 | width:640px; 37 | margin-left:-320px; 38 | } 39 | #input-submit, 40 | #input-url { 41 | width:100%; 42 | } 43 | } 44 | @media only screen 45 | and (max-device-width : 667px) { 46 | #container { 47 | position:static; 48 | top:auto; 49 | left:auto; 50 | right:auto; 51 | bottom:auto; 52 | margin:auto; 53 | display:block; 54 | box-shadow: none; 55 | border-radius: 0px; 56 | padding:30px 10px 10px; 57 | width:auto; 58 | } 59 | #map {display:none !important;} 60 | body.in-search #map {display:block !important;} 61 | img { 62 | max-width: 100%; 63 | height: auto; 64 | } 65 | footer { 66 | position:static; 67 | bottom:auto; 68 | left:auto; 69 | right:auto; 70 | width:100%; 71 | display:block; 72 | } 73 | body.in-search #map { 74 | margin:0px; 75 | height:calc(100vh - 56px); 76 | } 77 | body.in-search #sidebar-wrapper { 78 | height:calc(100vh - 56px); 79 | } 80 | body.in-search footer { 81 | display:none; 82 | } 83 | 84 | /* PAGE */ 85 | .page-container { 86 | max-width:100%; 87 | padding:30px 0 0 !important; 88 | } 89 | .page-container .page-breadcrumb { 90 | font-size:14px; 91 | } 92 | .page-container h1 { 93 | margin:0; 94 | padding:0; 95 | background:none; 96 | color:#000; 97 | font-size:18px; 98 | height:auto; 99 | } 100 | .page-container h1 span { 101 | float:none; 102 | background:none; 103 | display:inline; 104 | } 105 | .page-container .page-footer { 106 | line-height: 40px; 107 | padding:0 40px; 108 | margin:0; 109 | } 110 | .page-container .download-plugin { 111 | width:100%; 112 | } 113 | .page-container ul { 114 | padding:0; 115 | } 116 | .page-container li.img { 117 | display:none; 118 | } 119 | /* MODAL */ 120 | .sweet-alert .sa-icon { 121 | display:none !important; 122 | } 123 | } 124 | 125 | @media only screen 126 | and (max-device-width : 515px) { 127 | /* NAVBAR */ 128 | #navbar h1 a { 129 | font-size:20px; 130 | } 131 | #navbar #form-search { 132 | width:calc(100vw - 117px); 133 | position:relative; 134 | } 135 | #navbar #input-url { 136 | width:calc(100% - 50px); 137 | } 138 | #navbar #input-submit { 139 | width:50px; 140 | position:absolute; 141 | right:0px; 142 | top:9px; 143 | } 144 | /* CONTAINER */ 145 | #container { 146 | padding:60px 10px; 147 | } 148 | /* SIDEBAR */ 149 | #sidebar-wrapper { 150 | width:280px; 151 | } 152 | #toggle { 153 | left:280px; 154 | } 155 | body.in-search.toggle #toggle { 156 | -webkit-transform: translateX(-280px); 157 | transform: translateX(-280px) scaleX(-1); 158 | } 159 | body.in-search.toggle #sidebar-wrapper { 160 | /*margin-left:-480px;*/ 161 | -webkit-transform: translateX(-480px); 162 | transform: translateX(-480px); 163 | } 164 | .sidebar-title { 165 | display:none; 166 | } 167 | #sidebar-advanced-search { 168 | line-height:20px; 169 | } 170 | .sidebar-header { 171 | padding:10px; 172 | } 173 | .filter-item { 174 | width:auto; 175 | display:block; 176 | } 177 | .filter-item label { 178 | display: inline-block; 179 | width: 110px; 180 | } 181 | .filter-day .filter-select-wrapper, 182 | .filter-distance .filter-select-wrapper { 183 | width:130px; 184 | } 185 | .sidebar-details { 186 | display:none; 187 | } 188 | /* MEDIA */ 189 | .media { 190 | padding:10px; 191 | } 192 | .media-left { 193 | width:100px; 194 | margin-right:5px; 195 | } 196 | .media-left .media-object { 197 | top:auto; 198 | max-width: 100px; 199 | } 200 | .media-body { 201 | width:150px; 202 | } 203 | .media-heading a { 204 | display:block; 205 | overflow:hidden; 206 | max-height:38px; 207 | } 208 | /* FOOTER */ 209 | body footer { 210 | background:none; 211 | border:none; 212 | } 213 | body footer a, footer span { 214 | display:block; 215 | text-align: center; 216 | font-size:16px !important; 217 | } 218 | body footer a { 219 | text-decoration: underline; 220 | } 221 | .rightlinks { 222 | float:none; 223 | } 224 | /* PAGE */ 225 | .page-body { 226 | padding:15px !important; 227 | } 228 | .page-container h1 { 229 | 230 | } 231 | } 232 | -------------------------------------------------------------------------------- /webroot/comment-ca-marche.php: -------------------------------------------------------------------------------- 1 | 7 | 8 | 9 | 10 | <?= $title ?> - Mapicoin 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 | 43 | 44 |
45 | 46 | 49 | 50 |

Mapicoin :

51 | 52 |
53 |

Comment ça marche ?

54 |

Mapicoin permet de visualiser les annonces du site leboncoin.fr sur une carte interactive.

55 |

À partir d'une recherche LeBonCoin, Mapicoin vous aide à localiser l'ensemble des annonces sur une carte pour que vous puissiez avoir un regard global sur la situation géographique de chaque annonce.

56 |

Sans plus attendre, voici une vidéo vous expliquant le principe :

57 |
58 | 59 |
60 | 61 |

Mode automatique (recommandé)

62 |

Nous vous recommandons d'utiliser notre plugin Mapicoin afin de passer rapidement du site d'annonce à mapicoin!

63 |

Concrètement, une fois le plugin installé, il vous suffit, à partir d'une page de recherche, de cliquer sur ce dernier pour ouvrir automatiquement le site Mapicoin avec votre recherche déjà paramétrée. Ainsi, les résultats s'afficheront directement sur la carte, sans avoir à copier/coller l'URL dans le champ prévu à cet effet.

64 |

Nous supportons actuellement deux navigateurs :

65 |

66 | 67 | Plugin Mapicoin Chrome 68 | Téléchargez l'extension pour Chrome 69 | 70 | 71 | Plugin Mapicoin Firefox 72 | Téléchargez l'extension pour Firefox 73 | 74 |

75 |

Pour utiliser l'extension, c'est très simple :

76 |
    77 |
  • 1. Installez-la ;-)
  • 78 |
  • 2. Effectuez votre recherche sur leboncoin
  • 79 |
  • 3. L'icône de l'extension change de couleur (Elle passe du gris à l'orange)
  • 80 |
  • 4. Cliquez sur l'icône de l'extension et explorez ce fabuleux service!
    81 | (Vous pouvez aussi cliquer droit sur la page > "Voir sur mapicoin")
  • 82 |
83 | 84 | 85 |

Mode manuel

86 |

Pour fonctionner, mapicoin nécessite l'URL de votre recherche leboncoin.

87 |

Pour utiliser Mapicoin manuellement, procédez ainsi :

88 |
    89 |
  • 1. Effectuez votre recherche sur leboncoin
  • 90 | 93 |
  • 2. Copiez ensuite l'URL du site leboncoin
  • 94 | 97 |
  • 3. Rendez-vous ensuite sur mapicoin.fr et collez l'URL dans le champ prévu à cet effet
  • 98 | 101 |
  • 4. Lancez la recherche en cliquant sur "Afficher les résutlats"
  • 102 | 105 |
  • 5. Profitez du service ;-)
  • 106 |
107 | 108 |

That's all folk's !

109 |
110 | 111 |
112 | 113 | 114 | 115 |
116 | 117 | 118 | 119 | 120 | 121 | -------------------------------------------------------------------------------- /webroot/css/mapicoin-page.css: -------------------------------------------------------------------------------- 1 | .body { 2 | color:#000; 3 | } 4 | .page-container { 5 | width:800px; 6 | margin:0 auto; 7 | padding:30px 0; 8 | } 9 | .page-container a { 10 | color:#3b69c4; 11 | text-decoration: none; 12 | cursor:pointer; 13 | } 14 | .page-header { 15 | 16 | } 17 | .page-logo { 18 | margin:0 0 20px 0; 19 | text-align:center; 20 | } 21 | .page-logo a { 22 | font-size:35px; 23 | line-height:45px; 24 | text-decoration: none; 25 | } 26 | .page-logo a span { 27 | font-weight:700; 28 | color:#b9afa4; 29 | } 30 | .page-logo a span + span, 31 | .page-logo a i { 32 | color:#f56b2a; 33 | } 34 | .page-logo .baseline { 35 | font-size:11px; 36 | line-height: 12px; 37 | color:#b9afa4; 38 | padding:0 0 0 42px; 39 | display:block; 40 | font-weight:normal; 41 | } 42 | .page-body { 43 | padding:20px 40px; 44 | background: white; 45 | border-top:5px solid #f76a00; 46 | border-bottom:5px solid #b9afa4; 47 | } 48 | .page-breadcrumb { 49 | font-size: 10px; 50 | margin-bottom: 20px; 51 | line-height:20px; 52 | } 53 | .page-breadcrumb a, .page-breadcrumb a:visited { 54 | color:#b9afa4 55 | } 56 | .page-footer { 57 | color:#b9afa4; 58 | font-size:12px; 59 | line-height: 16px; 60 | margin:5px 0 0; 61 | } 62 | .page-footer .right { 63 | float:right; 64 | } 65 | h1 { 66 | font-weight: bold; 67 | font-size: 14px; 68 | margin-left: -30px; 69 | padding-left: 30px; 70 | height: 41px; 71 | line-height: 30px; 72 | text-transform: uppercase; 73 | color: #FFF; 74 | background: transparent url(/img/bg-h1.png) no-repeat left top; 75 | } 76 | h1 span { 77 | display: block; 78 | float: left; 79 | height: 31px; 80 | padding-right: 30px; 81 | background-color: #f76a00; 82 | } 83 | h2 { 84 | margin:30px 0 0 0; 85 | font-size:16px; 86 | line-height: 24px; 87 | font-weight:bold; 88 | } 89 | h2:first-child { 90 | margin-top:15px; 91 | } 92 | h2 .sup { 93 | color:#f56b2a; 94 | font-size:10px; 95 | position:relative; 96 | top:-10px; 97 | font-weight:normal; 98 | } 99 | p { 100 | margin-bottom: 20px; 101 | font-size:12px; 102 | line-height:20px; 103 | } 104 | ul { 105 | font-size:12px; 106 | line-height: 20px; 107 | padding:0 0 0 40px; 108 | list-style-type: none; 109 | margin:0 0 20px 0; 110 | font-weight: bold; 111 | } 112 | li.img { 113 | margin:15px 0 15px -40px; 114 | list-style: none; 115 | display:block; 116 | text-align: center; 117 | } 118 | li.img img { 119 | max-width: 100%; 120 | height:auto; 121 | box-shadow: 1px 1px 6px #555; 122 | } 123 | .download-plugin { 124 | margin:15px 0 0 0; 125 | text-align:center; 126 | width:340px; 127 | display:inline-block; 128 | } 129 | .download-plugin span { 130 | display:block; 131 | font-weight:bold; 132 | } 133 | .download-plugin img { 134 | line-height:64px; 135 | } 136 | .page-content iframe { 137 | max-width: 100%; 138 | } 139 | /* TABLE */ 140 | .page-content table a:link { 141 | /*color: #666;*/ 142 | color:#f76a00; 143 | font-weight: bold; 144 | text-decoration:none; 145 | } 146 | .page-content table a:active, 147 | .page-content table a:hover { 148 | color: #bd5a35; 149 | text-decoration:underline; 150 | } 151 | table { 152 | font-family:Arial, Helvetica, sans-serif; 153 | color:#666; 154 | font-size:12px; 155 | text-shadow: 1px 1px 0px #fff; 156 | background:#eaebec; 157 | margin:20px; 158 | border:#ccc 1px solid; 159 | 160 | -moz-border-radius:3px; 161 | -webkit-border-radius:3px; 162 | border-radius:3px; 163 | 164 | -moz-box-shadow: 0 1px 2px #d1d1d1; 165 | -webkit-box-shadow: 0 1px 2px #d1d1d1; 166 | box-shadow: 0 1px 2px #d1d1d1; 167 | } 168 | table th { 169 | padding:21px 25px 22px 25px; 170 | border-top:1px solid #fafafa; 171 | border-bottom:1px solid #e0e0e0; 172 | vertical-align: middle; 173 | background: #ededed; 174 | background: -webkit-gradient(linear, left top, left bottom, from(#ededed), to(#ebebeb)); 175 | background: -moz-linear-gradient(top, #ededed, #ebebeb); 176 | } 177 | table th:first-child { 178 | text-align: left; 179 | padding-left:20px; 180 | } 181 | table tr:first-child th:first-child { 182 | -moz-border-radius-topleft:3px; 183 | -webkit-border-top-left-radius:3px; 184 | border-top-left-radius:3px; 185 | } 186 | table tr:first-child th:last-child { 187 | -moz-border-radius-topright:3px; 188 | -webkit-border-top-right-radius:3px; 189 | border-top-right-radius:3px; 190 | } 191 | table tr { 192 | text-align: center; 193 | padding-left:20px; 194 | } 195 | table td:first-child { 196 | text-align: left; 197 | padding-left:20px; 198 | border-left: 0; 199 | } 200 | table td { 201 | padding:10px; 202 | border-top: 1px solid #ffffff; 203 | border-bottom:1px solid #e0e0e0; 204 | border-left: 1px solid #e0e0e0; 205 | word-wrap: break-word; 206 | max-width: 490px; 207 | vertical-align: middle; 208 | background: #fafafa; 209 | background: -webkit-gradient(linear, left top, left bottom, from(#fbfbfb), to(#fafafa)); 210 | background: -moz-linear-gradient(top, #fbfbfb, #fafafa); 211 | } 212 | table tr.even td { 213 | background: #f6f6f6; 214 | background: -webkit-gradient(linear, left top, left bottom, from(#f8f8f8), to(#f6f6f6)); 215 | background: -moz-linear-gradient(top, #f8f8f8, #f6f6f6); 216 | } 217 | table tr:last-child td { 218 | border-bottom:0; 219 | } 220 | table tr:last-child td:first-child { 221 | -moz-border-radius-bottomleft:3px; 222 | -webkit-border-bottom-left-radius:3px; 223 | border-bottom-left-radius:3px; 224 | } 225 | table tr:last-child td:last-child { 226 | -moz-border-radius-bottomright:3px; 227 | -webkit-border-bottom-right-radius:3px; 228 | border-bottom-right-radius:3px; 229 | } 230 | table tr:hover td { 231 | background: #f2f2f2; 232 | background: -webkit-gradient(linear, left top, left bottom, from(#f2f2f2), to(#f0f0f0)); 233 | background: -moz-linear-gradient(top, #f2f2f2, #f0f0f0); 234 | } -------------------------------------------------------------------------------- /webroot/mentions-legales.php: -------------------------------------------------------------------------------- 1 | 7 | 8 | 9 | 10 | <?= $title ?> - Mapicoin 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 | 41 | 42 |

Mapicoin :

43 | 44 |
45 |

Directeur de la technique : Quentin - quentin@mapicoin.fr

46 |

Directeur de la publication : Vincent - vincent@mapicoin.fr

47 | 48 |

Hébergeur

49 | 50 |

ONLINE SAS
51 | BP 438
52 | 75366 PARIS CEDEX 08
53 | Tél : + 33 (0) 899 173 788

54 | 55 | 56 |

Mentions relatives aux cookies

57 | 58 |

Qu’est-ce qu’un cookie et à quoi sert-il ?

59 | 60 |

Un cookie (ou témoin de connexion) est un fichier texte susceptible d’être enregistré, sous réserve de vos choix, dans un espace dédié du disque dur de votre terminal (ordinateur, tablette …) à l’occasion de la consultation d’un service en ligne grâce à votre logiciel de navigation.

61 |

Il est transmis par le serveur d’un site internet à votre navigateur. A chaque cookie est attribué un identifiant anonyme. Le fichier cookie permet à son émetteur d’identifier le terminal dans lequel il est enregistré pendant la durée de validité ou d’enregistrement du cookie concerné. Un cookie ne permet pas de remonter à une personne physique.

62 | 63 | 64 |

Quels types de cookies sont déposés par le site Web ?

65 | 66 | 67 |

Cookie publicitaires Google AdSense

68 | 69 |

Des services tiers de publicités comme la régie Adsense (et ses partenaires) contrôlent des cookies depuis leurs espaces publicitaires. Pour en savoir plus sur les annonces ciblées de Google, cliquez sur le lien règles de confidentialité.

70 | 71 |

Le présent site Internet n’est pas responsable de la gestion et de la durée de vie de ces cookies tiers et vous invite à prendre connaissance de la politique de chacun de ces prestataires.

72 | 73 |

Ils ont pour but de vous adresser des publicités personnalisées adaptées à vos attentes. Aucune données personnelles telles que vos nom, prénom, adresse postale ou électronique, etc. ne sera transmis à ces tiers partenaires dont l’intervention sur le site web se limite au dépôt de cookie par le biais des contenus publicitaires qu’ils gèrent.

74 | 75 |

Le présent site Web et n’a aucun contrôle sur ces cookies.

76 | 77 | 78 | 79 |

Cookies de Statistiques Google Analytics

80 | 81 |

Ces cookies permettent d’établir des statistiques de fréquentation des sites mapicoin.fr et de détecter des problèmes de navigation afin de suivre et d’améliorer la qualité de nos services.

82 | 83 |

Exercez vos choix selon le navigateur que vous utilisez.

84 | 85 |

Vous pouvez à tout moment paramétrer votre navigateur afin d’exprimer et de modifier vos souhaits en matière de cookies et notamment concernant les cookies de statistique. Vous pouvez exprimer vos choix en paramétrant votre navigateur de façon à refuser certains cookies.

86 | 87 |

Si vous refusez nos cookies et ceux de nos partenaires, votre des sites mapicoin.fr ne sera plus comptabilisée dans Google Analytics et vous ne pourrez plus bénéficier d’un certain nombre de fonctionnalités qui sont néanmoins nécessaires pour naviguer dans certaines de nos pages.

88 |

Nous vous informons que vous pouvez toutefois vous opposer à l’enregistrement de cookies en suivant le mode opératoire disponible ci-dessous :

89 | 90 |

Sur Internet Explorer

91 |
    92 |
  • 1. Allez dans Outils > Options Internet.
  • 93 |
  • 2. Cliquez sur l’onglet confidentialité.
  • 94 |
  • 3. Cliquez sur le bouton avancé, cochez la case » Ignorer la gestion automatique des cookies ».
  • 95 |
96 | 97 |

Sur Firefox

98 |
    99 |
  • 1. En haut de la fenêtre de Firefox, cliquez sur le bouton Firefox (menu Outils sous Windows XP), puis sélectionnez Options.
  • 100 |
  • 2. Sélectionnez le panneau Vie privée.
  • 101 |
  • 3. Paramétrez Règles de conservation : à utiliser les paramètres personnalisés pour l’historique.
  • 102 |
  • 4. Décochez Accepter les cookies.
  • 103 |
104 | 105 |

Sur Chrome

106 |
    107 |
  • 1. Cliquez sur l’icône représentant une clé à molette qui est située dans la barre d’outils du navigateur.
  • 108 |
  • 2. Sélectionnez Paramètres.
  • 109 |
  • 3. Cliquez sur Afficher les paramètres avancés.
  • 110 |
  • 4. Dans la section « Confidentialité », cliquez sur le bouton Paramètres de contenu.
  • 111 |
  • 5. Dans la section « Cookies », vous pouvez bloquer les cookies et données de sites tiers
  • 112 |
113 | 114 |

Sur Safari

115 |

    116 |
  • 1. Allez dans Réglages > Préférences 117 |
  • 2. Cliquez sur l’onglet Confidentialité 118 |
  • 3. Dans la zone » Bloquer les cookies « , cochez la case « toujours » 119 |
120 | 121 |

Sur Opéra

122 |
    123 |
  • 1. Allez dans Réglages > Préférences
  • 124 |
  • 2. Cliquez sur l’onglet avancées
  • 125 |
  • 3. Dans la zone » Cookies « , cochez la case » Ne jamais accepter les cookies »
  • 126 |
127 | 128 | 129 | 130 |

Les cookies de partage des réseaux sociaux

131 | 132 |

Sur certaines pages de mapicoin.fr figurent des boutons ou modules de réseaux sociaux tiers qui vous permettent d’exploiter les fonctionnalités de ces réseaux et en particulier de partager des contenus présents sur mapicoin.fr avec d’autres personnes.

133 |

Lorsque vous vous rendez sur une page internet sur laquelle se trouve un de ces boutons ou modules, votre navigateur peut envoyer des informations au réseau social qui peut alors associer cette visualisation à votre profil.

134 | 135 |

Des cookies des réseaux sociaux, dont nous n’avons pas la maîtrise, peuvent être alors être déposés dans votre navigateur par ces réseaux. Nous vous invitons à consulter les politiques de confidentialité propres à chacun de ces sites de réseaux sociaux, afin de prendre connaissance des finalités d’utilisation des informations de navigation que peuvent recueillir les réseaux sociaux grâce à ces boutons et modules.

136 | 137 | 138 |
139 | 140 |
141 | 142 | 143 | 144 |
145 | 146 | 147 | 148 | 149 | 150 | -------------------------------------------------------------------------------- /inc/crawlers/LeboncoinCrawler.class.php: -------------------------------------------------------------------------------- 1 | isSingleAdPage($url)) { 17 | return parent::__construct($url); 18 | } 19 | // Basic search ? 20 | // Extract page parameter 21 | if (!$this->validateURL($url)) { 22 | // Not a valid URL, try basic search 23 | $url = str_replace('{{SEARCH}}', $url, self::$_basic_search_url); 24 | } 25 | $url = replace_get_parameter($url, 'o', self::$_PAGE_PATTERN); 26 | return parent::__construct($url); 27 | } 28 | 29 | protected function validateURL($url) { 30 | if (preg_match('#^https?://www\.leboncoin\.fr/.+#i', $url)) { 31 | return true; 32 | } 33 | return false; 34 | } 35 | 36 | public function isSingleAdPage($url) { 37 | if (preg_match('#^https?://www\.leboncoin\.fr/[^/]+/\d+\.htm#i', $url)) { 38 | return true; 39 | } 40 | return false; 41 | } 42 | 43 | 44 | /** 45 | * Return DOMElements of all ads based on a xpath 46 | * @return array(DOMElement, ...) 47 | */ 48 | public function getAds() { 49 | return $this->domXpath->query( 50 | '//section[@class="tabsContent block-white dontSwitch"]/ul/li' 51 | ); 52 | } 53 | 54 | /** 55 | * Return ad info from DOMElement (using xpath) 56 | * @return array 57 | */ 58 | public function getAdInfo(DOMElement $domElement) { 59 | $return = [ 60 | 'url' => null, 61 | 'title' => null, 62 | 'picture' => null, 63 | 'picture_count' => null, 64 | 'location' => null, 65 | 'price' => null, 66 | 'price_raw' => null, 67 | 'date' => null, 68 | 'pro' => null, 69 | ]; 70 | // url 71 | $tmp = $this->domXpath->query( 72 | './/a[@class="list_item clearfix trackable"]/@href', 73 | $domElement 74 | ); 75 | $return['url'] = 'https:'.$tmp->item(0)->nodeValue; 76 | 77 | // title 78 | $tmp = $this->domXpath->query( 79 | './/h2[@class="item_title"]/text()', 80 | $domElement 81 | ); 82 | $return['title'] = trim($tmp->item(0)->nodeValue); 83 | 84 | // picture 85 | $tmp = $this->domXpath->query( 86 | './/span[@class="lazyload"]/@data-imgsrc', 87 | $domElement 88 | ); 89 | $return['picture'] = trim(@$tmp->item(0)->nodeValue ?? 'https://static.leboncoin.fr/img/no-picture.png'); 90 | 91 | // picture_count 92 | $tmp = $this->domXpath->query( 93 | './/span[@class="item_imageNumber"]/span/text()', 94 | $domElement 95 | ); 96 | $return['picture_count'] = trim(@$tmp->item(0)->nodeValue ?? 0); 97 | 98 | // pro 99 | $tmp = $this->domXpath->query( 100 | './/span[@class="ispro"]/text()', 101 | $domElement 102 | ); 103 | $tmp = trim(@$tmp->item(0)->nodeValue ?? null); 104 | $return['pro'] = preg_replace('#\s+#i', ' ', $tmp); 105 | 106 | // location 107 | $tmp = $this->domXpath->query( 108 | '(.//meta[@itemprop="address"])[1]/@content', 109 | $domElement 110 | ); 111 | $tmp = trim($tmp->item(0)->nodeValue); 112 | $return['location'] = preg_replace('#\s+#i', ' ', $tmp); 113 | 114 | // price 115 | $tmp = $this->domXpath->query( 116 | './/h3[@class="item_price"]/text()', 117 | $domElement 118 | ); 119 | $return['price'] = trim(@$tmp->item(0)->nodeValue ?? ''); 120 | $return['price_raw'] = filter_var($return['price'], FILTER_SANITIZE_NUMBER_FLOAT); 121 | 122 | // date 123 | $tmp = $this->domXpath->query( 124 | './/aside[@class="item_absolute"]/p[@class="item_supp"]/text()', 125 | $domElement 126 | ); 127 | $return['date'] = trim($tmp->item(0)->nodeValue); 128 | $return['timestamp'] = $this->convertDateToTimestamp($return['date']); 129 | 130 | return $return; 131 | } 132 | 133 | public function getSingleAdInfo() { 134 | $domElement = $this->domXpath->query( 135 | '//section[@id="adview"]' 136 | )->item(0); 137 | $return = [ 138 | 'url' => null, 139 | 'title' => null, 140 | 'picture' => null, 141 | 'picture_count' => null, 142 | 'location' => null, 143 | 'price' => null, 144 | 'price_raw' => null, 145 | 'date' => null, 146 | 'pro' => null, 147 | ]; 148 | // url 149 | $return['url'] = $this->url; 150 | 151 | // title 152 | $tmp = $this->domXpath->query( 153 | './/h1/text()', 154 | $domElement 155 | ); 156 | $return['title'] = trim($tmp->item(0)->nodeValue); 157 | 158 | // picture 159 | $tmp = $this->domXpath->query( 160 | './/section[@class="adview_main"]/meta/@content', 161 | $domElement 162 | ); 163 | $return['picture'] = 'https:'.trim(@$tmp->item(0)->nodeValue ?? '//static.leboncoin.fr/img/no-picture.png'); 164 | 165 | // picture_count 166 | $tmp = $this->domXpath->query( 167 | 'count(.//section[@class="carousel"]//ul/li)', 168 | $domElement 169 | ); 170 | $return['picture_count'] = trim(@$tmp->item(0)->nodeValue ?? 0); 171 | 172 | // pro 173 | $return['pro'] = ''; 174 | 175 | // location 176 | $tmp = $this->domXpath->query( 177 | './/span[@itemprop="address"]/text()', 178 | $domElement 179 | ); 180 | $tmp = trim($tmp->item(0)->nodeValue); 181 | $return['location'] = preg_replace('#\s+#i', ' ', $tmp); 182 | 183 | // price 184 | $tmp = $this->domXpath->query( 185 | './/h2[@class="item_price clearfix"]/span[@class="value"]/text()', 186 | $domElement 187 | ); 188 | $return['price'] = trim(@$tmp->item(0)->nodeValue ?? ''); 189 | $return['price_raw'] = filter_var($return['price'], FILTER_SANITIZE_NUMBER_FLOAT); 190 | 191 | // date 192 | $tmp = $this->domXpath->query( 193 | './/aside[@class="item_absolute"]/p[@class="item_supp"]/text()', 194 | $domElement 195 | ); 196 | $return['date'] = ''; 197 | $return['timestamp'] = ''; 198 | 199 | return $return; 200 | } 201 | 202 | /** 203 | * Convert a leboncoin date to timestamp 204 | */ 205 | private function convertDateToTimestamp($date) { 206 | $tmp = explode(',', $date); 207 | if (!isset($tmp[1])) { 208 | return false; 209 | } 210 | $jour = trim(strtolower($tmp[0])); 211 | $heure = trim($tmp[1]); 212 | if ($jour == "aujourd'hui") { 213 | $jour = date('d F'); 214 | } elseif ($jour == 'hier') { 215 | $jour = date('d F', strtotime('-1 day')); 216 | } 217 | 218 | // On converti les dates leboncoin en EN 219 | $replaces = [ 220 | 'janvier' => 'january', 221 | 'février' => 'february', 222 | 'mars' => 'march', 223 | 'avril' => 'april', 224 | 'mai' => 'may', 225 | 'juin' => 'june', 226 | 'juillet' => 'july', 227 | 'août' => 'august', 228 | 'septembre' => 'september', 229 | 'octobre' => 'october', 230 | 'novembre' => 'november', 231 | 'décembre' => 'december', 232 | ]; 233 | 234 | $date = sprintf('%s %d %s', $jour, date('Y'), $heure); 235 | $date = str_ireplace(array_keys($replaces), array_values($replaces), $date); 236 | 237 | return strtotime($date); 238 | } 239 | } -------------------------------------------------------------------------------- /inc/functions.inc.php: -------------------------------------------------------------------------------- 1 | $config) { 18 | $domains = $config['mapicoin-domains'] ?? []; 19 | foreach ($config['mapicoin-domains'] as $d) { 20 | if ($host == $d) { 21 | return $site; 22 | } 23 | } 24 | } 25 | return SITE_LEBONCOIN; 26 | } 27 | 28 | 29 | function stats_add_search($search) { 30 | global $_MYSQLI, $_SITE; 31 | /* Crée une requête préparée */ 32 | if (!($stmt = $_MYSQLI->prepare(" 33 | INSERT INTO `search` (id,site,search,count) 34 | VALUES (?,?,?,1) 35 | ON DUPLICATE KEY UPDATE 36 | count = (count + 1)" 37 | ))) { 38 | return false; 39 | } 40 | /* Lecture des marqueurs */ 41 | $id = md5($search); 42 | $stmt->bind_param( 43 | "sss", 44 | $id, 45 | $_SITE, 46 | $search 47 | ); 48 | /* Exécution de la requête */ 49 | $stmt->execute(); 50 | /* Fermeture du traitement */ 51 | $stmt->close(); 52 | return true; 53 | } 54 | 55 | 56 | function stats_get_searches() { 57 | global $_MYSQLI, $_SITE; 58 | /* Crée une requête préparée */ 59 | if (!($stmt = $_MYSQLI->prepare(" 60 | SELECT search,count,updated 61 | FROM search 62 | WHERE site = ? 63 | ORDER BY updated DESC"))) { 64 | return false; 65 | } 66 | $stmt->bind_param("s", $_SITE); 67 | $stmt->execute(); 68 | $return = []; 69 | $stmt->bind_result($search,$count,$updated); 70 | /* Lecture des valeurs */ 71 | while ($stmt->fetch()) { 72 | $return[] = [ 73 | 'search' => $search, 74 | 'count' => $count, 75 | 'updated' => $updated, 76 | ]; 77 | } 78 | /* Fermeture du traitement */ 79 | $stmt->close(); 80 | 81 | return $return; 82 | } 83 | 84 | 85 | function set_location_in_cache($location = '', $coords = array()) { 86 | global $_MYSQLI, $_SITE; 87 | /* Crée une requête préparée */ 88 | if (!($stmt = $_MYSQLI->prepare(" 89 | INSERT INTO geocode (id,site,location,lat,lng) 90 | VALUES (?,?,?,?,?) 91 | ON DUPLICATE KEY 92 | UPDATE lat=?, lng=?" 93 | ))) { 94 | error_log('set_location_in_cache() : MySQL ERROR : ' . $_MYSQLI->error); 95 | return false; 96 | } 97 | /* Lecture des marqueurs */ 98 | $id = md5($location); 99 | $stmt->bind_param( 100 | "sssssdd", 101 | $id, 102 | $_SITE, 103 | $location, 104 | $coords[0], 105 | $coords[1], 106 | $coords[0], 107 | $coords[1] 108 | ); 109 | /* Exécution de la requête */ 110 | $stmt->execute(); 111 | /* Fermeture du traitement */ 112 | $stmt->close(); 113 | 114 | return true; 115 | } 116 | 117 | function get_location_from_cache($location = '') { 118 | global $_MYSQLI, $_SITE; 119 | /* Crée une requête préparée */ 120 | if (!($stmt = $_MYSQLI->prepare("SELECT lat,lng FROM geocode WHERE id=? AND site=? LIMIT 1"))) { 121 | return false; 122 | } 123 | /* Lecture des marqueurs */ 124 | $id = md5($location); 125 | $stmt->bind_param("ss", $id, $_SITE); 126 | /* Exécution de la requête */ 127 | $stmt->execute(); 128 | /* Lecture des variables résultantes */ 129 | $stmt->bind_result($lat,$lng); 130 | /* Récupération des valeurs */ 131 | if (!$stmt->fetch()) { 132 | return null; 133 | } 134 | /* Fermeture du traitement */ 135 | $stmt->close(); 136 | 137 | return [$lat,$lng]; 138 | } 139 | 140 | function get_location_from_bdd($location = '') { 141 | global $_MYSQLI; 142 | 143 | $tmp = explode('/', $location); 144 | if (!isset($tmp[1])) { 145 | return false; 146 | } 147 | 148 | $ville = trim($tmp[0]); 149 | $departement = trim($tmp[1]); 150 | 151 | /* Crée une requête préparée */ 152 | if (!($stmt = $_MYSQLI->prepare(" 153 | SELECT latitude, longitude 154 | FROM city c 155 | JOIN departement d ON c.departement_code = d.departement_code 156 | WHERE 157 | (c.name=? OR CONCAT(c.article, ' ', c.name) = ? OR CONCAT(c.article, c.name) = ?) 158 | AND d.name = ? 159 | LIMIT 1"))) { 160 | error_log($_MYSQLI->error); 161 | return false; 162 | } 163 | // error_log(sprintf(" 164 | // SELECT latitude, longitude 165 | // FROM city c 166 | // JOIN departement d ON c.departement_code = d.departement_code 167 | // WHERE 168 | // (c.name=%s OR CONCAT(c.article, ' ', c.name) = %s) 169 | // AND d.name = %s 170 | // LIMIT 1", $ville, $ville, $departement)); 171 | /* Lecture des marqueurs */ 172 | $stmt->bind_param("ssss", $ville, $ville, $ville, $departement); 173 | /* Exécution de la requête */ 174 | $stmt->execute(); 175 | /* Lecture des variables résultantes */ 176 | $stmt->bind_result($lat,$lng); 177 | /* Récupération des valeurs */ 178 | if (!$stmt->fetch()) { 179 | return null; 180 | } 181 | /* Fermeture du traitement */ 182 | $stmt->close(); 183 | 184 | return [$lat,$lng]; 185 | } 186 | 187 | 188 | /** 189 | * Get lat & lng from places (bulk /!\) using mapquest 190 | */ 191 | function convert_places_to_latlng($places = array()) { 192 | global $_CONFIG, $_SITES, $_SITE; 193 | $geocoder = sprintf( 194 | 'https://maps.googleapis.com/maps/api/geocode/json?key=%s&address=%%s', 195 | $_CONFIG->api->google_server_key 196 | ); 197 | 198 | $return = []; 199 | foreach ($places as $i => $place) { 200 | 201 | $cache = get_location_from_cache($place); 202 | 203 | if (!$cache) { 204 | $cache = get_location_from_bdd($place); 205 | } 206 | 207 | if (!$cache) { 208 | $tmp = preg_replace("/[^\s\p{L}0-9]+/u", "", $place); 209 | $tmp = str_replace(' / ', ', ', $tmp); 210 | $tmp = preg_replace('/\s+/', '+', $tmp); 211 | $tmp .= ',+'.$_SITES[$_SITE]['country']; 212 | $query = sprintf($geocoder, $tmp); 213 | $result = json_decode(file_get_contents($query)); 214 | if (count($result->results) == 0) { 215 | $lat = 46.5002839; 216 | $lng = 2.7915620; 217 | } else { 218 | $json = $result->results[0]; 219 | $lat = $json->geometry->location->lat; 220 | $lng = $json->geometry->location->lng; 221 | set_location_in_cache( 222 | $place, 223 | [$lat, $lng] 224 | ); 225 | error_log("NEW LOCATION: ".$place); 226 | } 227 | // Don't overload google ! 228 | usleep(USLEEP_BETWEEN_API_CALL); 229 | } else { 230 | $lat = $cache[0]; 231 | $lng = $cache[1]; 232 | // error_log("LOCATION FROM CACHE: ".$place); 233 | } 234 | $return[$i] = [ 235 | 'lat' => $lat, 236 | 'lng' => $lng 237 | ]; 238 | } 239 | 240 | return $return; 241 | } 242 | 243 | /** 244 | * Calcul la moyenne en ignorant $ignore pourcent(s) de valeur trop haute et trop basse 245 | */ 246 | function get_average_price(array $data, $ignore = 10) { 247 | $count = count($data); 248 | if ($count == 0) { 249 | return 0; 250 | } 251 | if ($count == 1) { 252 | $tmp = array_pop($data); 253 | // error_log(print_r($tmp, 1)); 254 | return round($tmp['price_raw'], 0); 255 | } 256 | // Moyenne 257 | $moyenne = $count2 = 0; 258 | $offset = round($count * $ignore / 100, 0); 259 | foreach ($data as $i => $e) { 260 | if (!isset($e['price_raw']) || $e['price_raw'] === '') { 261 | continue; 262 | } 263 | if ($i < $offset || $i > ($count - $offset)) { 264 | continue; 265 | } 266 | $moyenne += $e['price_raw']; 267 | ++$count2; 268 | } 269 | return round($moyenne / $count2, 0); 270 | } 271 | 272 | 273 | /** 274 | * Replace a specific GET parameter from a given URL 275 | */ 276 | function replace_get_parameter($url, $parameter, $value) { 277 | // parse the url 278 | $pathInfo = parse_url($url); 279 | $queryString = $pathInfo['query'] ?? ''; 280 | // convert the query parameters to an array 281 | parse_str($queryString, $queryArray); 282 | // add the new query parameter into the array 283 | $queryArray[$parameter] = $value; 284 | // build the new query string 285 | $newQueryStr = http_build_query($queryArray); 286 | return sprintf( 287 | '%s://%s%s?%s', 288 | $pathInfo['scheme'], 289 | $pathInfo['host'], 290 | $pathInfo['path'], 291 | $newQueryStr 292 | ); 293 | } 294 | 295 | function time_elapsed_string($ptime) 296 | { 297 | $etime = time() - $ptime; 298 | 299 | if ($etime < 1) { 300 | return "À l'instant"; 301 | } 302 | 303 | $a = array( 365 * 24 * 60 * 60 => 'année', 304 | 30 * 24 * 60 * 60 => 'mois', 305 | 24 * 60 * 60 => 'jour', 306 | 60 * 60 => 'heure', 307 | 60 => 'minute', 308 | 1 => 'seconde' 309 | ); 310 | $a_plural = array( 'année' => 'années', 311 | 'mois' => 'months', 312 | 'jour' => 'jours', 313 | 'heure' => 'heures', 314 | 'minute' => 'minutes', 315 | 'seconde' => 'secondes' 316 | ); 317 | 318 | foreach ($a as $secs => $str) { 319 | $d = $etime / $secs; 320 | if ($d >= 1) { 321 | $r = round($d); 322 | return "Il y a ".$r . ' ' . ($r > 1 ? $a_plural[$str] : $str); 323 | } 324 | } 325 | } 326 | 327 | 328 | -------------------------------------------------------------------------------- /webroot/js/mapicoin.js: -------------------------------------------------------------------------------- 1 | $(document).ready(function() { 2 | 3 | // === 4 | // Filters 5 | // === 6 | init_search_filters(); 7 | 8 | $('body').on('click', '#toggle', function(event) { 9 | $('body').toggleClass('toggle'); 10 | }) 11 | 12 | // === 13 | // Init. google map 14 | // === 15 | map = initialize_map(); 16 | 17 | // === 18 | // Focus main input on load 19 | // === 20 | $('.input-url:eq(1)').focus(); 21 | 22 | // === 23 | // Handle demo links 24 | // === 25 | handle_demo_links(); 26 | 27 | // === 28 | // Handle alert box 29 | // === 30 | handle_alert_boxes(); 31 | 32 | // === 33 | // Form submit 34 | // === 35 | $('.form-search').each(function() { 36 | $(this).on('submit', function(event) { 37 | event.preventDefault(); 38 | var $form = $(this); 39 | var $input = $form.find('.input-url'); 40 | var url = $input.val(); 41 | // Update all input-url 42 | if (!url) { 43 | $form.find('.input-url').focus(); 44 | custom_alert( 45 | "Oops !", 46 | "Veuillez renseigner le champ de recherche :

" + 47 | "", 51 | "warning", 52 | {html: true} 53 | ); 54 | return false; 55 | } 56 | $input.blur(); 57 | $('.input-url').val(url); 58 | 59 | lock_search(true); 60 | 61 | map.initialZoom = false; 62 | 63 | $.ajax({ 64 | url: './get-ads.php', 65 | type: 'post', 66 | data: $form.serialize(), 67 | dataType: 'json', 68 | timeout: 60000, 69 | success: function(data) { 70 | if (!data.status) { 71 | lock_search(false); 72 | custom_alert("Oops !", data.message, "error"); 73 | return false; 74 | } 75 | if (!data.datas || data.datas.length == 0) { 76 | lock_search(false); 77 | custom_alert( 78 | "Aucune annonce trouvée :-(", 79 | "Votre recherche n'a retourné aucun résultat.
" + 80 | "Si besoin, rendez-vous sur la page Comment ça marche " + 81 | "pour plus d'explications " + 82 | "sur le fonctionnement de Mapicoin", 83 | "warning", 84 | {html: true} 85 | ); 86 | return false; 87 | } 88 | 89 | // Remove previous markers 90 | remove_markers(); 91 | 92 | // Update URL 93 | var tmp = url.replace(/\?/g, '%3F').replace(/&/g, '%26'); 94 | update_browser_url({u: tmp}, false); 95 | 96 | // Group ads by lat/lng 97 | var datas = regroup_ads(data.datas); 98 | 99 | // Toggle panel 100 | panel_toggle(true); 101 | 102 | // Update panel + Bind 103 | panel_update(data, datas); 104 | 105 | // Create and add markers to the map + bind 106 | add_ads_markers(map, datas); 107 | 108 | // Fit bounds 109 | map_fit_bounds(); 110 | 111 | // ScrollTo the map 112 | $('html, body').animate({ 113 | scrollTop:$('#map').offset().top 114 | }, 115 | 400, 116 | function () { 117 | $('#header').hide(); 118 | $('#map').show(); 119 | lock_search(false); 120 | } 121 | ); 122 | }, 123 | error: function() { 124 | lock_search(false); 125 | custom_alert( 126 | "Oops !", 127 | "Une erreur inconnue sur Mapicoin est survenue.
" + 128 | "
" + 129 | "Si le problème persiste, n'hésitez pas à contacter notre équipe via " + 130 | "Facebook ou " + 131 | "Twitter.", 132 | "error", 133 | {html: true} 134 | ); 135 | } 136 | }) 137 | 138 | }); 139 | }); 140 | 141 | 142 | // === 143 | // Detect GET parameter and submit if needed 144 | // === 145 | var u = parse_query_strings('u'); 146 | if (u) { 147 | // u = u 148 | // .replace(/%3F/g, '?') 149 | // .replace(/%26/g, '&'); 150 | console.log(u); 151 | u = decodeURIComponent(u); 152 | $('.input-url').val(u); 153 | $('.form-search').first().submit(); 154 | } 155 | var distance = parse_query_strings('distance'); 156 | if (distance) { 157 | set_user_distance(distance); 158 | } 159 | var day = parse_query_strings('day'); 160 | if (day) { 161 | set_user_day(day); 162 | } 163 | 164 | }); 165 | 166 | /** 167 | * (un)lock the search form 168 | */ 169 | function lock_search(lock) { 170 | if (lock) { 171 | $('.input-submit').button('loading'); 172 | $('.input-url').prop('readonly', true); 173 | $("body").css("cursor", "progress"); 174 | } else { 175 | $('.input-submit').button('reset'); 176 | $('.input-url').prop('readonly', false); 177 | $("body").css("cursor", "default"); 178 | } 179 | } 180 | 181 | function panel_toggle(toggle) { 182 | if (toggle) { 183 | $('body').addClass('in-search'); 184 | //$('#map').addClass('in-search'); 185 | //$('#overlay').fadeOut(); 186 | } else { 187 | $('body').removeClass('in-search'); 188 | //$('#map').removeClass('in-search'); 189 | //$('#overlay').fadeIn(); 190 | } 191 | google.maps.event.trigger(map, "resize"); 192 | } 193 | 194 | function panel_update(data, datas) { 195 | var title = data.title; 196 | // Title + Content + Edit 197 | $('.sidebar-title').text(title).attr('title', title); 198 | $('.sidebar-edit-search').attr('href', $('.input-url').first().val()); 199 | $('#sidebar').html(''); 200 | 201 | // Update content 202 | for (var i in datas) { 203 | var ads = datas[i].ads; 204 | for (var j in ads) { 205 | $('#sidebar').append(ads[j].text); 206 | } 207 | } 208 | 209 | // Update count 210 | panel_update_count(); 211 | // Update average 212 | panel_update_average(data); 213 | 214 | // Lazyload 215 | $(".lazyload").lazyload({ 216 | effect : "fadeIn", 217 | container: $("#sidebar") 218 | }); 219 | 220 | // Bind click 221 | $('#sidebar').on('click', '.pwet', function(event) { 222 | var i = $(this).data('index'); 223 | google.maps.event.trigger(markers[i], "click"); 224 | }); 225 | 226 | // Bind hover 227 | $('#sidebar').on({ 228 | mouseenter: function() { 229 | var i = $(this).data('index'); 230 | if (typeof markers[i] == 'undefined') { 231 | return; 232 | } 233 | markers[i].setAnimation(google.maps.Animation.BOUNCE); 234 | }, 235 | // mouse out 236 | mouseleave: function () { 237 | var i = $(this).data('index'); 238 | if (typeof markers[i] == 'undefined') { 239 | return; 240 | } 241 | markers[i].setAnimation(null); 242 | } 243 | }, '.pwet'); 244 | 245 | // HL first 246 | panel_highlight(0); 247 | } 248 | function panel_update_count() { 249 | var count = $('#sidebar').find('.pwet:visible').length; 250 | var plural = (count > 1 ? 's' : ''); 251 | $('.sidebar-count').text( 252 | 'Affichage de '+count+' annonce'+plural 253 | ); 254 | $('.sidebar-count').text( 255 | 'Affichage de '+count+' annonce'+plural 256 | ); 257 | } 258 | function panel_update_average(data) { 259 | if (data.currency.position == 'left') { 260 | var text = "{1}{0}"; 261 | } else { 262 | var text = "{0}{1}"; 263 | } 264 | price = data.average_price; 265 | console.log(price); 266 | text = text.format( 267 | price.formatMoney(0, ',', '\''), 268 | data.currency.symbol 269 | ); 270 | $('.sidebar-average-price').text( 271 | "Prix moyen : "+text 272 | ); 273 | } 274 | 275 | function panel_highlight(index) { 276 | $('#sidebar .pwet').removeClass('active'); 277 | $('#sidebar .pwet[data-index="'+index+'"]').addClass('active'); 278 | var container = $('#sidebar'), 279 | scrollTo = $('#sidebar .pwet[data-index="'+index+'"]').first(); 280 | if (scrollTo.size()) { 281 | container.animate({ 282 | scrollTop: scrollTo.offset().top - container.offset().top + container.scrollTop() 283 | }); 284 | } 285 | } 286 | function panel_toggle_item(index, show) { 287 | var $e = $('#sidebar .pwet[data-index="'+index+'"]'); 288 | if (show) { 289 | $e.show(); 290 | } else { 291 | $e.hide(); 292 | } 293 | } 294 | 295 | 296 | 297 | /** 298 | * Bind les filtres de recherche 299 | */ 300 | function init_search_filters() { 301 | $('#filter-distance,#filter-day').on('change', function() { 302 | var value = $(this).val(); 303 | var id = $(this).attr('id'); 304 | // Update URL 305 | // var day = parse_query_strings('day'); 306 | // var distance = parse_query_strings('distance'); 307 | switch (id) { 308 | case 'filter-day': 309 | update_browser_url({'day': value}, false); 310 | set_user_day(value); 311 | break; 312 | case 'filter-distance': 313 | update_browser_url({'distance': value}, false); 314 | set_user_distance(value); 315 | break; 316 | default: 317 | // Invalid choice 318 | break; 319 | } 320 | update_marker_from_filters(); 321 | }); 322 | } 323 | 324 | /** 325 | * Gère les événements sur les liens de démonstration 326 | */ 327 | function handle_demo_links() { 328 | $('body').on('click', '.demo-link', function(event) { 329 | event.preventDefault(); 330 | // Fill input 331 | $('.input-url').val($(this).text()); 332 | // Close modal (found nothin' better than simulate a click...) 333 | $('.sa-confirm-button-container').find('button').click() 334 | // Submit 335 | $('#form-search').submit(); 336 | }) 337 | } 338 | 339 | /** 340 | * Gère les événements pour pop-up des modals diverses 341 | */ 342 | function handle_alert_boxes() { 343 | $('body').on('click', '.filter-item.filter-distance.disabled', function() { 344 | custom_alert( 345 | "Fonctionnalité désactivée ! ", 346 | "Cette fonctionnalité est désactivée car il semblerait que " + 347 | "vous n'avez pas activé la géolocalisation.
" + 348 | "Nous avons besoin de l'approbation de votre navigateur afin de déterminer votre position. ", 349 | "warning", 350 | { 351 | html:true, 352 | showCancelButton: true, 353 | closeOnConfirm: false, 354 | showLoaderOnConfirm: true, 355 | confirmButtonText: "Déterminer ma position" 356 | }, 357 | function () { 358 | get_user_location(); 359 | } 360 | ); 361 | }) 362 | } 363 | 364 | 365 | 366 | /** 367 | * Used to browse all ads and group them by location (lat/lng) in 368 | * order to have 1 marker for multiple ads 369 | */ 370 | function regroup_ads(datas) { 371 | 372 | var result = []; 373 | 374 | for (var i in datas) { 375 | 376 | var ad = datas[i]; 377 | ad['count'] = 1; 378 | ad['text'] = '' + 379 | '
' + 380 | '
' + 381 | ''+ad.title+'' + 382 | // ''+ad.picture_count+'' + 383 | '
' + 384 | '
' + 385 | '

'+ 386 | '' + 387 | ad.title+ 388 | ''+ 389 | '

' + 390 | '

'+ad.pro+'

' + 391 | '

'+ad.location+'

' + 392 | '

'+ad.price+'

' + 393 | '

'+ad.date+'

' + 394 | '
' + 395 | '
'; 396 | 397 | // Test if current ad has the same lat/lng of another ad 398 | var found = false; 399 | for (var j in result) { 400 | var tmp = result[j]; 401 | // ad matching another one 402 | if (tmp.latlng.lat == ad.latlng.lat && tmp.latlng.lng == ad.latlng.lng) { 403 | found = true; 404 | result[j].ads.push(ad) 405 | break; 406 | } 407 | } 408 | // If found, we add the pop-up content next to the current one('s) 409 | if (!found) { 410 | result.push({ 411 | 'ads' : [ad], 412 | 'latlng': ad.latlng 413 | }); 414 | } 415 | } 416 | 417 | // We set class index for each ads now, based of lat/lng 418 | for (var i in result) { 419 | var ads = result[i].ads; 420 | for (var j in ads) { 421 | ads[j].text = ''+ 422 | '
  • '+ads[j].text+'
  • '; 423 | } 424 | } 425 | 426 | return result; 427 | } 428 | 429 | 430 | 431 | -------------------------------------------------------------------------------- /webroot/css/mapicoin.css: -------------------------------------------------------------------------------- 1 | /* RESET */ 2 | 3 | html, body, div, span, applet, object, iframe, 4 | h1, h2, h3, h4, h5, h6, p, blockquote, pre, 5 | a, abbr, acronym, address, big, cite, code, 6 | del, dfn, em, img, ins, kbd, q, s, samp, 7 | small, strike, strong, sub, sup, tt, var, 8 | b, u, i, center, 9 | dl, dt, dd, ol, ul, li, 10 | fieldset, form, label, legend, 11 | table, caption, tbody, tfoot, thead, tr, th, td, 12 | article, aside, canvas, details, embed, 13 | figure, figcaption, footer, header, hgroup, 14 | menu, nav, output, ruby, section, summary, 15 | time, mark, audio, video { 16 | margin: 0; 17 | padding: 0; 18 | border: 0; 19 | font-size: 100%; 20 | font: inherit; 21 | vertical-align: baseline; 22 | } 23 | * { 24 | box-sizing:border-box; 25 | } 26 | /* HTML5 display-role reset for older browsers */ 27 | article, aside, details, figcaption, figure, 28 | footer, header, hgroup, menu, nav, section { 29 | display: block; 30 | } 31 | html, body {line-height: 1;} 32 | ol, ul {list-style: none;} 33 | blockquote, q {quotes: none;} 34 | blockquote:before, blockquote:after, 35 | q:before, q:after { 36 | content: ''; 37 | content: none; 38 | } 39 | table { 40 | border-collapse: collapse; 41 | border-spacing: 0; 42 | } 43 | strong {font-weight:bold;} 44 | i {font-style: italic;} 45 | input:read-only {opacity:0.7;} 46 | input:-webkit-autofill {-webkit-box-shadow: 0 0 0px 1000px white inset;} 47 | 48 | /* APP */ 49 | body { 50 | font-family: Verdana,Arial,sans-serif; 51 | margin:0; 52 | padding:0; 53 | color:#333; 54 | font-size:14px; 55 | background:#e3f0f3; 56 | } 57 | .dib {display:inline-block;vertical-align: middle;} 58 | .hand {cursor:pointer;} 59 | .text-center {text-align: center;} 60 | .input-lg { 61 | max-width: 100%; 62 | width: 550px; 63 | height: 36px; 64 | line-height: 36px; 65 | font-size: 16px; 66 | padding:0px 15px; 67 | } 68 | .ellipsis { 69 | white-space:nowrap; 70 | overflow: hidden; 71 | text-overflow: ellipsis; 72 | word-wrap: break-word; 73 | } 74 | .pull-right { float:right;} 75 | .pull-left { float:left;} 76 | .btn { 77 | padding: 5px 12px; 78 | font-size: 13px; 79 | font-weight: 700; 80 | line-height: 18px; 81 | cursor: default; 82 | -webkit-background-clip: border-box; 83 | background-clip: border-box; 84 | border-radius: 2px; 85 | -webkit-box-shadow: none; 86 | box-shadow: none; 87 | cursor:pointer; 88 | } 89 | .btn:active { 90 | position:relative; 91 | top:2px; 92 | } 93 | .btn-warning { 94 | color: #fff; 95 | text-shadow: 0 1px rgba(0,0,0,.1); 96 | /*background-image: -webkit-linear-gradient(top,#f5a42a 0,#f56b2a 100%); 97 | background-image: -o-linear-gradient(top,#f5a42a 0,#f56b2a 100%); 98 | background-image: -webkit-gradient(linear,left top,left bottom,from(#f5a42a),to(#f56b2a)); 99 | background-image: linear-gradient(to bottom,#f5a42a 0,#f56b2a 100%); 100 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffbb450', endColorstr='#fffaa937', GradientType=0); 101 | filter: progid:DXImageTransform.Microsoft.gradient(enabled=false); 102 | background-repeat: repeat-x; 103 | border: 1px solid #f5a42a;*/ 104 | background:#f56b2a; 105 | border:none; 106 | } 107 | .btn-default { 108 | color: #333; 109 | text-shadow: 0 1px rgba(0,0,0,.1); 110 | text-shadow: 0 1px 0 #fff; 111 | background-color: #f3f3f3; 112 | background-image: -webkit-linear-gradient(top,#f5f5f5 0,#f1f1f1 100%); 113 | background-image: -o-linear-gradient(top,#f5f5f5 0,#f1f1f1 100%); 114 | background-image: -webkit-gradient(linear,left top,left bottom,from(#f5f5f5),to(#f1f1f1)); 115 | background-image: linear-gradient(to bottom,#f5f5f5 0,#f1f1f1 100%); 116 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#fff1f1f1', GradientType=0); 117 | filter: progid:DXImageTransform.Microsoft.gradient(enabled=false); 118 | background-repeat: repeat-x; 119 | border: 1px solid #dcdcdc; 120 | } 121 | .btn-lg { 122 | padding: 0px 15px; 123 | height: 36px; 124 | line-height: 36px; 125 | font-size:15px; 126 | } 127 | .btn-xs { 128 | padding: 2px 6px; 129 | font-size: 11px; 130 | line-height: 1.25; 131 | border-radius: 1px; 132 | } 133 | .form-group { 134 | margin:0 0 10px 0; 135 | } 136 | @font-face { 137 | font-family: 'Glyphicons Halflings'; 138 | src: url('../fonts/glyphicons-halflings-regular.eot'); 139 | src: url('../fonts/glyphicons-halflings-regular.eot?#iefix') format('embedded-opentype'), url('../fonts/glyphicons-halflings-regular.woff2') format('woff2'), url('../fonts/glyphicons-halflings-regular.woff') format('woff'), url('../fonts/glyphicons-halflings-regular.ttf') format('truetype'), url('../fonts/glyphicons-halflings-regular.svg#glyphicons_halflingsregular') format('svg'); 140 | } 141 | .glyphicon { 142 | position: relative; 143 | top: 1px; 144 | display: inline-block; 145 | font-family: 'Glyphicons Halflings'; 146 | font-style: normal; 147 | font-weight: 400; 148 | line-height: 1; 149 | -webkit-font-smoothing: antialiased; 150 | -moz-osx-font-smoothing: grayscale; 151 | } 152 | .glyphicon-map-marker:before {content: "\e062";} 153 | .glyphicon-refresh:before {content: "\e031";} 154 | .glyphicon-chevron-down:before {content: "\e114";} 155 | .glyphicon-search:before {content: "\e003";} 156 | /* NAVBAR */ 157 | #navbar { 158 | display:none; 159 | margin:0; 160 | border-radius:0; 161 | background-color: #ffffe9; 162 | border-color:#ffffe9; 163 | height:56px; 164 | line-height:56px; 165 | padding:0 15px; 166 | position:relative; 167 | z-index:10; 168 | } 169 | #navbar h1 { 170 | display:inline-block; 171 | margin:0 10px 0 0; 172 | height:56px; 173 | line-height:56px; 174 | position:relative; 175 | top:-1px; 176 | } 177 | body.in-search #navbar { 178 | display:block; 179 | } 180 | #navbar h1 a { 181 | font-size:24px; 182 | text-decoration: none; 183 | } 184 | #navbar h1 span { 185 | font-weight:700; 186 | color:#3472ff; 187 | } 188 | #navbar h1 span + span {color:#f56b2a;} 189 | #navbar .input-url {text-align: left;} 190 | #navbar .navbar-extra { 191 | font-size:12px; 192 | color:#3472ff; 193 | } 194 | #geolocalize-info { 195 | max-width: 165px; 196 | white-space:nowrap; 197 | overflow: hidden; 198 | text-overflow: ellipsis; 199 | word-wrap: break-word; 200 | height: 56px; 201 | display:inline-block; 202 | vertical-align: middle; 203 | } 204 | #navbar h1 i {color:#f56b2a;} 205 | #navbar button {/*margin-left:10px;*/} 206 | h3 { 207 | font-size:15px; 208 | margin:0 0 10px; 209 | color:#444; 210 | font-weight:bold; 211 | } 212 | h4 { 213 | font-weight:bold; 214 | color:#888; 215 | margin:0 0 5px; 216 | } 217 | 218 | /* HOMEPAGE */ 219 | #container { 220 | position:absolute; 221 | z-index:10; 222 | width:800px; 223 | top:50%; 224 | left:50%; 225 | margin:-200px 0 0 -400px; 226 | padding:35px 20px 20px 20px; 227 | background:#ffffe9; 228 | box-shadow: 1px 1px 6px #555; 229 | border-radius:5px; 230 | } 231 | body.in-search #container { 232 | display:none; 233 | } 234 | .container-table { 235 | height:100%; 236 | display: table; 237 | } 238 | .vertical-center-row { 239 | display: table-cell; 240 | vertical-align: middle; 241 | } 242 | #logo { 243 | margin:0 0 25px 0; 244 | } 245 | .input-url { 246 | text-align:center; 247 | } 248 | .input-submit { 249 | font-weight: 300; 250 | } 251 | footer { 252 | z-index:10; 253 | position:fixed; 254 | bottom:0px; 255 | left:0px; 256 | right:0px; 257 | background:#ffffe9; 258 | border-top: 1px solid #d4d4d4; 259 | line-height: 55px; 260 | min-height: 55px; 261 | } 262 | /*body.in-search footer { 263 | display:none; 264 | }*/ 265 | 266 | footer a, footer a:link, footer a:visited, footer a:active, 267 | footer span { 268 | color: #696969; 269 | font-size:13px; 270 | text-decoration:none; 271 | line-height: 50px; 272 | } 273 | footer a:hover { 274 | text-decoration:underline; 275 | } 276 | footer a { 277 | padding-right:15px; 278 | padding-left:15px; 279 | } 280 | .rightlinks { 281 | float:right; 282 | padding-right:15px; 283 | padding-left:15px; 284 | } 285 | 286 | /* MAP */ 287 | #map { 288 | z-index:1; 289 | display:block; 290 | position:relative; 291 | height:calc(100vh - 56px); 292 | opacity:0.3; 293 | width:100%; 294 | } 295 | body.in-search #map { 296 | opacity:1; 297 | height:calc(100vh - 56px - 56px); 298 | } 299 | /* LOADER */ 300 | .glyphicon-spin { 301 | -webkit-animation: spin 1000ms infinite linear; 302 | animation: spin 1000ms infinite linear; 303 | } 304 | @-webkit-keyframes spin { 305 | 0% { 306 | -webkit-transform: rotate(0deg); 307 | transform: rotate(0deg); 308 | } 309 | 100% { 310 | -webkit-transform: rotate(359deg); 311 | transform: rotate(359deg); 312 | } 313 | } 314 | @keyframes spin { 315 | 0% { 316 | -webkit-transform: rotate(0deg); 317 | transform: rotate(0deg); 318 | } 319 | 100% { 320 | -webkit-transform: rotate(359deg); 321 | transform: rotate(359deg); 322 | } 323 | } 324 | 325 | /** PANEL */ 326 | #toggle { 327 | display:none; 328 | position:absolute; 329 | z-index:10; 330 | top:66px; 331 | left:480px; 332 | width:23px; 333 | height:48px; 334 | cursor:pointer; 335 | background:rgba(255,255,255,1) url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAUCAQAAAAXDMSnAAAAi0lEQVR4AX3JQcqBURQG4O/+9WNG30D3vOfSDTuQsgcZyBakZANSzMVMBme3zsBI5/VMn4ZKLP5ki1E4tYejWpilxVUtzOEUD68odYmXR5BJNp/4zllXD2phllYvamHmirsayUkfJ5ruHzueTldC08kcT5YOY9xYujqQM03XKXuaLmEtNF1e1Nz89gbL+0do6OEwRwAAAABJRU5ErkJggg==) 7px center/7px 10px no-repeat; 336 | border-left:1px solid #D4D4D4; 337 | } 338 | body.in-search #toggle {display:block;} 339 | body.in-search.toggle #toggle { 340 | -webkit-transform: translateX(-480px); 341 | transform: translateX(-480px) scaleX(-1); 342 | } 343 | #sidebar-wrapper { 344 | position: fixed; 345 | left:0; 346 | width:480px; 347 | height:calc(100vh - 56px - 56px); 348 | background: #FFF; 349 | display:none; 350 | box-shadow: 0 0 20px rgba(0,0,0,0.3); 351 | z-index:8; 352 | -webkit-transform: translateX(0px); 353 | } 354 | body.in-search #sidebar-wrapper {display:block;} 355 | body.in-search.toggle #sidebar-wrapper { 356 | /*margin-left:-480px;*/ 357 | -webkit-transform: translateX(-480px); 358 | transform: translateX(-480px); 359 | } 360 | #sidebar-wrapper, #toggle { 361 | transform: translateX(0px); 362 | transition-property: -webkit-transform,transform,opacity; 363 | transition-duration: 0.2s; 364 | transition-timing-function: cubic-bezier(0.0,0.0,0.2,1); 365 | } 366 | .sidebar-header { 367 | padding:10px 15px; 368 | border-bottom: 1px solid #ccc; 369 | padding-bottom:10px; 370 | } 371 | .sidebar-title { 372 | font-weight: bold; 373 | margin:0 0 5px 0; 374 | font-size:15px; 375 | line-height: 20px; 376 | text-overflow: ellipsis; 377 | white-space: nowrap; 378 | overflow:hidden; 379 | } 380 | .sidebar-details { 381 | display: block; 382 | font-size: 12px; 383 | line-height:12px; 384 | text-align: center; 385 | margin:8px 0 0; 386 | color: #b9afa4; 387 | } 388 | /* FILTERS */ 389 | .filter-item { 390 | font-size:12px; 391 | display:inline-block; 392 | width:216px; 393 | } 394 | .filter-item.disabled { 395 | color:#ccc; 396 | } 397 | .filter-item.disabled .filter-select-wrapper { 398 | background: #eee; 399 | } 400 | .filter-select-wrapper { 401 | height: 20px; 402 | border: 1px solid #ccc; 403 | border-radius: 4px; 404 | display: inline-block; 405 | width: 100px; 406 | position:relative; 407 | } 408 | .filter-day .filter-select-wrapper { 409 | width: 110px; 410 | } 411 | .filter-select-wrapper i { 412 | position:absolute; 413 | top:4px; 414 | right:5px; 415 | } 416 | .filter-select { 417 | position: relative; 418 | width: 100%; 419 | padding: 0 25px 0 15px; 420 | height: 19px; 421 | line-height: 19px; 422 | color: #1a1a1a; 423 | border: none; 424 | background: transparent; 425 | cursor: pointer; 426 | outline: none; 427 | z-index: 10; 428 | -webkit-appearance: none; 429 | -moz-appearance: none; 430 | -ms-appearance: none; 431 | display: inline-block; 432 | max-width: 100%; 433 | overflow: hidden; 434 | text-overflow: ellipsis; 435 | white-space: nowrap; 436 | word-wrap: normal; 437 | text-align: left; 438 | -webkit-touch-callout: none; 439 | -webkit-user-select: none; 440 | -khtml-user-select: none; 441 | -moz-user-select: none; 442 | -ms-user-select: none; 443 | user-select: none; 444 | } 445 | #sidebar { 446 | overflow-y: scroll; 447 | height: calc(100% - 86px); 448 | } 449 | .pwet:hover, 450 | .pwet.active { 451 | background-color:#e6e6e6; 452 | } 453 | .pwet:not(:last-child) { 454 | border-bottom: 1px solid #ccc; 455 | } 456 | .media { 457 | position: relative; 458 | display: block; 459 | padding:15px; 460 | -webkit-transition: background,0.3s; 461 | -moz-transition: background,0.3s; 462 | transition: background,0.3s; 463 | text-decoration:none; 464 | min-height:129px; 465 | } 466 | .media-heading a { 467 | text-decoration: underline; 468 | color:#3472ff; 469 | font-weight: normal; 470 | } 471 | .media-left { 472 | position:relative; 473 | text-align:center; 474 | width: 120px; 475 | margin:0 10px 0 0; 476 | display:inline-block; 477 | } 478 | .media-left .media-object { 479 | max-width:120px; 480 | max-height:90px; 481 | position:relative; 482 | top:10px; 483 | } 484 | .media-number { 485 | position: absolute; 486 | left: 0px; 487 | bottom: 0px; 488 | height: 20px; 489 | width: 20px; 490 | text-align: center; 491 | background: #fff; 492 | opacity: 0.8; 493 | line-height: 20px; 494 | color: #f56b2a; 495 | font-weight: bold; 496 | } 497 | .media-number.null{ 498 | display:none; 499 | } 500 | .media-body { 501 | display:inline-block; 502 | position: relative; 503 | height:99px; 504 | width:303px; 505 | vertical-align:top; 506 | } 507 | .media-heading { 508 | margin: 0 0 5px; 509 | font-size: 1rem; 510 | line-height: 1.2rem; 511 | } 512 | .media-supp { 513 | color: #1a1a1a; 514 | margin:0; 515 | line-height: 1.2rem; 516 | font-size:12px; 517 | } 518 | .media-supp.null { 519 | display:none; 520 | } 521 | .media-price { 522 | margin:0; 523 | font-size: 1rem; 524 | color: #f56b2a; 525 | font-weight: 700; 526 | position:absolute; 527 | bottom:0; 528 | } 529 | .media-date { 530 | position: absolute; 531 | bottom: 0px; 532 | right: 0; 533 | font-size:.65rem; 534 | } 535 | /* INFOBOX */ 536 | .trajet { 537 | line-height:15px; 538 | } 539 | .trajet p { 540 | 541 | } 542 | .trajet span { 543 | font-weight:bold; 544 | } 545 | #cookies-cnil-banner { 546 | background-color: #ffffe9; 547 | text-align: center; 548 | padding: 5px; 549 | border-bottom: 1px solid #d4d4d4; 550 | line-height:21px; 551 | } 552 | /* MODAL SWAL */ 553 | .sweet-alert a { 554 | color:#f56b2a; 555 | text-decoration: none; 556 | cursor: pointer; 557 | } 558 | .sweet-alert a:hover {text-decoration: underline;} 559 | .sweet-alert ul {list-style: none;} 560 | -------------------------------------------------------------------------------- /webroot/js/sweetalert.min.js: -------------------------------------------------------------------------------- 1 | !function(e,t,n){"use strict";!function o(e,t,n){function a(s,l){if(!t[s]){if(!e[s]){var i="function"==typeof require&&require;if(!l&&i)return i(s,!0);if(r)return r(s,!0);var u=new Error("Cannot find module '"+s+"'");throw u.code="MODULE_NOT_FOUND",u}var c=t[s]={exports:{}};e[s][0].call(c.exports,function(t){var n=e[s][1][t];return a(n?n:t)},c,c.exports,o,e,t,n)}return t[s].exports}for(var r="function"==typeof require&&require,s=0;s=0;)n=n.replace(" "+t+" "," ");e.className=n.replace(/^\s+|\s+$/g,"")}},i=function(e){var n=t.createElement("div");return n.appendChild(t.createTextNode(e)),n.innerHTML},u=function(e){e.style.opacity="",e.style.display="block"},c=function(e){if(e&&!e.length)return u(e);for(var t=0;t0?setTimeout(o,t):e.style.display="none"});o()},h=function(n){if("function"==typeof MouseEvent){var o=new MouseEvent("click",{view:e,bubbles:!1,cancelable:!0});n.dispatchEvent(o)}else if(t.createEvent){var a=t.createEvent("MouseEvents");a.initEvent("click",!1,!1),n.dispatchEvent(a)}else t.createEventObject?n.fireEvent("onclick"):"function"==typeof n.onclick&&n.onclick()},b=function(t){"function"==typeof t.stopPropagation?(t.stopPropagation(),t.preventDefault()):e.event&&e.event.hasOwnProperty("cancelBubble")&&(e.event.cancelBubble=!0)};a.hasClass=r,a.addClass=s,a.removeClass=l,a.escapeHtml=i,a._show=u,a.show=c,a._hide=d,a.hide=f,a.isDescendant=p,a.getTopMargin=m,a.fadeIn=v,a.fadeOut=y,a.fireClick=h,a.stopEventPropagation=b},{}],5:[function(t,o,a){Object.defineProperty(a,"__esModule",{value:!0});var r=t("./handle-dom"),s=t("./handle-swal-dom"),l=function(t,o,a){var l=t||e.event,i=l.keyCode||l.which,u=a.querySelector("button.confirm"),c=a.querySelector("button.cancel"),d=a.querySelectorAll("button[tabindex]");if(-1!==[9,13,32,27].indexOf(i)){for(var f=l.target||l.srcElement,p=-1,m=0;m"),i.innerHTML=e.html?e.text:s.escapeHtml(e.text||"").split("\n").join("
    "),e.text&&s.show(i),e.customClass)s.addClass(t,e.customClass),t.setAttribute("data-custom-class",e.customClass);else{var d=t.getAttribute("data-custom-class");s.removeClass(t,d),t.setAttribute("data-custom-class","")}if(s.hide(t.querySelectorAll(".sa-icon")),e.type&&!a.isIE8()){var f=function(){for(var o=!1,a=0;ao;o++)n=parseInt(e.substr(2*o,2),16),n=Math.round(Math.min(Math.max(0,n+n*t),255)).toString(16),a+=("00"+n).substr(n.length);return a};o.extend=a,o.hexToRgb=r,o.isIE8=s,o.logStr=l,o.colorLuminance=i},{}]},{},[1]),"function"==typeof define&&define.amd?define(function(){return sweetAlert}):"undefined"!=typeof module&&module.exports&&(module.exports=sweetAlert)}(window,document); -------------------------------------------------------------------------------- /webroot/js/mapicoin-map.js: -------------------------------------------------------------------------------- 1 | var map, geocoder, GeoMarker, GeoCircle, currentActiveMarker; 2 | var filterDistance = 0 3 | var filterTime = 0; 4 | var markers = []; 5 | var is_geolocated = false; 6 | var directionsDisplay; 7 | var directionsService = new google.maps.DirectionsService(); 8 | var infowindow = new google.maps.InfoWindow({ 9 | content: "" 10 | }); 11 | var iconDefault = { 12 | url: '//maps.google.com/mapfiles/ms/icons/red-dot.png' 13 | }; 14 | var iconActive = { 15 | url: '//maps.google.com/mapfiles/ms/icons/green-dot.png' 16 | }; 17 | var iconGps = { 18 | 'url': '/img/gpsloc.png', 19 | 'size': new google.maps.Size(34, 34), 20 | 'scaledSize': new google.maps.Size(17, 17), 21 | 'origin': new google.maps.Point(0, 0), 22 | 'anchor': new google.maps.Point(8, 8) 23 | }; 24 | 25 | 26 | /** 27 | * Init google map 28 | */ 29 | function initialize_map() { 30 | // console.log('function initialize_map() {'); 31 | // Center of France 32 | // var myLatLng = {lat: 47.351, lng: 3.392}; 33 | var element = document.getElementById('map'); 34 | // For direction calculation 35 | opts = { 36 | preserveViewport:true, 37 | suppressMarkers:true 38 | } 39 | directionsDisplay = new google.maps.DirectionsRenderer(opts); 40 | $('#map').show(); 41 | var map = new google.maps.Map(element, { 42 | center: centerMap, 43 | scrollwheel: true, 44 | zoom: 6, 45 | mapTypeControlOptions: { 46 | mapTypeIds: [google.maps.MapTypeId.ROADMAP] 47 | }, 48 | mapTypeControl: false, 49 | streetViewControl: false 50 | }); 51 | // Create the legend and display on the map 52 | // var legend = document.createElement('div'); 53 | // legend.id = 'legend'; 54 | // var content = []; 55 | // content.push('

    Légende

    '); 56 | // content.push('

    : annonce non visitée

    '); 57 | // content.push('

    : annonces multiples non visitées

    '); 58 | // content.push('

    : votre position (peut être changée)

    '); 59 | // legend.innerHTML = content.join(''); 60 | // legend.index = 1; 61 | // map.controls[google.maps.ControlPosition.RIGHT_TOP].push(legend); 62 | 63 | // This is needed to set the zoom after fitbounds, 64 | google.maps.event.addListener(map, 'zoom_changed', function() { 65 | zoomChangeBoundsListener = 66 | google.maps.event.addListener(map, 'bounds_changed', function(event) { 67 | if (this.getZoom() > 13/* && this.initialZoom == true*/) { 68 | // Change max/min zoom here 69 | this.setZoom(13); 70 | this.initialZoom = false; 71 | } 72 | // google.maps.event.removeListener(zoomChangeBoundsListener); 73 | }); 74 | }); 75 | map.initialZoom = true; 76 | 77 | //map loaded fully ? 78 | google.maps.event.addListenerOnce(map, 'idle', function(){ 79 | // Retrieve user address from cookie ? 80 | retrieve_user_address_from_cookies(); 81 | }); 82 | 83 | // In order to localize client 84 | geocoder = new google.maps.Geocoder(); 85 | 86 | // For distance 87 | directionsDisplay.setMap(map); 88 | 89 | return map; 90 | } 91 | 92 | 93 | /** 94 | * Add markers to the map 95 | */ 96 | function add_ads_markers(map, datas) { 97 | // console.log('function add_ads_markers(map, datas) {'); 98 | 99 | for (var i in datas) { 100 | 101 | var data = datas[i]; 102 | var ads = data.ads; 103 | 104 | var marker = new google.maps.Marker({ 105 | id: i, 106 | map: map, 107 | position: new google.maps.LatLng(data.latlng.lat, data.latlng.lng), 108 | icon: iconDefault, 109 | timestamp: data.timestamp 110 | }); 111 | 112 | // On click event (calculate distance) 113 | marker.addListener('click', function() { 114 | set_icon_markers(iconDefault); 115 | if (is_geolocated) { 116 | var trajet = calc_distance_to_marker(this); 117 | } 118 | this.setIcon(iconActive); 119 | currentActiveMarker = this; 120 | panel_highlight(this.id); 121 | // Open sidebar if mobile 122 | if (is_mobile()) { 123 | $('body').removeClass('toggle'); 124 | } 125 | }); 126 | 127 | marker.addListener('visible_changed', function() { 128 | panel_toggle_item(this.id, this.getVisible()); 129 | }) 130 | 131 | markers.push(marker); 132 | 133 | } 134 | 135 | return update_marker_from_filters(); 136 | } 137 | 138 | /** 139 | * Bind une tooltip sur les markeurs 140 | */ 141 | function bind_info_window(marker, text) { 142 | // console.log('function bind_info_window(marker, text) {'); 143 | infowindow.setContent(text); 144 | infowindow.open(map, marker); 145 | return map_fit_bounds([GeoMarker, marker]); 146 | } 147 | 148 | 149 | /** 150 | * Recalcul le zoom de la map pour que tous les markeurs soient visibles 151 | */ 152 | function map_fit_bounds(m) { 153 | // console.log('function map_fit_bounds(m) {'); 154 | // Si on ne précise pas de marqueurs, on prends l'ensemble des marqueurs visibles 155 | if (!m) { 156 | var m = []; 157 | for (var i = 0; i < markers.length; i++) { 158 | if (markers[i].getVisible()) { 159 | m.push(markers[i]); 160 | } 161 | } 162 | } 163 | if (m.length == 0) { 164 | return false; 165 | } 166 | var bounds = new google.maps.LatLngBounds(); 167 | for (var i in m) { 168 | bounds.extend(m[i].position); 169 | } 170 | 171 | map.setCenter(bounds.getCenter()); 172 | map.fitBounds(bounds); 173 | map.setZoom(map.getZoom()-1); 174 | 175 | if (!$('body').hasClass('toggle')) { 176 | return offsetCenter(map.getCenter(), 200, 0); 177 | } 178 | } 179 | 180 | 181 | /** 182 | * Remove all markers from the map 183 | */ 184 | function remove_markers() { 185 | // console.log('function remove_markers() {'); 186 | directionsDisplay.setMap(null); 187 | for (var i = 0; i < markers.length; i++) { 188 | markers[i].setMap(null); 189 | } 190 | markers = []; 191 | } 192 | 193 | /** 194 | * Défini une icône pour tous les markeurs 195 | */ 196 | function set_icon_markers(icon) { 197 | // console.log('function set_icon_markers(icon) {'); 198 | if (!icon) { 199 | icon = iconDefault; 200 | } 201 | for (var i = 0; i < markers.length; i++) { 202 | markers[i].setIcon(icon) 203 | } 204 | } 205 | 206 | /** 207 | * Re-calcul la distance vers le dernier marker actif 208 | */ 209 | function calc_distance_to_last_active_marker() { 210 | // console.log('function calc_distance_to_last_active_marker() {'); 211 | if (!currentActiveMarker) { 212 | return false; 213 | } 214 | return calc_distance_to_marker(currentActiveMarker); 215 | } 216 | 217 | /** 218 | * Calcul la distance de sa géoloc à un marker passé en paramètre 219 | */ 220 | function calc_distance_to_marker(marker) { 221 | // console.log('function calc_distance_to_marker(marker) {'); 222 | if (!is_geolocated) { 223 | return false; 224 | } 225 | var request = { 226 | origin: GeoMarker.getPosition(), 227 | destination: marker.getPosition(), 228 | travelMode: google.maps.TravelMode.DRIVING 229 | }; 230 | directionsDisplay.setMap(map); 231 | directionsService.route(request, function(result, status) { 232 | if (status == google.maps.DirectionsStatus.OK) { 233 | directionsDisplay.setDirections(result); 234 | var distance = result.routes[0].legs[0].distance.text; 235 | var time = result.routes[0].legs[0].duration.text; 236 | var trajet = '
    '+ 237 | '

    Distance : '+distance+'

    '+ 238 | '

    Temps : '+time+'

    '+ 239 | '
    '; 240 | bind_info_window(marker, trajet); 241 | } 242 | }); 243 | } 244 | 245 | /** 246 | * Mets à jour les markeurs suivant le cercle de distance 247 | */ 248 | function update_marker_from_filters() { 249 | // console.log('function update_marker_from_filters() {'); 250 | var currentTimestamp = Math.floor(Date.now() / 1000); 251 | var tooFar = false; 252 | var tooOld = true; 253 | for (var i = 0; i < markers.length; i++) { 254 | // Distance : 1 marker = plusieurs annonces avec même distance 255 | if (is_geolocated && GeoCircle && GeoCircle.getRadius() > 0) { 256 | var d = google.maps.geometry.spherical.computeDistanceBetween( 257 | markers[i].getPosition(), 258 | GeoMarker.getPosition() 259 | ); 260 | // true si marker en dehors du cercle 261 | var tooFar = (d > GeoCircle.getRadius()); 262 | } 263 | // Time : /!\ 1 marker = plusieurs annonces avec age différent /!\ 264 | // On cherche donc à ce qu'une annonce au moins respecte la condition, pour afficher le marker 265 | if (filterTime > 0) { 266 | var id = markers[i].id; 267 | // On parcours chaque annonce pour voir du marker pour voir si 1 match la condition 'day' 268 | tooOld = true; 269 | var $pwet = $('#sidebar .pwet[data-index="'+id+'"] > .media').each(function( i ) { 270 | var timestamp = $(this).data('timestamp'); 271 | // true si annonce assez récente 272 | if (timestamp > (currentTimestamp - filterTime)) { 273 | $(this).show(); 274 | tooOld = false; 275 | return; 276 | } else { 277 | // On cache l'annonce 278 | $(this).hide(); 279 | } 280 | }); 281 | } else { 282 | tooOld = false; 283 | } 284 | 285 | if (tooFar || tooOld) { 286 | markers[i].setVisible(false); 287 | } else { 288 | markers[i].setVisible(true); 289 | } 290 | } 291 | // Pour le time, on cache les li où il n'y a plus d'annonces 292 | if (filterTime > 0) { 293 | $('#sidebar .pwet').each(function(i) { 294 | if ($(this).find('.media:visible').length == 0) { 295 | $(this).hide(); 296 | } 297 | }) 298 | } 299 | // With lazyload, we need to force it (little bug) 300 | $(".lazyload").trigger('appear'); 301 | return panel_update_count(); 302 | } 303 | /** 304 | * Trace un cercle autour de la localisation GPS 305 | */ 306 | function draw_circle_around_user_location() { 307 | // console.log('function draw_circle_around_user_location() {'); 308 | if (!is_geolocated) { 309 | return false; 310 | } 311 | if (!GeoCircle) { 312 | GeoCircle = new google.maps.Circle({ 313 | // center: GeoMarker.getPosition(), 314 | // radius: kilometer * 1000, 315 | fillColor: "#0000FF", 316 | fillOpacity: 0.15, 317 | map: map, 318 | strokeColor: "#FFFFFF", 319 | strokeOpacity: 0.1, 320 | strokeWeight: 2 321 | }); 322 | } 323 | GeoCircle.setRadius(filterDistance); 324 | GeoCircle.setCenter(GeoMarker.getPosition()); 325 | if (filterDistance == 0) { 326 | GeoCircle.setVisible(false); 327 | } else { 328 | GeoCircle.setVisible(true); 329 | } 330 | return true; 331 | } 332 | /** 333 | * Défini la distance du cercle de recherche 334 | */ 335 | function set_user_distance(kilometer) { 336 | // console.log('function set_user_distance(kilometer) {'); 337 | if (kilometer > 0) { 338 | filterDistance = kilometer * 1000; 339 | } else { 340 | filterDistance = 0; 341 | } 342 | return draw_circle_around_user_location(); 343 | } 344 | /** 345 | * Défini le temps de recherche 346 | */ 347 | function set_user_day(nb_day) { 348 | // console.log('function set_user_day(nb_day) {'); 349 | if (nb_day > 0) { 350 | filterTime = nb_day * 86400; 351 | } else { 352 | filterTime = 0; 353 | } 354 | return true; 355 | } 356 | /** 357 | * Défini la position de l'utilisateur via un markeur 358 | */ 359 | function set_user_location(position) { 360 | var lat = position.coords.latitude; 361 | var lng = position.coords.longitude; 362 | // Remember position 363 | set_cookie('user_lat', lat, 30); 364 | set_cookie('user_lng', lng, 30); 365 | create_user_marker(lat, lng); 366 | get_address_from_latlng(lat, lng); 367 | 368 | custom_alert( 369 | "Position déterminée ! :-)", 370 | "Nous vous avons réussi à détecter votre position.
    "+ 371 | "
    " + 372 | "Vous pouvez désormais utiliser les fonctionnalités liées à la géolocalisation sur Mapicoin", 373 | "success", 374 | {html: true, confirmButtonText: "C'est parti !", timer: 4000} 375 | ); 376 | } 377 | 378 | /** 379 | * Fonction appelée si l'utilisateur refuse la localisation 380 | */ 381 | function user_denied_location() { 382 | // Pas de support, proposer une alternative ? 383 | custom_alert( 384 | "Position non déterminée :-(", 385 | "Votre navigateur ne semble pas supporter la géolocalisation automatique " + 386 | "ou vous avez refusé que l'on vous géolocalise.", 387 | "error", 388 | {html: true} 389 | ); 390 | create_user_marker(centerMap.lat, centerMap.lng); 391 | get_address_from_latlng(centerMap.lat, centerMap.lng); 392 | } 393 | 394 | function create_user_marker(lat, lng) { 395 | // On a déjà été géoloc au moins une fois avant, on update simplement que la position 396 | var pos = new google.maps.LatLng(lat, lng); 397 | if (GeoMarker) { 398 | GeoMarker.setPosition(pos); 399 | } else { 400 | // enable localization filter 401 | $('.filter-item.filter-distance') 402 | .removeClass('disabled') 403 | .removeAttr('title') 404 | .find('select') 405 | .removeAttr('disabled'); 406 | 407 | is_geolocated = true; 408 | var markerOpts = { 409 | 'map': map, 410 | 'cursor': 'pointer', 411 | 'draggable': true, 412 | 'flat': true, 413 | 'icon': iconGps, 414 | 'position': pos, 415 | 'title': "Votre position actuelle. Déplacez-moi si besoin !", 416 | 'zIndex': 2 417 | }; 418 | GeoMarker = new google.maps.Marker(markerOpts); 419 | GeoMarker.addListener('drag',function(event) { 420 | draw_circle_around_user_location(); 421 | // on-the-fly update is too slow ! 422 | // update_marker_from_filters(); 423 | }); 424 | GeoMarker.addListener('dragend',function(event) { 425 | calc_distance_to_last_active_marker(); 426 | update_marker_from_filters(); 427 | // Remember position 428 | set_cookie('user_lat', GeoMarker.getPosition().lat(), 30); 429 | set_cookie('user_lng', GeoMarker.getPosition().lng(), 30); 430 | get_address_from_latlng( 431 | GeoMarker.getPosition().lat(), 432 | GeoMarker.getPosition().lng() 433 | ); 434 | }); 435 | } 436 | // Draw blue circle 437 | draw_circle_around_user_location(); 438 | // Update markers 439 | update_marker_from_filters(); 440 | // Fit bounds 441 | map_fit_bounds(); 442 | } 443 | 444 | 445 | /** 446 | * Récupération de la position via le navigateur 447 | */ 448 | function get_user_location() { 449 | loader_user_location(true); 450 | if (navigator.geolocation) { 451 | navigator.geolocation.getCurrentPosition(set_user_location, user_denied_location); 452 | } else { 453 | user_denied_location(); 454 | } 455 | } 456 | 457 | function loader_user_location(status) { 458 | if (status) { 459 | $('#geolocalize-info').html($('#geolocalize-info').data('loader')); 460 | if (is_mobile()) { 461 | $('#geolocalize-me').html($('#geolocalize-me').data('loader')); 462 | } 463 | } else { 464 | if (is_mobile()) { 465 | $('#geolocalize-me').html($('#geolocalize-me').data('default')); 466 | } 467 | } 468 | } 469 | 470 | function offsetCenter(latlng, offsetx, offsety) { 471 | 472 | // latlng is the apparent centre-point 473 | // offsetx is the distance you want that point to move to the right, in pixels 474 | // offsety is the distance you want that point to move upwards, in pixels 475 | // offset can be negative 476 | // offsetx and offsety are both optional 477 | 478 | var scale = Math.pow(2, map.getZoom()); 479 | 480 | var worldCoordinateCenter = map.getProjection().fromLatLngToPoint(latlng); 481 | var pixelOffset = new google.maps.Point((offsetx/scale) || 0,(offsety/scale) ||0) 482 | 483 | var worldCoordinateNewCenter = new google.maps.Point( 484 | worldCoordinateCenter.x - pixelOffset.x, 485 | worldCoordinateCenter.y + pixelOffset.y 486 | ); 487 | 488 | var newCenter = map.getProjection().fromPointToLatLng(worldCoordinateNewCenter); 489 | 490 | map.setCenter(newCenter); 491 | 492 | } 493 | 494 | /** 495 | * Geocode des coordonnées en une adresse à partir de l'API Google 496 | */ 497 | function get_address_from_latlng(lat, lng) { 498 | // console.log(coords); 499 | var latlng = new google.maps.LatLng(lat, lng); 500 | $element = $('#geolocalize-info'); 501 | loader_user_location(true); 502 | // AJAX call 503 | geocoder.geocode({'latLng': latlng}, function(results, status) { 504 | loader_user_location(false); 505 | if (status != google.maps.GeocoderStatus.OK) { 506 | $element.html($element.data('default')); 507 | return false; 508 | } 509 | if (!results[1]) { 510 | $element.html($element.data('default')); 511 | return false; 512 | } 513 | // Remember position 514 | set_cookie('user_address', results[1].formatted_address, 30); 515 | set_user_address(results[1].formatted_address); 516 | }); 517 | } 518 | 519 | /** 520 | * Affiche l'adresse de l'utilisateur 521 | * address : Paris, France 522 | */ 523 | function set_user_address(address) { 524 | var text = address.split(','); 525 | $('#geolocalize-info').text(text[0]); 526 | $('#geolocalize-info').attr('title', address); 527 | } 528 | 529 | function retrieve_user_address_from_cookies() { 530 | if (!get_cookie('user_address') || !get_cookie('user_lat') || !get_cookie('user_lng')) { 531 | return false; 532 | } 533 | // Affichage de l'adresse 534 | set_user_address(get_cookie('user_address')); 535 | // Création / MAJ du marqueur 536 | create_user_marker(get_cookie('user_lat'), get_cookie('user_lng')); 537 | } 538 | -------------------------------------------------------------------------------- /webroot/css/bootstrap-theme.min.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * Bootstrap v3.3.6 (http://getbootstrap.com) 3 | * Copyright 2011-2015 Twitter, Inc. 4 | * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) 5 | */.btn-danger,.btn-default,.btn-info,.btn-primary,.btn-success,.btn-warning{text-shadow:0 -1px 0 rgba(0,0,0,.2);-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.15),0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 0 rgba(255,255,255,.15),0 1px 1px rgba(0,0,0,.075)}.btn-danger.active,.btn-danger:active,.btn-default.active,.btn-default:active,.btn-info.active,.btn-info:active,.btn-primary.active,.btn-primary:active,.btn-success.active,.btn-success:active,.btn-warning.active,.btn-warning:active{-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,.125);box-shadow:inset 0 3px 5px rgba(0,0,0,.125)}.btn-danger.disabled,.btn-danger[disabled],.btn-default.disabled,.btn-default[disabled],.btn-info.disabled,.btn-info[disabled],.btn-primary.disabled,.btn-primary[disabled],.btn-success.disabled,.btn-success[disabled],.btn-warning.disabled,.btn-warning[disabled],fieldset[disabled] .btn-danger,fieldset[disabled] .btn-default,fieldset[disabled] .btn-info,fieldset[disabled] .btn-primary,fieldset[disabled] .btn-success,fieldset[disabled] .btn-warning{-webkit-box-shadow:none;box-shadow:none}.btn-danger .badge,.btn-default .badge,.btn-info .badge,.btn-primary .badge,.btn-success .badge,.btn-warning .badge{text-shadow:none}.btn.active,.btn:active{background-image:none}.btn-default{text-shadow:0 1px 0 #fff;background-image:-webkit-linear-gradient(top,#fff 0,#e0e0e0 100%);background-image:-o-linear-gradient(top,#fff 0,#e0e0e0 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#fff),to(#e0e0e0));background-image:linear-gradient(to bottom,#fff 0,#e0e0e0 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#ffe0e0e0', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#dbdbdb;border-color:#ccc}.btn-default:focus,.btn-default:hover{background-color:#e0e0e0;background-position:0 -15px}.btn-default.active,.btn-default:active{background-color:#e0e0e0;border-color:#dbdbdb}.btn-default.disabled,.btn-default.disabled.active,.btn-default.disabled.focus,.btn-default.disabled:active,.btn-default.disabled:focus,.btn-default.disabled:hover,.btn-default[disabled],.btn-default[disabled].active,.btn-default[disabled].focus,.btn-default[disabled]:active,.btn-default[disabled]:focus,.btn-default[disabled]:hover,fieldset[disabled] .btn-default,fieldset[disabled] .btn-default.active,fieldset[disabled] .btn-default.focus,fieldset[disabled] .btn-default:active,fieldset[disabled] .btn-default:focus,fieldset[disabled] .btn-default:hover{background-color:#e0e0e0;background-image:none}.btn-primary{background-image:-webkit-linear-gradient(top,#337ab7 0,#265a88 100%);background-image:-o-linear-gradient(top,#337ab7 0,#265a88 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#337ab7),to(#265a88));background-image:linear-gradient(to bottom,#337ab7 0,#265a88 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff265a88', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#245580}.btn-primary:focus,.btn-primary:hover{background-color:#265a88;background-position:0 -15px}.btn-primary.active,.btn-primary:active{background-color:#265a88;border-color:#245580}.btn-primary.disabled,.btn-primary.disabled.active,.btn-primary.disabled.focus,.btn-primary.disabled:active,.btn-primary.disabled:focus,.btn-primary.disabled:hover,.btn-primary[disabled],.btn-primary[disabled].active,.btn-primary[disabled].focus,.btn-primary[disabled]:active,.btn-primary[disabled]:focus,.btn-primary[disabled]:hover,fieldset[disabled] .btn-primary,fieldset[disabled] .btn-primary.active,fieldset[disabled] .btn-primary.focus,fieldset[disabled] .btn-primary:active,fieldset[disabled] .btn-primary:focus,fieldset[disabled] .btn-primary:hover{background-color:#265a88;background-image:none}.btn-success{background-image:-webkit-linear-gradient(top,#5cb85c 0,#419641 100%);background-image:-o-linear-gradient(top,#5cb85c 0,#419641 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#5cb85c),to(#419641));background-image:linear-gradient(to bottom,#5cb85c 0,#419641 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff419641', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#3e8f3e}.btn-success:focus,.btn-success:hover{background-color:#419641;background-position:0 -15px}.btn-success.active,.btn-success:active{background-color:#419641;border-color:#3e8f3e}.btn-success.disabled,.btn-success.disabled.active,.btn-success.disabled.focus,.btn-success.disabled:active,.btn-success.disabled:focus,.btn-success.disabled:hover,.btn-success[disabled],.btn-success[disabled].active,.btn-success[disabled].focus,.btn-success[disabled]:active,.btn-success[disabled]:focus,.btn-success[disabled]:hover,fieldset[disabled] .btn-success,fieldset[disabled] .btn-success.active,fieldset[disabled] .btn-success.focus,fieldset[disabled] .btn-success:active,fieldset[disabled] .btn-success:focus,fieldset[disabled] .btn-success:hover{background-color:#419641;background-image:none}.btn-info{background-image:-webkit-linear-gradient(top,#5bc0de 0,#2aabd2 100%);background-image:-o-linear-gradient(top,#5bc0de 0,#2aabd2 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#5bc0de),to(#2aabd2));background-image:linear-gradient(to bottom,#5bc0de 0,#2aabd2 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff2aabd2', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#28a4c9}.btn-info:focus,.btn-info:hover{background-color:#2aabd2;background-position:0 -15px}.btn-info.active,.btn-info:active{background-color:#2aabd2;border-color:#28a4c9}.btn-info.disabled,.btn-info.disabled.active,.btn-info.disabled.focus,.btn-info.disabled:active,.btn-info.disabled:focus,.btn-info.disabled:hover,.btn-info[disabled],.btn-info[disabled].active,.btn-info[disabled].focus,.btn-info[disabled]:active,.btn-info[disabled]:focus,.btn-info[disabled]:hover,fieldset[disabled] .btn-info,fieldset[disabled] .btn-info.active,fieldset[disabled] .btn-info.focus,fieldset[disabled] .btn-info:active,fieldset[disabled] .btn-info:focus,fieldset[disabled] .btn-info:hover{background-color:#2aabd2;background-image:none}.btn-warning{background-image:-webkit-linear-gradient(top,#f0ad4e 0,#eb9316 100%);background-image:-o-linear-gradient(top,#f0ad4e 0,#eb9316 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f0ad4e),to(#eb9316));background-image:linear-gradient(to bottom,#f0ad4e 0,#eb9316 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffeb9316', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#e38d13}.btn-warning:focus,.btn-warning:hover{background-color:#eb9316;background-position:0 -15px}.btn-warning.active,.btn-warning:active{background-color:#eb9316;border-color:#e38d13}.btn-warning.disabled,.btn-warning.disabled.active,.btn-warning.disabled.focus,.btn-warning.disabled:active,.btn-warning.disabled:focus,.btn-warning.disabled:hover,.btn-warning[disabled],.btn-warning[disabled].active,.btn-warning[disabled].focus,.btn-warning[disabled]:active,.btn-warning[disabled]:focus,.btn-warning[disabled]:hover,fieldset[disabled] .btn-warning,fieldset[disabled] .btn-warning.active,fieldset[disabled] .btn-warning.focus,fieldset[disabled] .btn-warning:active,fieldset[disabled] .btn-warning:focus,fieldset[disabled] .btn-warning:hover{background-color:#eb9316;background-image:none}.btn-danger{background-image:-webkit-linear-gradient(top,#d9534f 0,#c12e2a 100%);background-image:-o-linear-gradient(top,#d9534f 0,#c12e2a 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#d9534f),to(#c12e2a));background-image:linear-gradient(to bottom,#d9534f 0,#c12e2a 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc12e2a', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#b92c28}.btn-danger:focus,.btn-danger:hover{background-color:#c12e2a;background-position:0 -15px}.btn-danger.active,.btn-danger:active{background-color:#c12e2a;border-color:#b92c28}.btn-danger.disabled,.btn-danger.disabled.active,.btn-danger.disabled.focus,.btn-danger.disabled:active,.btn-danger.disabled:focus,.btn-danger.disabled:hover,.btn-danger[disabled],.btn-danger[disabled].active,.btn-danger[disabled].focus,.btn-danger[disabled]:active,.btn-danger[disabled]:focus,.btn-danger[disabled]:hover,fieldset[disabled] .btn-danger,fieldset[disabled] .btn-danger.active,fieldset[disabled] .btn-danger.focus,fieldset[disabled] .btn-danger:active,fieldset[disabled] .btn-danger:focus,fieldset[disabled] .btn-danger:hover{background-color:#c12e2a;background-image:none}.img-thumbnail,.thumbnail{-webkit-box-shadow:0 1px 2px rgba(0,0,0,.075);box-shadow:0 1px 2px rgba(0,0,0,.075)}.dropdown-menu>li>a:focus,.dropdown-menu>li>a:hover{background-color:#e8e8e8;background-image:-webkit-linear-gradient(top,#f5f5f5 0,#e8e8e8 100%);background-image:-o-linear-gradient(top,#f5f5f5 0,#e8e8e8 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f5f5f5),to(#e8e8e8));background-image:linear-gradient(to bottom,#f5f5f5 0,#e8e8e8 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0);background-repeat:repeat-x}.dropdown-menu>.active>a,.dropdown-menu>.active>a:focus,.dropdown-menu>.active>a:hover{background-color:#2e6da4;background-image:-webkit-linear-gradient(top,#337ab7 0,#2e6da4 100%);background-image:-o-linear-gradient(top,#337ab7 0,#2e6da4 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#337ab7),to(#2e6da4));background-image:linear-gradient(to bottom,#337ab7 0,#2e6da4 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);background-repeat:repeat-x}.navbar-default{background-image:-webkit-linear-gradient(top,#fff 0,#f8f8f8 100%);background-image:-o-linear-gradient(top,#fff 0,#f8f8f8 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#fff),to(#f8f8f8));background-image:linear-gradient(to bottom,#fff 0,#f8f8f8 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#fff8f8f8', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-radius:4px;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.15),0 1px 5px rgba(0,0,0,.075);box-shadow:inset 0 1px 0 rgba(255,255,255,.15),0 1px 5px rgba(0,0,0,.075)}.navbar-default .navbar-nav>.active>a,.navbar-default .navbar-nav>.open>a{background-image:-webkit-linear-gradient(top,#dbdbdb 0,#e2e2e2 100%);background-image:-o-linear-gradient(top,#dbdbdb 0,#e2e2e2 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#dbdbdb),to(#e2e2e2));background-image:linear-gradient(to bottom,#dbdbdb 0,#e2e2e2 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdbdbdb', endColorstr='#ffe2e2e2', GradientType=0);background-repeat:repeat-x;-webkit-box-shadow:inset 0 3px 9px rgba(0,0,0,.075);box-shadow:inset 0 3px 9px rgba(0,0,0,.075)}.navbar-brand,.navbar-nav>li>a{text-shadow:0 1px 0 rgba(255,255,255,.25)}.navbar-inverse{background-image:-webkit-linear-gradient(top,#3c3c3c 0,#222 100%);background-image:-o-linear-gradient(top,#3c3c3c 0,#222 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#3c3c3c),to(#222));background-image:linear-gradient(to bottom,#3c3c3c 0,#222 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff3c3c3c', endColorstr='#ff222222', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-radius:4px}.navbar-inverse .navbar-nav>.active>a,.navbar-inverse .navbar-nav>.open>a{background-image:-webkit-linear-gradient(top,#080808 0,#0f0f0f 100%);background-image:-o-linear-gradient(top,#080808 0,#0f0f0f 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#080808),to(#0f0f0f));background-image:linear-gradient(to bottom,#080808 0,#0f0f0f 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff080808', endColorstr='#ff0f0f0f', GradientType=0);background-repeat:repeat-x;-webkit-box-shadow:inset 0 3px 9px rgba(0,0,0,.25);box-shadow:inset 0 3px 9px rgba(0,0,0,.25)}.navbar-inverse .navbar-brand,.navbar-inverse .navbar-nav>li>a{text-shadow:0 -1px 0 rgba(0,0,0,.25)}.navbar-fixed-bottom,.navbar-fixed-top,.navbar-static-top{border-radius:0}@media (max-width:767px){.navbar .navbar-nav .open .dropdown-menu>.active>a,.navbar .navbar-nav .open .dropdown-menu>.active>a:focus,.navbar .navbar-nav .open .dropdown-menu>.active>a:hover{color:#fff;background-image:-webkit-linear-gradient(top,#337ab7 0,#2e6da4 100%);background-image:-o-linear-gradient(top,#337ab7 0,#2e6da4 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#337ab7),to(#2e6da4));background-image:linear-gradient(to bottom,#337ab7 0,#2e6da4 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);background-repeat:repeat-x}}.alert{text-shadow:0 1px 0 rgba(255,255,255,.2);-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.25),0 1px 2px rgba(0,0,0,.05);box-shadow:inset 0 1px 0 rgba(255,255,255,.25),0 1px 2px rgba(0,0,0,.05)}.alert-success{background-image:-webkit-linear-gradient(top,#dff0d8 0,#c8e5bc 100%);background-image:-o-linear-gradient(top,#dff0d8 0,#c8e5bc 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#dff0d8),to(#c8e5bc));background-image:linear-gradient(to bottom,#dff0d8 0,#c8e5bc 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffc8e5bc', GradientType=0);background-repeat:repeat-x;border-color:#b2dba1}.alert-info{background-image:-webkit-linear-gradient(top,#d9edf7 0,#b9def0 100%);background-image:-o-linear-gradient(top,#d9edf7 0,#b9def0 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#d9edf7),to(#b9def0));background-image:linear-gradient(to bottom,#d9edf7 0,#b9def0 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffb9def0', GradientType=0);background-repeat:repeat-x;border-color:#9acfea}.alert-warning{background-image:-webkit-linear-gradient(top,#fcf8e3 0,#f8efc0 100%);background-image:-o-linear-gradient(top,#fcf8e3 0,#f8efc0 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#fcf8e3),to(#f8efc0));background-image:linear-gradient(to bottom,#fcf8e3 0,#f8efc0 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fff8efc0', GradientType=0);background-repeat:repeat-x;border-color:#f5e79e}.alert-danger{background-image:-webkit-linear-gradient(top,#f2dede 0,#e7c3c3 100%);background-image:-o-linear-gradient(top,#f2dede 0,#e7c3c3 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f2dede),to(#e7c3c3));background-image:linear-gradient(to bottom,#f2dede 0,#e7c3c3 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffe7c3c3', GradientType=0);background-repeat:repeat-x;border-color:#dca7a7}.progress{background-image:-webkit-linear-gradient(top,#ebebeb 0,#f5f5f5 100%);background-image:-o-linear-gradient(top,#ebebeb 0,#f5f5f5 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#ebebeb),to(#f5f5f5));background-image:linear-gradient(to bottom,#ebebeb 0,#f5f5f5 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffebebeb', endColorstr='#fff5f5f5', GradientType=0);background-repeat:repeat-x}.progress-bar{background-image:-webkit-linear-gradient(top,#337ab7 0,#286090 100%);background-image:-o-linear-gradient(top,#337ab7 0,#286090 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#337ab7),to(#286090));background-image:linear-gradient(to bottom,#337ab7 0,#286090 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff286090', GradientType=0);background-repeat:repeat-x}.progress-bar-success{background-image:-webkit-linear-gradient(top,#5cb85c 0,#449d44 100%);background-image:-o-linear-gradient(top,#5cb85c 0,#449d44 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#5cb85c),to(#449d44));background-image:linear-gradient(to bottom,#5cb85c 0,#449d44 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff449d44', GradientType=0);background-repeat:repeat-x}.progress-bar-info{background-image:-webkit-linear-gradient(top,#5bc0de 0,#31b0d5 100%);background-image:-o-linear-gradient(top,#5bc0de 0,#31b0d5 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#5bc0de),to(#31b0d5));background-image:linear-gradient(to bottom,#5bc0de 0,#31b0d5 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff31b0d5', GradientType=0);background-repeat:repeat-x}.progress-bar-warning{background-image:-webkit-linear-gradient(top,#f0ad4e 0,#ec971f 100%);background-image:-o-linear-gradient(top,#f0ad4e 0,#ec971f 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f0ad4e),to(#ec971f));background-image:linear-gradient(to bottom,#f0ad4e 0,#ec971f 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffec971f', GradientType=0);background-repeat:repeat-x}.progress-bar-danger{background-image:-webkit-linear-gradient(top,#d9534f 0,#c9302c 100%);background-image:-o-linear-gradient(top,#d9534f 0,#c9302c 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#d9534f),to(#c9302c));background-image:linear-gradient(to bottom,#d9534f 0,#c9302c 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc9302c', GradientType=0);background-repeat:repeat-x}.progress-bar-striped{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.list-group{border-radius:4px;-webkit-box-shadow:0 1px 2px rgba(0,0,0,.075);box-shadow:0 1px 2px rgba(0,0,0,.075)}.list-group-item.active,.list-group-item.active:focus,.list-group-item.active:hover{text-shadow:0 -1px 0 #286090;background-image:-webkit-linear-gradient(top,#337ab7 0,#2b669a 100%);background-image:-o-linear-gradient(top,#337ab7 0,#2b669a 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#337ab7),to(#2b669a));background-image:linear-gradient(to bottom,#337ab7 0,#2b669a 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2b669a', GradientType=0);background-repeat:repeat-x;border-color:#2b669a}.list-group-item.active .badge,.list-group-item.active:focus .badge,.list-group-item.active:hover .badge{text-shadow:none}.panel{-webkit-box-shadow:0 1px 2px rgba(0,0,0,.05);box-shadow:0 1px 2px rgba(0,0,0,.05)}.panel-default>.panel-heading{background-image:-webkit-linear-gradient(top,#f5f5f5 0,#e8e8e8 100%);background-image:-o-linear-gradient(top,#f5f5f5 0,#e8e8e8 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f5f5f5),to(#e8e8e8));background-image:linear-gradient(to bottom,#f5f5f5 0,#e8e8e8 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0);background-repeat:repeat-x}.panel-primary>.panel-heading{background-image:-webkit-linear-gradient(top,#337ab7 0,#2e6da4 100%);background-image:-o-linear-gradient(top,#337ab7 0,#2e6da4 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#337ab7),to(#2e6da4));background-image:linear-gradient(to bottom,#337ab7 0,#2e6da4 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);background-repeat:repeat-x}.panel-success>.panel-heading{background-image:-webkit-linear-gradient(top,#dff0d8 0,#d0e9c6 100%);background-image:-o-linear-gradient(top,#dff0d8 0,#d0e9c6 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#dff0d8),to(#d0e9c6));background-image:linear-gradient(to bottom,#dff0d8 0,#d0e9c6 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffd0e9c6', GradientType=0);background-repeat:repeat-x}.panel-info>.panel-heading{background-image:-webkit-linear-gradient(top,#d9edf7 0,#c4e3f3 100%);background-image:-o-linear-gradient(top,#d9edf7 0,#c4e3f3 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#d9edf7),to(#c4e3f3));background-image:linear-gradient(to bottom,#d9edf7 0,#c4e3f3 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffc4e3f3', GradientType=0);background-repeat:repeat-x}.panel-warning>.panel-heading{background-image:-webkit-linear-gradient(top,#fcf8e3 0,#faf2cc 100%);background-image:-o-linear-gradient(top,#fcf8e3 0,#faf2cc 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#fcf8e3),to(#faf2cc));background-image:linear-gradient(to bottom,#fcf8e3 0,#faf2cc 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fffaf2cc', GradientType=0);background-repeat:repeat-x}.panel-danger>.panel-heading{background-image:-webkit-linear-gradient(top,#f2dede 0,#ebcccc 100%);background-image:-o-linear-gradient(top,#f2dede 0,#ebcccc 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f2dede),to(#ebcccc));background-image:linear-gradient(to bottom,#f2dede 0,#ebcccc 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffebcccc', GradientType=0);background-repeat:repeat-x}.well{background-image:-webkit-linear-gradient(top,#e8e8e8 0,#f5f5f5 100%);background-image:-o-linear-gradient(top,#e8e8e8 0,#f5f5f5 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#e8e8e8),to(#f5f5f5));background-image:linear-gradient(to bottom,#e8e8e8 0,#f5f5f5 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffe8e8e8', endColorstr='#fff5f5f5', GradientType=0);background-repeat:repeat-x;border-color:#dcdcdc;-webkit-box-shadow:inset 0 1px 3px rgba(0,0,0,.05),0 1px 0 rgba(255,255,255,.1);box-shadow:inset 0 1px 3px rgba(0,0,0,.05),0 1px 0 rgba(255,255,255,.1)} 6 | /*# sourceMappingURL=bootstrap-theme.min.css.map */ -------------------------------------------------------------------------------- /webroot/css/sweetalert.css: -------------------------------------------------------------------------------- 1 | body.stop-scrolling { 2 | height: 100%; 3 | overflow: hidden; } 4 | 5 | .sweet-overlay { 6 | background-color: black; 7 | /* IE8 */ 8 | -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=40)"; 9 | /* IE8 */ 10 | background-color: rgba(0, 0, 0, 0.4); 11 | position: fixed; 12 | left: 0; 13 | right: 0; 14 | top: 0; 15 | bottom: 0; 16 | display: none; 17 | z-index: 10000; } 18 | 19 | .sweet-alert { 20 | background-color: white; 21 | font-family: 'Open Sans', 'Helvetica Neue', Helvetica, Arial, sans-serif; 22 | width: 478px; 23 | padding: 17px; 24 | border-radius: 5px; 25 | text-align: center; 26 | position: fixed; 27 | left: 50%; 28 | top: 50%; 29 | margin-left: -256px; 30 | margin-top: -200px; 31 | overflow: hidden; 32 | display: none; 33 | z-index: 99999; } 34 | @media all and (max-width: 540px) { 35 | .sweet-alert { 36 | width: auto; 37 | margin-left: 0; 38 | margin-right: 0; 39 | left: 15px; 40 | right: 15px; } } 41 | .sweet-alert h2 { 42 | color: #575757; 43 | font-size: 30px; 44 | text-align: center; 45 | font-weight: 600; 46 | text-transform: none; 47 | position: relative; 48 | margin: 25px 0; 49 | padding: 0; 50 | line-height: 40px; 51 | display: block; } 52 | .sweet-alert p { 53 | color: #797979; 54 | font-size: 16px; 55 | text-align: center; 56 | font-weight: 300; 57 | position: relative; 58 | text-align: inherit; 59 | float: none; 60 | margin: 0; 61 | padding: 0; 62 | line-height: normal; } 63 | .sweet-alert fieldset { 64 | border: none; 65 | position: relative; } 66 | .sweet-alert .sa-error-container { 67 | background-color: #f1f1f1; 68 | margin-left: -17px; 69 | margin-right: -17px; 70 | overflow: hidden; 71 | padding: 0 10px; 72 | max-height: 0; 73 | webkit-transition: padding 0.15s, max-height 0.15s; 74 | transition: padding 0.15s, max-height 0.15s; } 75 | .sweet-alert .sa-error-container.show { 76 | padding: 10px 0; 77 | max-height: 100px; 78 | webkit-transition: padding 0.2s, max-height 0.2s; 79 | transition: padding 0.25s, max-height 0.25s; } 80 | .sweet-alert .sa-error-container .icon { 81 | display: inline-block; 82 | width: 24px; 83 | height: 24px; 84 | border-radius: 50%; 85 | background-color: #ea7d7d; 86 | color: white; 87 | line-height: 24px; 88 | text-align: center; 89 | margin-right: 3px; } 90 | .sweet-alert .sa-error-container p { 91 | display: inline-block; } 92 | .sweet-alert .sa-input-error { 93 | position: absolute; 94 | top: 29px; 95 | right: 26px; 96 | width: 20px; 97 | height: 20px; 98 | opacity: 0; 99 | -webkit-transform: scale(0.5); 100 | transform: scale(0.5); 101 | -webkit-transform-origin: 50% 50%; 102 | transform-origin: 50% 50%; 103 | -webkit-transition: all 0.1s; 104 | transition: all 0.1s; } 105 | .sweet-alert .sa-input-error::before, .sweet-alert .sa-input-error::after { 106 | content: ""; 107 | width: 20px; 108 | height: 6px; 109 | background-color: #f06e57; 110 | border-radius: 3px; 111 | position: absolute; 112 | top: 50%; 113 | margin-top: -4px; 114 | left: 50%; 115 | margin-left: -9px; } 116 | .sweet-alert .sa-input-error::before { 117 | -webkit-transform: rotate(-45deg); 118 | transform: rotate(-45deg); } 119 | .sweet-alert .sa-input-error::after { 120 | -webkit-transform: rotate(45deg); 121 | transform: rotate(45deg); } 122 | .sweet-alert .sa-input-error.show { 123 | opacity: 1; 124 | -webkit-transform: scale(1); 125 | transform: scale(1); } 126 | .sweet-alert input { 127 | width: 100%; 128 | box-sizing: border-box; 129 | border-radius: 3px; 130 | border: 1px solid #d7d7d7; 131 | height: 43px; 132 | margin-top: 10px; 133 | margin-bottom: 17px; 134 | font-size: 18px; 135 | box-shadow: inset 0px 1px 1px rgba(0, 0, 0, 0.06); 136 | padding: 0 12px; 137 | display: none; 138 | -webkit-transition: all 0.3s; 139 | transition: all 0.3s; } 140 | .sweet-alert input:focus { 141 | outline: none; 142 | box-shadow: 0px 0px 3px #c4e6f5; 143 | border: 1px solid #b4dbed; } 144 | .sweet-alert input:focus::-moz-placeholder { 145 | transition: opacity 0.3s 0.03s ease; 146 | opacity: 0.5; } 147 | .sweet-alert input:focus:-ms-input-placeholder { 148 | transition: opacity 0.3s 0.03s ease; 149 | opacity: 0.5; } 150 | .sweet-alert input:focus::-webkit-input-placeholder { 151 | transition: opacity 0.3s 0.03s ease; 152 | opacity: 0.5; } 153 | .sweet-alert input::-moz-placeholder { 154 | color: #bdbdbd; } 155 | .sweet-alert input:-ms-input-placeholder { 156 | color: #bdbdbd; } 157 | .sweet-alert input::-webkit-input-placeholder { 158 | color: #bdbdbd; } 159 | .sweet-alert.show-input input { 160 | display: block; } 161 | .sweet-alert .sa-confirm-button-container { 162 | display: inline-block; 163 | position: relative; } 164 | .sweet-alert .la-ball-fall { 165 | position: absolute; 166 | left: 50%; 167 | top: 50%; 168 | margin-left: -27px; 169 | margin-top: 4px; 170 | opacity: 0; 171 | visibility: hidden; } 172 | .sweet-alert button { 173 | background-color: #8CD4F5; 174 | color: white; 175 | border: none; 176 | box-shadow: none; 177 | font-size: 17px; 178 | font-weight: 500; 179 | -webkit-border-radius: 4px; 180 | border-radius: 5px; 181 | padding: 10px 32px; 182 | margin: 26px 5px 0 5px; 183 | cursor: pointer; } 184 | .sweet-alert button:focus { 185 | outline: none; 186 | box-shadow: 0 0 2px rgba(128, 179, 235, 0.5), inset 0 0 0 1px rgba(0, 0, 0, 0.05); } 187 | .sweet-alert button:hover { 188 | background-color: #7ecff4; } 189 | .sweet-alert button:active { 190 | background-color: #5dc2f1; } 191 | .sweet-alert button.cancel { 192 | background-color: #C1C1C1; } 193 | .sweet-alert button.cancel:hover { 194 | background-color: #b9b9b9; } 195 | .sweet-alert button.cancel:active { 196 | background-color: #a8a8a8; } 197 | .sweet-alert button.cancel:focus { 198 | box-shadow: rgba(197, 205, 211, 0.8) 0px 0px 2px, rgba(0, 0, 0, 0.0470588) 0px 0px 0px 1px inset !important; } 199 | .sweet-alert button[disabled] { 200 | opacity: .6; 201 | cursor: default; } 202 | .sweet-alert button.confirm[disabled] { 203 | color: transparent; } 204 | .sweet-alert button.confirm[disabled] ~ .la-ball-fall { 205 | opacity: 1; 206 | visibility: visible; 207 | transition-delay: 0s; } 208 | .sweet-alert button::-moz-focus-inner { 209 | border: 0; } 210 | .sweet-alert[data-has-cancel-button=false] button { 211 | box-shadow: none !important; } 212 | .sweet-alert[data-has-confirm-button=false][data-has-cancel-button=false] { 213 | padding-bottom: 40px; } 214 | .sweet-alert .sa-icon { 215 | width: 80px; 216 | height: 80px; 217 | border: 4px solid gray; 218 | -webkit-border-radius: 40px; 219 | border-radius: 40px; 220 | border-radius: 50%; 221 | margin: 20px auto; 222 | padding: 0; 223 | position: relative; 224 | box-sizing: content-box; } 225 | .sweet-alert .sa-icon.sa-error { 226 | border-color: #F27474; } 227 | .sweet-alert .sa-icon.sa-error .sa-x-mark { 228 | position: relative; 229 | display: block; } 230 | .sweet-alert .sa-icon.sa-error .sa-line { 231 | position: absolute; 232 | height: 5px; 233 | width: 47px; 234 | background-color: #F27474; 235 | display: block; 236 | top: 37px; 237 | border-radius: 2px; } 238 | .sweet-alert .sa-icon.sa-error .sa-line.sa-left { 239 | -webkit-transform: rotate(45deg); 240 | transform: rotate(45deg); 241 | left: 17px; } 242 | .sweet-alert .sa-icon.sa-error .sa-line.sa-right { 243 | -webkit-transform: rotate(-45deg); 244 | transform: rotate(-45deg); 245 | right: 16px; } 246 | .sweet-alert .sa-icon.sa-warning { 247 | border-color: #F8BB86; } 248 | .sweet-alert .sa-icon.sa-warning .sa-body { 249 | position: absolute; 250 | width: 5px; 251 | height: 47px; 252 | left: 50%; 253 | top: 10px; 254 | -webkit-border-radius: 2px; 255 | border-radius: 2px; 256 | margin-left: -2px; 257 | background-color: #F8BB86; } 258 | .sweet-alert .sa-icon.sa-warning .sa-dot { 259 | position: absolute; 260 | width: 7px; 261 | height: 7px; 262 | -webkit-border-radius: 50%; 263 | border-radius: 50%; 264 | margin-left: -3px; 265 | left: 50%; 266 | bottom: 10px; 267 | background-color: #F8BB86; } 268 | .sweet-alert .sa-icon.sa-info { 269 | border-color: #C9DAE1; } 270 | .sweet-alert .sa-icon.sa-info::before { 271 | content: ""; 272 | position: absolute; 273 | width: 5px; 274 | height: 29px; 275 | left: 50%; 276 | bottom: 17px; 277 | border-radius: 2px; 278 | margin-left: -2px; 279 | background-color: #C9DAE1; } 280 | .sweet-alert .sa-icon.sa-info::after { 281 | content: ""; 282 | position: absolute; 283 | width: 7px; 284 | height: 7px; 285 | border-radius: 50%; 286 | margin-left: -3px; 287 | top: 19px; 288 | background-color: #C9DAE1; } 289 | .sweet-alert .sa-icon.sa-success { 290 | border-color: #A5DC86; } 291 | .sweet-alert .sa-icon.sa-success::before, .sweet-alert .sa-icon.sa-success::after { 292 | content: ''; 293 | -webkit-border-radius: 40px; 294 | border-radius: 40px; 295 | border-radius: 50%; 296 | position: absolute; 297 | width: 60px; 298 | height: 120px; 299 | background: white; 300 | -webkit-transform: rotate(45deg); 301 | transform: rotate(45deg); } 302 | .sweet-alert .sa-icon.sa-success::before { 303 | -webkit-border-radius: 120px 0 0 120px; 304 | border-radius: 120px 0 0 120px; 305 | top: -7px; 306 | left: -33px; 307 | -webkit-transform: rotate(-45deg); 308 | transform: rotate(-45deg); 309 | -webkit-transform-origin: 60px 60px; 310 | transform-origin: 60px 60px; } 311 | .sweet-alert .sa-icon.sa-success::after { 312 | -webkit-border-radius: 0 120px 120px 0; 313 | border-radius: 0 120px 120px 0; 314 | top: -11px; 315 | left: 30px; 316 | -webkit-transform: rotate(-45deg); 317 | transform: rotate(-45deg); 318 | -webkit-transform-origin: 0px 60px; 319 | transform-origin: 0px 60px; } 320 | .sweet-alert .sa-icon.sa-success .sa-placeholder { 321 | width: 80px; 322 | height: 80px; 323 | border: 4px solid rgba(165, 220, 134, 0.2); 324 | -webkit-border-radius: 40px; 325 | border-radius: 40px; 326 | border-radius: 50%; 327 | box-sizing: content-box; 328 | position: absolute; 329 | left: -4px; 330 | top: -4px; 331 | z-index: 2; } 332 | .sweet-alert .sa-icon.sa-success .sa-fix { 333 | width: 5px; 334 | height: 90px; 335 | background-color: white; 336 | position: absolute; 337 | left: 28px; 338 | top: 8px; 339 | z-index: 1; 340 | -webkit-transform: rotate(-45deg); 341 | transform: rotate(-45deg); } 342 | .sweet-alert .sa-icon.sa-success .sa-line { 343 | height: 5px; 344 | background-color: #A5DC86; 345 | display: block; 346 | border-radius: 2px; 347 | position: absolute; 348 | z-index: 2; } 349 | .sweet-alert .sa-icon.sa-success .sa-line.sa-tip { 350 | width: 25px; 351 | left: 14px; 352 | top: 46px; 353 | -webkit-transform: rotate(45deg); 354 | transform: rotate(45deg); } 355 | .sweet-alert .sa-icon.sa-success .sa-line.sa-long { 356 | width: 47px; 357 | right: 8px; 358 | top: 38px; 359 | -webkit-transform: rotate(-45deg); 360 | transform: rotate(-45deg); } 361 | .sweet-alert .sa-icon.sa-custom { 362 | background-size: contain; 363 | border-radius: 0; 364 | border: none; 365 | background-position: center center; 366 | background-repeat: no-repeat; } 367 | 368 | /* 369 | * Animations 370 | */ 371 | @-webkit-keyframes showSweetAlert { 372 | 0% { 373 | transform: scale(0.7); 374 | -webkit-transform: scale(0.7); } 375 | 45% { 376 | transform: scale(1.05); 377 | -webkit-transform: scale(1.05); } 378 | 80% { 379 | transform: scale(0.95); 380 | -webkit-transform: scale(0.95); } 381 | 100% { 382 | transform: scale(1); 383 | -webkit-transform: scale(1); } } 384 | 385 | @keyframes showSweetAlert { 386 | 0% { 387 | transform: scale(0.7); 388 | -webkit-transform: scale(0.7); } 389 | 45% { 390 | transform: scale(1.05); 391 | -webkit-transform: scale(1.05); } 392 | 80% { 393 | transform: scale(0.95); 394 | -webkit-transform: scale(0.95); } 395 | 100% { 396 | transform: scale(1); 397 | -webkit-transform: scale(1); } } 398 | 399 | @-webkit-keyframes hideSweetAlert { 400 | 0% { 401 | transform: scale(1); 402 | -webkit-transform: scale(1); } 403 | 100% { 404 | transform: scale(0.5); 405 | -webkit-transform: scale(0.5); } } 406 | 407 | @keyframes hideSweetAlert { 408 | 0% { 409 | transform: scale(1); 410 | -webkit-transform: scale(1); } 411 | 100% { 412 | transform: scale(0.5); 413 | -webkit-transform: scale(0.5); } } 414 | 415 | @-webkit-keyframes slideFromTop { 416 | 0% { 417 | top: 0%; } 418 | 100% { 419 | top: 50%; } } 420 | 421 | @keyframes slideFromTop { 422 | 0% { 423 | top: 0%; } 424 | 100% { 425 | top: 50%; } } 426 | 427 | @-webkit-keyframes slideToTop { 428 | 0% { 429 | top: 50%; } 430 | 100% { 431 | top: 0%; } } 432 | 433 | @keyframes slideToTop { 434 | 0% { 435 | top: 50%; } 436 | 100% { 437 | top: 0%; } } 438 | 439 | @-webkit-keyframes slideFromBottom { 440 | 0% { 441 | top: 70%; } 442 | 100% { 443 | top: 50%; } } 444 | 445 | @keyframes slideFromBottom { 446 | 0% { 447 | top: 70%; } 448 | 100% { 449 | top: 50%; } } 450 | 451 | @-webkit-keyframes slideToBottom { 452 | 0% { 453 | top: 50%; } 454 | 100% { 455 | top: 70%; } } 456 | 457 | @keyframes slideToBottom { 458 | 0% { 459 | top: 50%; } 460 | 100% { 461 | top: 70%; } } 462 | 463 | .showSweetAlert[data-animation=pop] { 464 | -webkit-animation: showSweetAlert 0.3s; 465 | animation: showSweetAlert 0.3s; } 466 | 467 | .showSweetAlert[data-animation=none] { 468 | -webkit-animation: none; 469 | animation: none; } 470 | 471 | .showSweetAlert[data-animation=slide-from-top] { 472 | -webkit-animation: slideFromTop 0.3s; 473 | animation: slideFromTop 0.3s; } 474 | 475 | .showSweetAlert[data-animation=slide-from-bottom] { 476 | -webkit-animation: slideFromBottom 0.3s; 477 | animation: slideFromBottom 0.3s; } 478 | 479 | .hideSweetAlert[data-animation=pop] { 480 | -webkit-animation: hideSweetAlert 0.2s; 481 | animation: hideSweetAlert 0.2s; } 482 | 483 | .hideSweetAlert[data-animation=none] { 484 | -webkit-animation: none; 485 | animation: none; } 486 | 487 | .hideSweetAlert[data-animation=slide-from-top] { 488 | -webkit-animation: slideToTop 0.4s; 489 | animation: slideToTop 0.4s; } 490 | 491 | .hideSweetAlert[data-animation=slide-from-bottom] { 492 | -webkit-animation: slideToBottom 0.3s; 493 | animation: slideToBottom 0.3s; } 494 | 495 | @-webkit-keyframes animateSuccessTip { 496 | 0% { 497 | width: 0; 498 | left: 1px; 499 | top: 19px; } 500 | 54% { 501 | width: 0; 502 | left: 1px; 503 | top: 19px; } 504 | 70% { 505 | width: 50px; 506 | left: -8px; 507 | top: 37px; } 508 | 84% { 509 | width: 17px; 510 | left: 21px; 511 | top: 48px; } 512 | 100% { 513 | width: 25px; 514 | left: 14px; 515 | top: 45px; } } 516 | 517 | @keyframes animateSuccessTip { 518 | 0% { 519 | width: 0; 520 | left: 1px; 521 | top: 19px; } 522 | 54% { 523 | width: 0; 524 | left: 1px; 525 | top: 19px; } 526 | 70% { 527 | width: 50px; 528 | left: -8px; 529 | top: 37px; } 530 | 84% { 531 | width: 17px; 532 | left: 21px; 533 | top: 48px; } 534 | 100% { 535 | width: 25px; 536 | left: 14px; 537 | top: 45px; } } 538 | 539 | @-webkit-keyframes animateSuccessLong { 540 | 0% { 541 | width: 0; 542 | right: 46px; 543 | top: 54px; } 544 | 65% { 545 | width: 0; 546 | right: 46px; 547 | top: 54px; } 548 | 84% { 549 | width: 55px; 550 | right: 0px; 551 | top: 35px; } 552 | 100% { 553 | width: 47px; 554 | right: 8px; 555 | top: 38px; } } 556 | 557 | @keyframes animateSuccessLong { 558 | 0% { 559 | width: 0; 560 | right: 46px; 561 | top: 54px; } 562 | 65% { 563 | width: 0; 564 | right: 46px; 565 | top: 54px; } 566 | 84% { 567 | width: 55px; 568 | right: 0px; 569 | top: 35px; } 570 | 100% { 571 | width: 47px; 572 | right: 8px; 573 | top: 38px; } } 574 | 575 | @-webkit-keyframes rotatePlaceholder { 576 | 0% { 577 | transform: rotate(-45deg); 578 | -webkit-transform: rotate(-45deg); } 579 | 5% { 580 | transform: rotate(-45deg); 581 | -webkit-transform: rotate(-45deg); } 582 | 12% { 583 | transform: rotate(-405deg); 584 | -webkit-transform: rotate(-405deg); } 585 | 100% { 586 | transform: rotate(-405deg); 587 | -webkit-transform: rotate(-405deg); } } 588 | 589 | @keyframes rotatePlaceholder { 590 | 0% { 591 | transform: rotate(-45deg); 592 | -webkit-transform: rotate(-45deg); } 593 | 5% { 594 | transform: rotate(-45deg); 595 | -webkit-transform: rotate(-45deg); } 596 | 12% { 597 | transform: rotate(-405deg); 598 | -webkit-transform: rotate(-405deg); } 599 | 100% { 600 | transform: rotate(-405deg); 601 | -webkit-transform: rotate(-405deg); } } 602 | 603 | .animateSuccessTip { 604 | -webkit-animation: animateSuccessTip 0.75s; 605 | animation: animateSuccessTip 0.75s; } 606 | 607 | .animateSuccessLong { 608 | -webkit-animation: animateSuccessLong 0.75s; 609 | animation: animateSuccessLong 0.75s; } 610 | 611 | .sa-icon.sa-success.animate::after { 612 | -webkit-animation: rotatePlaceholder 4.25s ease-in; 613 | animation: rotatePlaceholder 4.25s ease-in; } 614 | 615 | @-webkit-keyframes animateErrorIcon { 616 | 0% { 617 | transform: rotateX(100deg); 618 | -webkit-transform: rotateX(100deg); 619 | opacity: 0; } 620 | 100% { 621 | transform: rotateX(0deg); 622 | -webkit-transform: rotateX(0deg); 623 | opacity: 1; } } 624 | 625 | @keyframes animateErrorIcon { 626 | 0% { 627 | transform: rotateX(100deg); 628 | -webkit-transform: rotateX(100deg); 629 | opacity: 0; } 630 | 100% { 631 | transform: rotateX(0deg); 632 | -webkit-transform: rotateX(0deg); 633 | opacity: 1; } } 634 | 635 | .animateErrorIcon { 636 | -webkit-animation: animateErrorIcon 0.5s; 637 | animation: animateErrorIcon 0.5s; } 638 | 639 | @-webkit-keyframes animateXMark { 640 | 0% { 641 | transform: scale(0.4); 642 | -webkit-transform: scale(0.4); 643 | margin-top: 26px; 644 | opacity: 0; } 645 | 50% { 646 | transform: scale(0.4); 647 | -webkit-transform: scale(0.4); 648 | margin-top: 26px; 649 | opacity: 0; } 650 | 80% { 651 | transform: scale(1.15); 652 | -webkit-transform: scale(1.15); 653 | margin-top: -6px; } 654 | 100% { 655 | transform: scale(1); 656 | -webkit-transform: scale(1); 657 | margin-top: 0; 658 | opacity: 1; } } 659 | 660 | @keyframes animateXMark { 661 | 0% { 662 | transform: scale(0.4); 663 | -webkit-transform: scale(0.4); 664 | margin-top: 26px; 665 | opacity: 0; } 666 | 50% { 667 | transform: scale(0.4); 668 | -webkit-transform: scale(0.4); 669 | margin-top: 26px; 670 | opacity: 0; } 671 | 80% { 672 | transform: scale(1.15); 673 | -webkit-transform: scale(1.15); 674 | margin-top: -6px; } 675 | 100% { 676 | transform: scale(1); 677 | -webkit-transform: scale(1); 678 | margin-top: 0; 679 | opacity: 1; } } 680 | 681 | .animateXMark { 682 | -webkit-animation: animateXMark 0.5s; 683 | animation: animateXMark 0.5s; } 684 | 685 | @-webkit-keyframes pulseWarning { 686 | 0% { 687 | border-color: #F8D486; } 688 | 100% { 689 | border-color: #F8BB86; } } 690 | 691 | @keyframes pulseWarning { 692 | 0% { 693 | border-color: #F8D486; } 694 | 100% { 695 | border-color: #F8BB86; } } 696 | 697 | .pulseWarning { 698 | -webkit-animation: pulseWarning 0.75s infinite alternate; 699 | animation: pulseWarning 0.75s infinite alternate; } 700 | 701 | @-webkit-keyframes pulseWarningIns { 702 | 0% { 703 | background-color: #F8D486; } 704 | 100% { 705 | background-color: #F8BB86; } } 706 | 707 | @keyframes pulseWarningIns { 708 | 0% { 709 | background-color: #F8D486; } 710 | 100% { 711 | background-color: #F8BB86; } } 712 | 713 | .pulseWarningIns { 714 | -webkit-animation: pulseWarningIns 0.75s infinite alternate; 715 | animation: pulseWarningIns 0.75s infinite alternate; } 716 | 717 | @-webkit-keyframes rotate-loading { 718 | 0% { 719 | transform: rotate(0deg); } 720 | 100% { 721 | transform: rotate(360deg); } } 722 | 723 | @keyframes rotate-loading { 724 | 0% { 725 | transform: rotate(0deg); } 726 | 100% { 727 | transform: rotate(360deg); } } 728 | 729 | /* Internet Explorer 9 has some special quirks that are fixed here */ 730 | /* The icons are not animated. */ 731 | /* This file is automatically merged into sweet-alert.min.js through Gulp */ 732 | /* Error icon */ 733 | .sweet-alert .sa-icon.sa-error .sa-line.sa-left { 734 | -ms-transform: rotate(45deg) \9; } 735 | 736 | .sweet-alert .sa-icon.sa-error .sa-line.sa-right { 737 | -ms-transform: rotate(-45deg) \9; } 738 | 739 | /* Success icon */ 740 | .sweet-alert .sa-icon.sa-success { 741 | border-color: transparent\9; } 742 | 743 | .sweet-alert .sa-icon.sa-success .sa-line.sa-tip { 744 | -ms-transform: rotate(45deg) \9; } 745 | 746 | .sweet-alert .sa-icon.sa-success .sa-line.sa-long { 747 | -ms-transform: rotate(-45deg) \9; } 748 | 749 | /*! 750 | * Load Awesome v1.1.0 (http://github.danielcardoso.net/load-awesome/) 751 | * Copyright 2015 Daniel Cardoso <@DanielCardoso> 752 | * Licensed under MIT 753 | */ 754 | .la-ball-fall, 755 | .la-ball-fall > div { 756 | position: relative; 757 | -webkit-box-sizing: border-box; 758 | -moz-box-sizing: border-box; 759 | box-sizing: border-box; } 760 | 761 | .la-ball-fall { 762 | display: block; 763 | font-size: 0; 764 | color: #fff; } 765 | 766 | .la-ball-fall.la-dark { 767 | color: #333; } 768 | 769 | .la-ball-fall > div { 770 | display: inline-block; 771 | float: none; 772 | background-color: currentColor; 773 | border: 0 solid currentColor; } 774 | 775 | .la-ball-fall { 776 | width: 54px; 777 | height: 18px; } 778 | 779 | .la-ball-fall > div { 780 | width: 10px; 781 | height: 10px; 782 | margin: 4px; 783 | border-radius: 100%; 784 | opacity: 0; 785 | -webkit-animation: ball-fall 1s ease-in-out infinite; 786 | -moz-animation: ball-fall 1s ease-in-out infinite; 787 | -o-animation: ball-fall 1s ease-in-out infinite; 788 | animation: ball-fall 1s ease-in-out infinite; } 789 | 790 | .la-ball-fall > div:nth-child(1) { 791 | -webkit-animation-delay: -200ms; 792 | -moz-animation-delay: -200ms; 793 | -o-animation-delay: -200ms; 794 | animation-delay: -200ms; } 795 | 796 | .la-ball-fall > div:nth-child(2) { 797 | -webkit-animation-delay: -100ms; 798 | -moz-animation-delay: -100ms; 799 | -o-animation-delay: -100ms; 800 | animation-delay: -100ms; } 801 | 802 | .la-ball-fall > div:nth-child(3) { 803 | -webkit-animation-delay: 0ms; 804 | -moz-animation-delay: 0ms; 805 | -o-animation-delay: 0ms; 806 | animation-delay: 0ms; } 807 | 808 | .la-ball-fall.la-sm { 809 | width: 26px; 810 | height: 8px; } 811 | 812 | .la-ball-fall.la-sm > div { 813 | width: 4px; 814 | height: 4px; 815 | margin: 2px; } 816 | 817 | .la-ball-fall.la-2x { 818 | width: 108px; 819 | height: 36px; } 820 | 821 | .la-ball-fall.la-2x > div { 822 | width: 20px; 823 | height: 20px; 824 | margin: 8px; } 825 | 826 | .la-ball-fall.la-3x { 827 | width: 162px; 828 | height: 54px; } 829 | 830 | .la-ball-fall.la-3x > div { 831 | width: 30px; 832 | height: 30px; 833 | margin: 12px; } 834 | 835 | /* 836 | * Animation 837 | */ 838 | @-webkit-keyframes ball-fall { 839 | 0% { 840 | opacity: 0; 841 | -webkit-transform: translateY(-145%); 842 | transform: translateY(-145%); } 843 | 10% { 844 | opacity: .5; } 845 | 20% { 846 | opacity: 1; 847 | -webkit-transform: translateY(0); 848 | transform: translateY(0); } 849 | 80% { 850 | opacity: 1; 851 | -webkit-transform: translateY(0); 852 | transform: translateY(0); } 853 | 90% { 854 | opacity: .5; } 855 | 100% { 856 | opacity: 0; 857 | -webkit-transform: translateY(145%); 858 | transform: translateY(145%); } } 859 | 860 | @-moz-keyframes ball-fall { 861 | 0% { 862 | opacity: 0; 863 | -moz-transform: translateY(-145%); 864 | transform: translateY(-145%); } 865 | 10% { 866 | opacity: .5; } 867 | 20% { 868 | opacity: 1; 869 | -moz-transform: translateY(0); 870 | transform: translateY(0); } 871 | 80% { 872 | opacity: 1; 873 | -moz-transform: translateY(0); 874 | transform: translateY(0); } 875 | 90% { 876 | opacity: .5; } 877 | 100% { 878 | opacity: 0; 879 | -moz-transform: translateY(145%); 880 | transform: translateY(145%); } } 881 | 882 | @-o-keyframes ball-fall { 883 | 0% { 884 | opacity: 0; 885 | -o-transform: translateY(-145%); 886 | transform: translateY(-145%); } 887 | 10% { 888 | opacity: .5; } 889 | 20% { 890 | opacity: 1; 891 | -o-transform: translateY(0); 892 | transform: translateY(0); } 893 | 80% { 894 | opacity: 1; 895 | -o-transform: translateY(0); 896 | transform: translateY(0); } 897 | 90% { 898 | opacity: .5; } 899 | 100% { 900 | opacity: 0; 901 | -o-transform: translateY(145%); 902 | transform: translateY(145%); } } 903 | 904 | @keyframes ball-fall { 905 | 0% { 906 | opacity: 0; 907 | -webkit-transform: translateY(-145%); 908 | -moz-transform: translateY(-145%); 909 | -o-transform: translateY(-145%); 910 | transform: translateY(-145%); } 911 | 10% { 912 | opacity: .5; } 913 | 20% { 914 | opacity: 1; 915 | -webkit-transform: translateY(0); 916 | -moz-transform: translateY(0); 917 | -o-transform: translateY(0); 918 | transform: translateY(0); } 919 | 80% { 920 | opacity: 1; 921 | -webkit-transform: translateY(0); 922 | -moz-transform: translateY(0); 923 | -o-transform: translateY(0); 924 | transform: translateY(0); } 925 | 90% { 926 | opacity: .5; } 927 | 100% { 928 | opacity: 0; 929 | -webkit-transform: translateY(145%); 930 | -moz-transform: translateY(145%); 931 | -o-transform: translateY(145%); 932 | transform: translateY(145%); } } 933 | --------------------------------------------------------------------------------