1
0
Prechádzať zdrojové kódy

Merge remote-tracking branch 'upstream/master'

pauder 9 rokov pred
rodič
commit
368b73f5d7
92 zmenil súbory, kde vykonal 6530 pridanie a 132 odobranie
  1. 6 0
      .gitignore
  2. 61 20
      README.md
  3. 25 0
      UNLICENSE
  4. 44 0
      bridges/ABCTabsBridge.php
  5. 52 0
      bridges/AcrimedBridge.php
  6. 55 0
      bridges/AllocineFRBridge.php
  7. 55 0
      bridges/AllocineT5Bridge.php
  8. 55 0
      bridges/AllocineTueursEnSerieBridge.php
  9. 78 0
      bridges/Arte7deBridge.php
  10. 78 0
      bridges/Arte7frBridge.php
  11. 48 0
      bridges/BandcampBridge.php
  12. 51 0
      bridges/BastaBridge.php
  13. 48 0
      bridges/BlaguesDeMerdeBridge.php
  14. 55 0
      bridges/BooruprojectBridge.php
  15. 56 0
      bridges/CoinDeskBridge.php
  16. 41 0
      bridges/CollegeDeFranceBridge.php
  17. 59 0
      bridges/CopieDoubleBridge.php
  18. 6 2
      bridges/CryptomeBridge.php
  19. 81 0
      bridges/DailymotionBridge.php
  20. 48 0
      bridges/DanbooruBridge.php
  21. 41 0
      bridges/DansTonChatBridge.php
  22. 55 0
      bridges/DauphineLibereBridge.php
  23. 75 0
      bridges/DeveloppezDotComBridge.php
  24. 45 0
      bridges/DilbertBridge.php
  25. 50 0
      bridges/DollbooruBridge.php
  26. 42 0
      bridges/DuckDuckGoBridge.php
  27. 60 0
      bridges/FSBridge.php
  28. 4 1
      bridges/FlickrExploreBridge.php
  29. 53 0
      bridges/FlickrTagBridge.php
  30. 58 0
      bridges/FootitoBridge.php
  31. 52 0
      bridges/GelbooruBridge.php
  32. 57 0
      bridges/GizmodoFRBridge.php
  33. 125 0
      bridges/GooglePlusPostBridge.php
  34. 4 1
      bridges/GoogleSearchBridge.php
  35. 57 0
      bridges/GuruMedBridge.php
  36. 62 0
      bridges/HumbleStoreDiscountBridge.php
  37. 3 0
      bridges/IdenticaBridge.php
  38. 3 0
      bridges/InstagramBridge.php
  39. 54 0
      bridges/KonachanBridge.php
  40. 56 0
      bridges/KoreusBridge.php
  41. 61 0
      bridges/LeBonCoinBridge.php
  42. 69 0
      bridges/LeJournalDuGeekBridge.php
  43. 52 0
      bridges/LeMotDuJourBridge.php
  44. 58 0
      bridges/LesJoiesDuCodeBridge.php
  45. 60 0
      bridges/MalikiBridge.php
  46. 54 0
      bridges/MemoLinuxBridge.php
  47. 50 0
      bridges/MilbooruBridge.php
  48. 62 0
      bridges/MondeDiploBridge.php
  49. 55 0
      bridges/MsnMondeBridge.php
  50. 52 0
      bridges/MspabooruBridge.php
  51. 56 0
      bridges/NasaApodBridge.php
  52. 56 0
      bridges/NextInpactBridge.php
  53. 62 0
      bridges/NiceMatinBridge.php
  54. 56 0
      bridges/NumeramaBridge.php
  55. 47 0
      bridges/OpenClassroomsBridge.php
  56. 59 0
      bridges/OpenTheoryBridge.php
  57. 15 1
      bridges/PinterestBridge.php
  58. 45 0
      bridges/PlanetLibreBridge.php
  59. 43 0
      bridges/ProjectMGameBridge.php
  60. 52 0
      bridges/RaymondBridge.php
  61. 52 0
      bridges/Rule34Bridge.php
  62. 50 0
      bridges/Rule34pahealBridge.php
  63. 52 0
      bridges/SafebooruBridge.php
  64. 54 0
      bridges/SakugabooruBridge.php
  65. 54 0
      bridges/ScilogsBridge.php
  66. 52 0
      bridges/ScmbBridge.php
  67. 48 0
      bridges/ScoopItBridge.php
  68. 56 0
      bridges/SegfaultMintBridge.php
  69. 91 0
      bridges/Sexactu.php
  70. 55 0
      bridges/SoundcloudBridge.php
  71. 50 0
      bridges/TagBoardBridge.php
  72. 52 0
      bridges/TbibBridge.php
  73. 58 0
      bridges/TheCodingLoveBridge.php
  74. 90 0
      bridges/ThePirateBayBridge.php
  75. 62 0
      bridges/TuxboardBridge.php
  76. 20 5
      bridges/TwitterBridge.php
  77. 78 0
      bridges/TwitterBridgeExtended.php
  78. 54 0
      bridges/WakkuwakkuBridge.php
  79. 66 0
      bridges/WhydBridge.php
  80. 44 0
      bridges/WikipediaENBridge.php
  81. 44 0
      bridges/WikipediaEOBridge.php
  82. 42 0
      bridges/WikipediaFRBridge.php
  83. 95 0
      bridges/WordPressBridge.php
  84. 52 0
      bridges/XbooruBridge.php
  85. 54 0
      bridges/YandereBridge.php
  86. 121 34
      bridges/YoutubeBridge.php
  87. 16 2
      css/style.css
  88. 13 6
      formats/AtomFormat.php
  89. 37 15
      formats/HtmlFormat.php
  90. 152 43
      index.php
  91. 2 2
      lib/Bridge.php
  92. 1742 0
      vendor/simplehtmldom/simple_html_dom.php

+ 6 - 0
.gitignore

@@ -166,6 +166,11 @@ UpgradeLog*.htm
 App_Data/*.mdf
 App_Data/*.ldf
 
+#################
+## Other ide stuff
+#################
+.idea/*
+
 #############
 ## Windows detritus
 #############
@@ -219,3 +224,4 @@ pip-log.txt
 ## RSS-Bridge
 ##############
 /cache
+/whitelist.txt

+ 61 - 20
README.md

@@ -1,18 +1,31 @@
 rss-bridge
 ===
 
-rss-bridge is a php script capable of generating ATOM feed for specific pages which don't have one.
+rss-bridge is a PHP project capable of generating ATOM feeds for websites which don't have one.
 
-Supported sites/pages
+Supported sites/pages (main)
 ===
 
- * `FlickrExplore` : [Latest interesting images](http://www.flickr.com/explore) from Flickr.
- * `GoogleSearch` : Most recent results from Google Search.
- * `Twitter` : Can return keyword/hashtag search or user timeline.
- * `Identi.ca` : Identica user timeline (Should be compatible with other Pump.io instances).
- * `YouTube` : YouTube user channel feed.
- * `Cryptome` : Returns the most recent documents from Cryptome.org.
-
+ * `FlickrExplore` : [Latest interesting images](http://www.flickr.com/explore) from Flickr
+ * `GoogleSearch` : Most recent results from Google Search
+ * `GooglePlus` : Most recent posts of user timeline
+ * `Twitter` : Return keyword/hashtag search or user timeline
+ * `Identi.ca` : Identica user timeline (Should be compatible with other Pump.io instances)
+ * `YouTube` : YouTube user channel, playlist or search
+ * `Cryptome` : Returns the most recent documents from [Cryptome.org](http://cryptome.org/)
+ * `DansTonChat`: Most recent quotes from [danstonchat.com](http://danstonchat.com/)
+ * `DuckDuckGo`: Most recent results from [DuckDuckGo.com](https://duckduckgo.com/)
+ * `Instagram`: Most recent photos from an Instagram user
+ * `OpenClassrooms`: Lastest tutorials from [fr.openclassrooms.com](http://fr.openclassrooms.com/)
+ * `Pinterest`: Most recent photos from user or search
+ * `ScmbBridge`: Newest stories from [secouchermoinsbete.fr](http://secouchermoinsbete.fr/)
+ * `WikipediaENLatest`: highlighted articles from Wikipedia in English
+ * `WikipediaFRLatest`: highlighted articles from Wikipedia in French
+ * `WikipediaEOLatest`: highlighted articles from Wikipedia in Esperanto
+ * `Bandcamp` : Returns last release from [bandcamp](https://bandcamp.com/) for a tag
+ * `ThePirateBay` : Returns the newest indexed torrents from [The Pirate Bay](https://thepiratebay.se/) with keywords
+
+Plus [many other bridges](bridges/) to enable, thanks to the community
 
 Output format
 ===
@@ -37,10 +50,17 @@ Minecraft hashtag (#Minecraft) search on Twitter, in ATOM format (as displayed b
 Requirements
 ===
 
- * php 5.3
- * [PHP Simple HTML DOM Parser](http://simplehtmldom.sourceforge.net). (Put `simple_html_dom.php` in `vendor/simplehtmldom/`).
- * Ssl lib activated in PHP config
+ * PHP 5.3
+ * `openssl` extension enabled in PHP config (`php.ini`)
+
+Enabling/Disabling bridges
+===
+
+By default, the script creates `whitelist.txt` and adds the main bridges (see above). `whitelist.txt` is ignored by git, you can edit it:
+ * to enable extra bridges (one bridge per line)
+ * to disable main bridges (remove the line)
 
+New bridges are disabled by default, so make sure to check regularly what's new and whitelist what you want !
  
 Author
 ===
@@ -49,19 +69,40 @@ I'm sebsauvage, webmaster of [sebsauvage.net](http://sebsauvage.net), author of
 Patch/contributors :
 
  * Yves ASTIER ([Draeli](https://github.com/Draeli)) : PHP optimizations, fixes, dynamic brigde/format list with all stuff behind and extend cache system. Mail : contact@yves-astier.com
- * [Mitsukarenai](https://github.com/Mitsukarenai) : Initial inspiration, TwitterBridge, IdenticaBridge, YoutubeBridge. 
+ * [Mitsukarenai](https://github.com/Mitsukarenai) : Initial inspiration, collaborator
  * [ArthurHoaro](https://github.com/ArthurHoaro)
  * [BoboTiG](https://github.com/BoboTiG)
-
-Licence
+ * [Astalaseven](https://github.com/Astalaseven)
+ * [qwertygc](https://github.com/qwertygc)
+ * [Djuuu](https://github.com/Djuuu)
+ * [Anadrark](https://github.com/Anadrark])
+ * [Grummfy](https://github.com/Grummfy)
+ * [Polopollo](https://github.com/Polopollo)
+ * [16mhz](https://github.com/16mhz)
+ * [kranack](https://github.com/kranack)
+
+License
 ===
-Code is public domain.
+Code is [Public Domain](UNLICENSE).
+
+Including `PHP Simple HTML DOM Parser` under the [MIT License](http://opensource.org/licenses/MIT)
 
 
 Technical notes
 ===
   * There is a cache so that source services won't ban you even if you hammer the rss-bridge with requests. Each bridge has a different duration for the cache. The `cache` subdirectory will be automatically created. You can purge it whenever you want.
-  * To implement a new rss-bridge, create a new class in `bridges` subdirectory. Look at existing bridges for examples. For items you generate in `$this->items`, only `uri` and `title` are mandatory in each item. `timestamp` and `content` are optional but recommended. Any additional key will be ignored by ATOM feed (but outputed to jSon).
+  * To implement a new rss-bridge, create a new class in `bridges` subdirectory. Look at existing bridges for examples and the guidelines below. For items you generate in `$this->items`, only `uri` and `title` are mandatory in each item. `timestamp` and `content` are optional but recommended. Any additional key will be ignored by ATOM feed (but outputed to json).
+
+### Bridge guidelines
+
+  * metatags: `@name` {Name of service}, `@homepage` {URL to homepage}, `@description`, `@update` {YYYY-MM-DD}, `@maintainer` {Github username or nickname}
+  * scripts (eg. Javascript) must be stripped out. Make good use of `strip_tags()` and `preg_replace()`
+  * bridge must present data within 8 seconds (adjust iterators accordingly)
+  * cache timeout must be fine-tuned so that each refresh can provide 1 or 2 new elements on busy periods
+  * `<audio>` and `<video>` must not autoplay. Seriously.
+  * do everything you can to extract valid timestamps. Translate formats, use API, exploit sitemap, whatever. Free the data!
+  * don't create duplicates. If the website runs on WordPress, use the generic WordPress bridge if possible.
+  * maintain efficient and well-commented code :wink:
 
 Rant
 ===
@@ -70,10 +111,10 @@ Rant
 
 Your catchword is "share", but you don't want us to share. You want to keep us within your walled gardens. That's why you've been removing RSS links from webpages, hiding them deep on your website, or removed RSS entirely, replacing it with crippled or demented proprietary API. **FUCK YOU.**
 
-You're not social when you hamper sharing by removing RSS. You're happy to have customers create content for your ecosystem, but you don't want this content out - a content you do not even own. Google Takeout is just a gimmick. We want our data to flow, we want RSS.
+You're not social when you hamper sharing by removing RSS. You're happy to have customers creating content for your ecosystem, but you don't want this content out - a content you do not even own. Google Takeout is just a gimmick. We want our data to flow, we want RSS.
 
-We want to share with friends, using open protocols: RSS, XMPP, whatever. Because no one wants to have *your* service with *your* applications using *your* API forced-feeded to them. Friends must be free to choose whatever software and service they want.
+We want to share with friends, using open protocols: RSS, XMPP, whatever. Because no one wants to have *your* service with *your* applications using *your* API force-feeding them. Friends must be free to choose whatever software and service they want.
 
-We are rebuilding bridges your have wilfully destroyed.
+We are rebuilding bridges you have wilfully destroyed.
 
 Get your shit together: Put RSS back in.

+ 25 - 0
UNLICENSE

@@ -0,0 +1,25 @@
+This is free and unencumbered software released into the public domain.
+
+Anyone is free to copy, modify, publish, use, compile, sell, or
+distribute this software, either in source code form or as a compiled
+binary, for any purpose, commercial or non-commercial, and by any
+means.
+
+In jurisdictions that recognize copyright laws, the author or authors
+of this software dedicate any and all copyright interest in the
+software to the public domain. We make this dedication for the benefit
+of the public at large and to the detriment of our heirs and
+successors. We intend this dedication to be an overt act of
+relinquishment in perpetuity of all present and future rights to this
+software under copyright law.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
+
+For more information, please refer to <http://unlicense.org>
+

+ 44 - 0
bridges/ABCTabsBridge.php

@@ -0,0 +1,44 @@
+<?php
+/**
+* ABCTabsBridge
+* Returns the newest tabs
+*
+* @name ABC Tabs Bridge
+* @homepage http://www.abc-tabs.com/
+* @description Returns 22 newest tabs
+* @maintainer kranack
+* @update 2014-07-23
+*
+*/
+class ABCTabsBridge extends BridgeAbstract{
+    
+	private $request;
+    
+	public function collectData(array $param){
+		$html = '';
+        $html = file_get_html('http://www.abc-tabs.com/tablatures/nouveautes.html') or $this->returnError('No results for this query.', 404);
+		$table = $html->find('table#myTable', 0)->children(1);
+		
+		foreach ($table->find('tr') as $tab)
+		{
+		    $item = new \Item();
+		    $item->name = $tab->find('td', 1)->plaintext . ' - ' . $tab->find('td', 2)->plaintext;
+		    $item->title = $tab->find('td', 1)->plaintext . ' - ' . $tab->find('td', 2)->plaintext;
+		    $item->content = 'Le ' . $tab->find('td', 0)->plaintext . '<br> Par: ' . $tab->find('td', 5)->plaintext . '<br> Type: ' . $tab->find('td', 3)->plaintext;
+		    $item->id = 'http://www.abc-tabs.com' . $tab->find('td', 2)->find('a', 0)->getAttribute('href');
+		    $item->uri = 'http://www.abc-tabs.com' . $tab->find('td', 2)->find('a', 0)->getAttribute('href');
+		    $this->items[] = $item;
+		}
+    }
+	public function getName(){
+		return 'ABC Tabs Bridge';
+	}
+
+	public function getURI(){
+		return 'http://www.abc-tabs.com/';
+	}
+
+	public function getCacheDuration(){
+		return 3600; // 1 hour
+	}
+}

+ 52 - 0
bridges/AcrimedBridge.php

@@ -0,0 +1,52 @@
+<?php
+/**
+* 2014-05-25
+* @name Acrimed Bridge
+* @homepage http://www.acrimed.org/
+* @description Returns the newest articles.
+* @maintainer qwertygc
+*/
+class AcrimedBridge extends BridgeAbstract{
+    
+        public function collectData(array $param){
+
+			function StripCDATA($string) {
+			$string = str_replace('<![CDATA[', '', $string);
+			$string = str_replace(']]>', '', $string);
+			return $string;
+		}
+		function ExtractContent($url) {
+		$html2 = file_get_html($url);
+		$text = $html2->find('div.texte', 0)->innertext;
+		return $text;
+		}
+		$html = file_get_html('http://www.acrimed.org/spip.php?page=backend') or $this->returnError('Could not request Acrimed.', 404);
+		$limit = 0;
+
+		foreach($html->find('item') as $element) {
+		 if($limit < 10) {
+		 $item = new \Item();
+		 $item->title = StripCDATA($element->find('title', 0)->innertext);
+		 $item->uri = StripCDATA($element->find('guid', 0)->plaintext);
+		 $item->timestamp = strtotime($element->find('pubDate', 0)->plaintext);
+		 $item->content = ExtractContent($item->uri);
+		 $this->items[] = $item;
+		 $limit++;
+		 }
+		}
+    
+    }
+
+    public function getName(){
+        return 'Acrimed Bridge';
+    }
+
+    public function getURI(){
+        return 'http://acrimed.org/';
+    }
+
+    public function getCacheDuration(){
+        return 3600*2; // 2 hours
+        // return 0; // 2 hours
+    }
+}

+ 55 - 0
bridges/AllocineFRBridge.php

@@ -0,0 +1,55 @@
+<?php
+/**
+*
+* @name Allo Cine : Faux Raccord
+* @homepage http://www.allocine.fr/video/programme-12284/saison-24580/
+* @description Allo Cine : Faux Raccord
+* @update 07/11/2013
+* initial maintainer: superbaillot.net
+*/
+class AllocineFRBridge extends BridgeAbstract{
+
+    private $_URL = "http://www.allocine.fr/video/programme-12284/saison-24580/";
+    private $_NOM = "Faux Raccord";
+    
+    public function collectData(array $param){
+        $html = file_get_html($this->_URL) or $this->returnError('Could not request Allo cine.', 404);
+
+        foreach($html->find('figure.media-meta-fig') as $element)
+        {
+            $item = new Item();
+            
+            $titre = $element->find('div.titlebar h3.title a', 0);
+            $content = trim($element->innertext);
+            
+            $figCaption = strpos($content, $this->_NOM);
+            if($figCaption !== false)
+            {
+                $content = str_replace('src="/', 'src="http://www.allocine.fr/',$content);
+                $content = str_replace('href="/', 'href="http://www.allocine.fr/',$content);
+                $content = str_replace('src=\'/', 'src=\'http://www.allocine.fr/',$content);
+                $content = str_replace('href=\'/', 'href=\'http://www.allocine.fr/',$content);
+                $item->content = $content;
+                $item->title = trim($titre->innertext);
+                $item->uri = "http://www.allocine.fr" . $titre->href;
+                $this->items[] = $item;
+            }
+        }
+    }
+
+    public function getName(){
+        return 'Allo Cine : ' . $this->_NOM;
+    }
+
+    public function getURI(){
+        return $this->_URL;
+    }
+
+    public function getCacheDuration(){
+        return 25200; // 7 hours
+    }
+    public function getDescription(){
+        return "Allo Cine : " . $this->_NOM . " via rss-bridge";
+    }
+}
+?>

+ 55 - 0
bridges/AllocineT5Bridge.php

@@ -0,0 +1,55 @@
+<?php
+/**
+*
+* @name Allo Cine : Top 5
+* @homepage http://www.allocine.fr/video/programme-12299/saison-22542/
+* @description Allo Cine : Top 5 via rss-bridge
+* @update 07/11/2013
+* initial maintainer: superbaillot.net
+*/
+class AllocineT5Bridge extends BridgeAbstract{
+
+    private $_URL = "http://www.allocine.fr/video/programme-12299/saison-22542/";
+    private $_NOM = "Top 5";
+    
+    public function collectData(array $param){
+        $html = file_get_html($this->_URL) or $this->returnError('Could not request Allo cine.', 404);
+
+        foreach($html->find('figure.media-meta-fig') as $element)
+        {
+            $item = new Item();
+            
+            $titre = $element->find('div.titlebar h3.title a', 0);
+            $content = trim($element->innertext);
+            
+            $figCaption = strpos($content, $this->_NOM);
+            if($figCaption !== false)
+            {
+                $content = str_replace('src="/', 'src="http://www.allocine.fr/',$content);
+                $content = str_replace('href="/', 'href="http://www.allocine.fr/',$content);
+                $content = str_replace('src=\'/', 'src=\'http://www.allocine.fr/',$content);
+                $content = str_replace('href=\'/', 'href=\'http://www.allocine.fr/',$content);
+                $item->content = $content;
+                $item->title = trim($titre->innertext);
+                $item->uri = "http://www.allocine.fr" . $titre->href;
+                $this->items[] = $item;
+            }
+        }
+    }
+
+    public function getName(){
+        return 'Allo Cine : ' . $this->_NOM;
+    }
+
+    public function getURI(){
+        return $this->_URL;
+    }
+
+    public function getCacheDuration(){
+        return 25200; // 7 hours
+    }
+    public function getDescription(){
+        return "Allo Cine : " . $this->_NOM . " via rss-bridge";
+    }
+}
+?>

+ 55 - 0
bridges/AllocineTueursEnSerieBridge.php

@@ -0,0 +1,55 @@
+<?php
+/**
+*
+* @name Allo Cine : Tueurs En Serie
+* @homepage http://www.allocine.fr/video/programme-12286/saison-22938/
+* @description Allo Cine : Tueurs En Serie
+* @update 12/11/2013
+* initial maintainer: superbaillot.net
+*/
+class AllocineTueursEnSerieBridge extends BridgeAbstract{
+
+    private $_URL = "http://www.allocine.fr/video/programme-12286/saison-22938/";
+    private $_NOM = "Tueurs en Séries";
+    
+    public function collectData(array $param){
+        $html = file_get_html($this->_URL) or $this->returnError('Could not request Allo cine.', 404);
+
+        foreach($html->find('figure.media-meta-fig') as $element)
+        {
+            $item = new Item();
+            
+            $titre = $element->find('div.titlebar h3.title a', 0);
+            $content = trim($element->innertext);
+            
+            $figCaption = strpos($content, $this->_NOM);
+            if($figCaption !== false)
+            {
+                $content = str_replace('src="/', 'src="http://www.allocine.fr/',$content);
+                $content = str_replace('href="/', 'href="http://www.allocine.fr/',$content);
+                $content = str_replace('src=\'/', 'src=\'http://www.allocine.fr/',$content);
+                $content = str_replace('href=\'/', 'href=\'http://www.allocine.fr/',$content);
+                $item->content = $content;
+                $item->title = trim($titre->innertext);
+                $item->uri = "http://www.allocine.fr" . $titre->href;
+                $this->items[] = $item;
+            }
+        }
+    }
+
+    public function getName(){
+        return 'Allo Cine : ' . $this->_NOM;
+    }
+
+    public function getURI(){
+        return $this->_URL;
+    }
+
+    public function getCacheDuration(){
+        return 25200; // 7 hours
+    }
+    public function getDescription(){
+        return "Allo Cine : " . $this->_NOM . " via rss-bridge";
+    }
+}
+?>

+ 78 - 0
bridges/Arte7deBridge.php

@@ -0,0 +1,78 @@
+<?php
+/**
+* RssBridgeArte7de
+* Returns images from given page and tags
+* 2014-05-25
+*
+* @name Arte +7 DE
+* @homepage http://www.arte.tv/guide/de/
+* @description Returns newest videos from ARTE +7 (german)
+* @maintainer mitsukarenai
+*/
+class Arte7deBridge extends BridgeAbstract{
+
+    public function collectData(array $param){
+
+	$input_json = json_decode(file_get_contents('http://www.arte.tv/guide/de/plus7.json'), TRUE) or $this->returnError('Could not request ARTE.', 404);
+   
+        foreach($input_json['videos'] as $element) {
+            $item = new \Item();
+            $item->uri = 'http://www.arte.tv'.$element['url'];
+            $item->postid = $item->uri;
+
+				$date = $element['airdate_long'];
+				$date = explode(' ', $date);
+				$day = (int)$date['1'];
+				$month=FALSE;
+				switch ($date['2']) {
+					case 'Januar':
+						$month=1;break;
+					case 'Februar':
+						$month=2;break;
+					case 'März':
+						$month=3;break;
+					case 'April':
+						$month=4;break;
+					case 'Mai':
+						$month=5;break;
+					case 'Juni':
+						$month=6;break;
+					case 'Juli':
+						$month=7;break;
+					case 'August':
+						$month=8;break;
+					case 'September':
+						$month=9;break;
+					case 'Oktober':
+						$month=10;break;
+					case 'November':
+						$month=11;break;
+					case 'Dezember':
+						$month=12;break;
+					}
+				$year=(int)date('Y');
+				$heure=explode(':', $date['4']);
+				$hour=(int)$heure['0'];
+				$minute=(int)$heure['1'];
+  
+
+            $item->timestamp = mktime($hour, $minute, 0, $month, $day, $year);
+            $item->thumbnailUri = $element['image_url'];
+            $item->title = $element['title'];
+            $item->content = $element['desc'].'<br><br>'.$element['video_channels'].', '.$element['duration'].'min<br><img src="' . $item->thumbnailUri . '" />';
+            $this->items[] = $item;
+        }
+    }
+
+    public function getName(){
+        return 'Arte7de';
+    }
+
+    public function getURI(){
+        return 'http://www.arte.tv/';
+    }
+
+    public function getCacheDuration(){
+        return 1800; // 30 minutes
+    }
+}

+ 78 - 0
bridges/Arte7frBridge.php

@@ -0,0 +1,78 @@
+<?php
+/**
+* RssBridgeArte7fr
+* Returns images from given page and tags
+* 2014-05-25
+*
+* @name Arte +7 FR
+* @homepage http://www.arte.tv/guide/fr/
+* @description Returns newest videos from ARTE +7 (french)
+* @maintainer mitsukarenai
+*/
+class Arte7frBridge extends BridgeAbstract{
+
+    public function collectData(array $param){
+
+	$input_json = json_decode(file_get_contents('http://www.arte.tv/guide/fr/plus7.json'), TRUE) or $this->returnError('Could not request ARTE.', 404);
+   
+        foreach($input_json['videos'] as $element) {
+            $item = new \Item();
+            $item->uri = 'http://www.arte.tv'.$element['url'];
+            $item->postid = $item->uri;
+
+				$date = $element['airdate_long'];
+				$date = explode(' ', $date);
+				$day = (int)$date['1'];
+				$month=FALSE;
+				switch ($date['2']) {
+					case 'janvier':
+						$month=1;break;
+					case 'février':
+						$month=2;break;
+					case 'mars':
+						$month=3;break;
+					case 'avril':
+						$month=4;break;
+					case 'mai':
+						$month=5;break;
+					case 'juin':
+						$month=6;break;
+					case 'juillet':
+						$month=7;break;
+					case 'août':
+						$month=8;break;
+					case 'septembre':
+						$month=9;break;
+					case 'octobre':
+						$month=10;break;
+					case 'novembre':
+						$month=11;break;
+					case 'décembre':
+						$month=12;break;
+					}
+				$year=(int)date('Y');
+				$heure=explode('h', $date['4']);
+				$hour=(int)$heure['0'];
+				$minute=(int)$heure['1'];
+  
+
+            $item->timestamp = mktime($hour, $minute, 0, $month, $day, $year);
+            $item->thumbnailUri = $element['image_url'];
+            $item->title = $element['title'];
+            $item->content = $element['desc'].'<br><br>'.$element['video_channels'].', '.$element['duration'].'min<br><img src="' . $item->thumbnailUri . '" />';
+            $this->items[] = $item;
+        }
+    }
+
+    public function getName(){
+        return 'Arte7fr';
+    }
+
+    public function getURI(){
+        return 'http://www.arte.tv/';
+    }
+
+    public function getCacheDuration(){
+        return 1800; // 30 minutes
+    }
+}

+ 48 - 0
bridges/BandcampBridge.php

@@ -0,0 +1,48 @@
+<?php
+/**
+* BandcampTagRSS
+* 2014-05-25
+*
+* @name Bandcamp Tag
+* @homepage http://bandcamp.com/
+* @description New bandcamp release by tag
+* @maintainer sebsauvage
+* @use1(tag="tag")
+*/
+class BandcampBridge extends BridgeAbstract{
+    
+    private $request;
+
+    public function collectData(array $param){
+        $html = '';
+        if (isset($param['tag'])) {
+            $this->request = $param['tag'];
+            $html = file_get_html('http://bandcamp.com/tag/'.urlencode($this->request).'?sort_field=date') or $this->returnError('No results for this query.', 404);
+        }
+        else {
+            $this->returnError('You must specify tag (/tag/...)', 400);
+        }
+
+        foreach($html->find('li.item') as $release) {
+            $item = new \Item();
+            $item->name = $release->find('div.itemsubtext',0)->plaintext . ' - ' . $release->find('div.itemtext',0)->plaintext;
+            $item->title = $release->find('div.itemsubtext',0)->plaintext . ' - ' . $release->find('div.itemtext',0)->plaintext;
+            $item->content = '<img src="' . $release->find('img.art',0)->src . '"/><br/>' . $release->find('div.itemsubtext',0)->plaintext . ' - ' . $release->find('div.itemtext',0)->plaintext;
+            $item->id = $release->find('a',0)->getAttribute('href');
+            $item->uri = $release->find('a',0)->getAttribute('href');
+            $this->items[] = $item;
+        }
+    }
+
+    public function getName(){
+        return (!empty($this->request) ? $this->request .' - ' : '') .'Bandcamp Tag';
+    }
+
+    public function getURI(){
+        return 'http://bandcamp.com';
+    }
+
+    public function getCacheDuration(){
+        return 600; // 10 minutes
+    }
+}

+ 51 - 0
bridges/BastaBridge.php

@@ -0,0 +1,51 @@
+<?php
+/**
+* RssBridgeBastabag
+* Returns the newest articles
+* 2014-05-25
+*
+* @name Bastamag Bridge
+* @homepage http://www.bastamag.net/
+* @description Returns the newest articles.
+* @maintainer qwertygc
+*/
+class BastaBridge extends BridgeAbstract{
+    
+        public function collectData(array $param){
+
+			
+		function BastaExtractContent($url) {
+		$html2 = file_get_html($url);
+		$text = $html2->find('div.texte', 0)->innertext;
+		return $text;
+		}
+		$html = file_get_html('http://www.bastamag.net/spip.php?page=backend') or $this->returnError('Could not request Bastamag.', 404);
+		$limit = 0;
+
+		foreach($html->find('item') as $element) {
+		 if($limit < 10) {
+		 $item = new \Item();
+		 $item->title = $element->find('title', 0)->innertext;
+		 $item->uri = $element->find('guid', 0)->plaintext;
+		 $item->timestamp = strtotime($element->find('pubDate', 0)->plaintext);
+		 $item->content = BastaExtractContent($item->uri);
+		 $this->items[] = $item;
+		 $limit++;
+		 }
+		}
+    
+    }
+
+    public function getName(){
+        return 'Bastamag Bridge';
+    }
+
+    public function getURI(){
+        return 'http://bastamag.net/';
+    }
+
+    public function getCacheDuration(){
+        return 3600*2; // 2 hours
+        // return 0; // 2 hours
+    }
+}

+ 48 - 0
bridges/BlaguesDeMerdeBridge.php

@@ -0,0 +1,48 @@
+<?php
+/**
+*
+* @name Blagues De Merde
+* @homepage http://www.blaguesdemerde.fr/
+* @description Blagues De Merde
+* @update 16/10/2013
+* initial maintainer: superbaillot.net
+*/
+class BlaguesDeMerdeBridge extends BridgeAbstract{
+
+    public function collectData(array $param){
+        $html = file_get_html('http://www.blaguesdemerde.fr/') or $this->returnError('Could not request BDM.', 404);
+    
+        foreach($html->find('article.joke_contener') as $element) {
+            $item = new Item();
+            $temp = $element->find('a');
+            if(isset($temp[2]))
+            {
+                $item->content = trim($element->find('div.joke_text_contener', 0)->innertext);
+                $uri = $temp[2]->href;
+                $item->uri = $uri;
+                $item->title = substr($uri, (strrpos($uri, "/") + 1));
+                $date = $element->find("li.bdm_date",0)->innertext;
+                $time = mktime(0, 0, 0, substr($date, 3, 2), substr($date, 0, 2), substr($date, 6, 4));
+                $item->timestamp = $time;
+                $item->name = $element->find("li.bdm_pseudo",0)->innertext;;
+                $this->items[] = $item;
+            }
+        }
+    }
+
+    public function getName(){
+        return 'blaguesdemerde';
+    }
+
+    public function getURI(){
+        return 'http://www.blaguesdemerde.fr/';
+    }
+
+    public function getCacheDuration(){
+        return 7200; // 2h hours
+    }
+    public function getDescription(){
+        return "Blagues De Merde via rss-bridge";
+    }
+}
+?>

+ 55 - 0
bridges/BooruprojectBridge.php

@@ -0,0 +1,55 @@
+<?php
+/**
+* RssBridgeBooruproject
+* Returns images from given page
+* 2014-05-25
+*
+* @name Booruproject
+* @homepage http://booru.org/
+* @description Returns images from given page and booruproject instance (****.booru.org)
+* @maintainer mitsukarenai
+* @use1(i="instance (required)", p="page", t="tags")
+*/
+class BooruprojectBridge extends BridgeAbstract{
+
+    public function collectData(array $param){
+	$page = 0; $tags = '';
+        if (isset($param['p'])) { 
+		$page = (int)preg_replace("/[^0-9]/",'', $param['p']); 
+		$page = $page - 1;
+		$page = $page * 20;
+        }
+        if (isset($param['t'])) { 
+            $tags = '&tags='.urlencode($param['t']); 
+        }
+	if (empty($param['i'])) {
+		$this->returnError('Please enter a ***.booru.org instance.', 404);
+	}
+        $html = file_get_html("http://".$param['i'].".booru.org/index.php?page=post&s=list&pid=".$page.$tags) or $this->returnError('Could not request Booruproject.', 404);
+
+
+	foreach($html->find('div[class=content] span') as $element) {
+		$item = new \Item();
+		$item->uri = 'http://'.$param['i'].'.booru.org/'.$element->find('a', 0)->href;
+		$item->postid = (int)preg_replace("/[^0-9]/",'', $element->find('a', 0)->getAttribute('id'));	
+		$item->timestamp = time();
+		$item->thumbnailUri = $element->find('img', 0)->src;
+		$item->tags = $element->find('img', 0)->getAttribute('title');
+		$item->title = 'Booruproject '.$param['i'].' | '.$item->postid;
+		$item->content = '<a href="' . $item->uri . '"><img src="' . $item->thumbnailUri . '" /></a><br>Tags: '.$item->tags;
+		$this->items[] = $item; 
+	}
+    }
+
+    public function getName(){
+        return 'Booruproject';
+    }
+
+    public function getURI(){
+        return 'http://booru.org/';
+    }
+
+    public function getCacheDuration(){
+        return 1800; // 30 minutes
+    }
+}

+ 56 - 0
bridges/CoinDeskBridge.php

@@ -0,0 +1,56 @@
+<?php
+/**
+* RssBridgeCoinDesk 
+* Returns the 5 newest posts from coindesk.com (full text)
+*
+* @name CoinDesk
+* @homepage http://www.coindesk.com/
+* @description Returns the 5 newest posts from CoinDesk (full text)
+* @maintainer mitsukarenai
+* @update 2014-05-30
+*/
+class CoinDeskBridge extends BridgeAbstract{
+
+    public function collectData(array $param){
+
+    function CoinDeskStripCDATA($string) {
+    	$string = str_replace('<![CDATA[', '', $string);
+    	$string = str_replace(']]>', '', $string);
+    	return $string;
+    }
+    function CoinDeskExtractContent($url) {
+	$html2 = file_get_html($url);
+	$text = $html2->find('div.single-content', 0)->innertext;
+	$text = strip_tags($text, '<p><a><img>');
+	return $text;
+    }
+        $html = file_get_html('http://www.coindesk.com/feed/atom/') or $this->returnError('Could not request CoinDesk.', 404);
+	$limit = 0;
+
+	foreach($html->find('entry') as $element) {
+	 if($limit < 5) {
+	 $item = new \Item();
+	 $item->title = CoinDeskStripCDATA($element->find('title', 0)->innertext);
+	 $item->author = $element->find('author', 0)->plaintext;
+	 $item->uri = $element->find('link', 0)->href;
+	 $item->timestamp = strtotime($element->find('published', 0)->plaintext);
+	 $item->content = CoinDeskExtractContent($item->uri);
+	 $this->items[] = $item;
+	 $limit++;
+	 }
+	}
+    
+    }
+
+    public function getName(){
+        return 'CoinDesk';
+    }
+
+    public function getURI(){
+        return 'http://www.coindesk.com/';
+    }
+
+    public function getCacheDuration(){
+        return 1800; // 30min
+    }
+}

+ 41 - 0
bridges/CollegeDeFranceBridge.php

@@ -0,0 +1,41 @@
+<?php
+/**
+* RssBridgeCollegeDeFrance
+* Returns the 10 newest posts from http://www.college-de-france.fr
+*
+* @name CollegeDeFrance
+* @homepage http://www.college-de-france.fr/
+* @description Returns the 10 newest posts from CollegeDeFrance
+* @maintainer pit-fgfjiudghdf
+* @update 2014-05-26
+*/
+class CollegeDeFranceBridge extends BridgeAbstract{
+    public function collectData(array $param){
+$find = array('janvier', 'février', 'mars', 'avril', 'mai', 'juin', 'juillet', 'août', 'septembre', 'novembre', 'décembre');
+$replace = array('January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December');
+        $html = file_get_html('http://www.college-de-france.fr/site/audio-video/_audiovideos.jsp?index=0&prompt=&fulltextdefault=mots-cles...&fulltext=mots-cles...&fields=TYPE2_ACTIVITY&fieldsdefault=0_0&TYPE2=0') or $this->returnError('Could not request CollegeDeFrance.', 404);
+        $limit = 0;
+        foreach($html->find('li.audio') as $element) {
+         if($limit < 10) {
+         $item = new \Item();
+         $item->title = $element->find('span.title', 0)->plaintext;
+         $item->timestamp = strtotime(str_replace($find, $replace, $element->find('span.date', 0)->plaintext));
+         $item->content =  $element->find('span.lecturer', 0)->innertext . ' - ' . $element->find('span.title', 0)->innertext;
+         $item->uri = $element->find('a', 0)->href;
+         $this->items[] = $item;
+         $limit++;
+         }
+        }
+
+    }
+    public function getName(){
+        return 'CollegeDeFrance';
+    }
+    public function getURI(){
+        return 'http://www.college-de-france.fr/';
+    }
+    public function getCacheDuration(){
+        return 3600*3; // 3 hour
+    }
+}
+

+ 59 - 0
bridges/CopieDoubleBridge.php

@@ -0,0 +1,59 @@
+<?php
+/**
+ *
+ * @name CopieDouble
+* @homepage http://www.copie-double.com/
+ * @description CopieDouble
+ * @update 12/12/2013
+* initial maintainer: superbaillot.net
+ */
+class CopieDoubleBridge extends BridgeAbstract{
+
+    public function collectData(array $param){
+        $html = file_get_html('http://www.copie-double.com/') or $this->returnError('Could not request CopieDouble.', 404);
+        $table = $html->find('table table', 2);
+        
+        foreach($table->find('tr') as $element)
+        {
+            $td = $element->find('td', 0);
+             $cpt++;
+            if($td->class == "couleur_1")
+            {
+                $item = new Item();
+                
+                $title = $td->innertext;
+                $pos = strpos($title, "<a");
+                $title = substr($title, 0, $pos);
+                $item->title = $title;
+            }
+            elseif(strpos($element->innertext, "/images/suivant.gif") === false)
+            {
+                $a=$element->find("a", 0);
+                $item->uri = "http://www.copie-double.com" . $a->href;
+                
+                $content = str_replace('src="/', 'src="http://www.copie-double.com/',$element->find("td", 0)->innertext);
+                $content = str_replace('href="/', 'href="http://www.copie-double.com/',$content);
+                $item->content = $content;
+                $this->items[] = $item;
+            }
+        }
+    }
+
+    public function getName(){
+        return 'CopieDouble';
+    }
+
+    public function getURI(){
+        return 'http://www.copie-double.com';
+    }
+
+    public function getDescription(){
+        return 'CopieDouble via rss-bridge';
+    }
+
+    public function getCacheDuration(){
+        return 14400; // 4 hours
+    }
+}
+
+?>

+ 6 - 2
bridges/CryptomeBridge.php

@@ -3,25 +3,29 @@
 * RssBridgeCryptome
 * Retrieve lastest documents from Cryptome.
 * Returns the N most recent documents, sorting by date (most recent first).
+* 2014-05-25
 *
 * @name Cryptome
+* @homepage http://cryptome.org/
 * @description Returns the N most recent documents.
+* @maintainer BoboTiG
 * @use1(n="number")
 */
 class CryptomeBridge extends BridgeAbstract{
 
     public function collectData(array $param){
         $html = '';
-        $num = 90;
+        $num = 20;
         $link = 'http://cryptome.org/';
         // If you want HTTPS access instead, uncomment the following line:
         //$link = 'https://secure.netsolhost.com/cryptome.org/';
 
         $html = file_get_html($link) or $this->returnError('Could not request Cryptome.', 404);
-        if (isset($param['n'])) {   /* number of documents */
+        if (!empty($param['n'])) {   /* number of documents */
             $num = min(max(1, $param['n']+0), $num);
         }
 
+
         foreach($html->find('pre') as $element) {
             for ( $i = 0; $i < $num; ++$i ) {
                 $item = new \Item();

+ 81 - 0
bridges/DailymotionBridge.php

@@ -0,0 +1,81 @@
+<?php
+/**
+* RssBridgeDailymotion 
+* Returns the newest videos
+*
+* @name Dailymotion Bridge
+* @homepage https://www.dailymotion.com/
+* @description Returns the 5 newest videos by username/playlist or search
+* @maintainer mitsukarenai
+* @update 2014-11-18
+* @use1(u="username")
+* @use2(p="playlist id")
+* @use3(s="search keyword",pa="page")
+* 
+*/
+class DailymotionBridge extends BridgeAbstract{
+    
+	private $request;
+    
+	public function collectData(array $param){
+
+		function getMetadata($id) {
+			$metadata=array();
+			$html2 = file_get_html('http://www.dailymotion.com/video/'.$id) or $this->returnError('Could not request Dailymotion.', 404);
+			$metadata['title'] = $html2->find('meta[property=og:title]', 0)->getAttribute('content');
+			$metadata['timestamp'] = strtotime($html2->find('meta[property=video:release_date]', 0)->getAttribute('content') );
+			$metadata['thumbnailUri'] = $html2->find('meta[property=og:image]', 0)->getAttribute('content');
+			$metadata['uri'] = $html2->find('meta[property=og:url]', 0)->getAttribute('content');
+
+			return $metadata;
+		} 
+
+
+        	$html = '';
+		$limit = 5;
+		$count = 0;
+
+		if (isset($param['u'])) {   // user timeline mode
+			$this->request = $param['u'];
+			$html = file_get_html('http://www.dailymotion.com/user/'.urlencode($this->request).'/1') or $this->returnError('Could not request Dailymotion.', 404);
+		}
+		else if (isset($param['p'])) {    // playlist mode
+			$this->request = strtok($param['p'], '_');
+			$html = file_get_html('http://www.dailymotion.com/playlist/'.urlencode($this->request).'') or $this->returnError('Could not request Dailymotion.', 404);
+		}
+		else if (isset($param['s'])) {   // search mode
+			$this->request = $param['s']; $page = 1; if (isset($param['pa'])) $page = (int)preg_replace("/[^0-9]/",'', $param['pa']); 
+			$html = file_get_html('http://www.dailymotion.com/search/'.urlencode($this->request).'/'.$page.'') or $this->returnError('Could not request Dailymotion.', 404);
+		}
+		else {
+			$this->returnError('You must either specify a Dailymotion username (?u=...) or a playlist id (?p=...) or search (?s=...)', 400);
+		}
+
+		foreach($html->find('div.media a.preview_link') as $element) {
+			if($count < $limit) {
+				$item = new \Item();
+				$item->id = str_replace('/video/', '', strtok($element->href, '_'));
+				$metadata = getMetadata($item->id);
+				$item->uri = $metadata['uri'];
+				$item->thumbnailUri = $metadata['thumbnailUri'];
+				$item->title = $metadata['title'];
+				$item->timestamp = $metadata['timestamp'];
+				$item->content = '<a href="' . $item->uri . '"><img src="' . $item->thumbnailUri . '" /></a><br><a href="' . $item->uri . '">' . $item->title . '</a>';
+				$this->items[] = $item;
+				$count++;
+			}
+		}
+	}
+
+	public function getName(){
+		return (!empty($this->request) ? $this->request .' - ' : '') .'Dailymotion Bridge';
+	}
+
+	public function getURI(){
+		return 'https://www.dailymotion.com/';
+	}
+
+	public function getCacheDuration(){
+		return 3600*3; // 3 hours
+	}
+}

+ 48 - 0
bridges/DanbooruBridge.php

@@ -0,0 +1,48 @@
+<?php
+/**
+* RssBridgeDanbooru 
+* Returns images from given page
+* 2014-05-25
+*
+* @name Danbooru
+* @homepage http://donmai.us/
+* @description Returns images from given page
+* @maintainer mitsukarenai
+* @use1(p="page", t="tags")
+*/
+class DanbooruBridge extends BridgeAbstract{
+
+    public function collectData(array $param){
+	$page = 1;$tags='';
+        if (isset($param['p'])) { 
+            $page = (int)preg_replace("/[^0-9]/",'', $param['p']); 
+        }
+        if (isset($param['t'])) { 
+            $tags = urlencode($param['t']); 
+        }
+        $html = file_get_html("http://donmai.us/posts?&page=$page&tags=$tags") or $this->returnError('Could not request Danbooru.', 404);
+	foreach($html->find('div[id=posts] article') as $element) {
+		$item = new \Item();
+		$item->uri = 'http://donmai.us'.$element->find('a', 0)->href;
+		$item->postid = (int)preg_replace("/[^0-9]/",'', $element->getAttribute('data-id'));	
+		$item->timestamp = time();
+		$item->thumbnailUri = 'http://donmai.us'.$element->find('img', 0)->src;
+		$item->tags = $element->find('img', 0)->getAttribute('alt');
+		$item->title = 'Danbooru | '.$item->postid;
+		$item->content = '<a href="' . $item->uri . '"><img src="' . $item->thumbnailUri . '" /></a><br>Tags: '.$item->tags;
+		$this->items[] = $item; 
+	}
+    }
+
+    public function getName(){
+        return 'Danbooru';
+    }
+
+    public function getURI(){
+        return 'http://donmai.us/';
+    }
+
+    public function getCacheDuration(){
+        return 1800; // 30 minutes
+    }
+}

+ 41 - 0
bridges/DansTonChatBridge.php

@@ -0,0 +1,41 @@
+<?php
+/**
+* RssBridgeDansTonChat
+* Retrieve lastest quotes from DansTonChat.
+* Returns the most recent quotes, sorting by date (most recent first).
+* 2014-05-25
+*
+* @name DansTonChat Bridge
+* @homepage http://danstonchat.com/latest.html
+* @description Returns latest quotes from DansTonChat.
+* @maintainer Astalaseven
+*/
+class DansTonChatBridge extends BridgeAbstract{
+
+    public function collectData(array $param){
+        $html = '';
+        $link = 'http://danstonchat.com/latest.html';
+
+        $html = file_get_html($link) or $this->returnError('Could not request DansTonChat.', 404);
+
+        foreach($html->find('div.item') as $element) {
+                $item = new \Item();
+                $item->uri = $element->find('a', 0)->href;
+                $item->title = 'DansTonChat '.$element->find('a', 1)->plaintext;
+                $item->content = $element->find('a', 0)->innertext;
+                $this->items[] = $item;
+        }
+    }
+
+    public function getName(){
+        return 'DansTonChat';
+    }
+
+    public function getURI(){
+        return 'http://danstonchat.com';
+    }
+
+    public function getCacheDuration(){
+        return 21600; // 6 hours
+    }
+}

+ 55 - 0
bridges/DauphineLibereBridge.php

@@ -0,0 +1,55 @@
+<?php
+/**
+* @name DauphineLibereBridge Bridge
+* @homepage http://www.ledauphine.com/
+* @description Returns the newest articles. For choice « à la une » leave empty the input. For « France-Monde » input "france-monde". For « Faits Divers » input "faits-divers". For « Economie et Finance » input "economie-et-finance". For « Politique » input "politique". For « Sport » input "sport". For « Ain » input "ain". For « Alpes-de-Haute-Provence  » input "haute-provence". For « Hautes-Alpes » input "hautes-alpes". For « Ardèche » input "ardeche". For « Drôme » input "drome". For « Isere Sud » input "isere-sud". For « Isere Nord » input "isere-nord". For « Savoie » input "savoie". For « Haute-Savoie » input "haute-savoie". For « Vaucluse » input "vaucluse".
+* @maintainer qwertygc
+* @use1(u="edition")
+*/
+class DauphineLibereBridge extends BridgeAbstract{
+    
+        public function collectData(array $param){
+
+			
+		function ExtractContent($url) {
+		$html2 = file_get_html($url);
+		$text = $html2->find('div.column', 0)->innertext;
+		$text = preg_replace('@<script[^>]*?>.*?</script>@si', '', $text);
+		return $text;
+		}
+		if (isset($param['u'])) { /* user timeline mode */
+			$this->request = $param['u'];
+			$html = file_get_html('http://www.ledauphine.com/'.$this->request.'/rss') or $this->returnError('Could not request DauphineLibere.', 404);
+		}
+		else {
+			$html = file_get_html('http://www.ledauphine.com/rss') or $this->returnError('Could not request DauphineLibere.', 404);
+		}
+		$limit = 0;
+
+		foreach($html->find('item') as $element) {
+		 if($limit < 10) {
+		 $item = new \Item();
+		 $item->title = $element->find('title', 0)->innertext;
+		 $item->uri = $element->find('guid', 0)->plaintext;
+		 $item->timestamp = strtotime($element->find('pubDate', 0)->plaintext);
+		 $item->content = ExtractContent($item->uri);
+		 $this->items[] = $item;
+		 $limit++;
+		 }
+		}
+    
+    }
+
+    public function getName(){
+        return 'Dauphine Bridge';
+    }
+
+    public function getURI(){
+        return 'http://ledauphine.com/';
+    }
+
+    public function getCacheDuration(){
+        return 3600*2; // 2 hours
+        // return 0; // 2 hours
+    }
+}

+ 75 - 0
bridges/DeveloppezDotComBridge.php

@@ -0,0 +1,75 @@
+<?php
+/**
+* RssBridgeDeveloppezDotCom
+* Returns the 15 newest posts from http://www.developpez.com (full text)
+* 2014-07-14
+*
+* @name Developpez.com Actus (FR)
+* @homepage http://www.developpez.com/
+* @description Returns the 15 newest posts from DeveloppezDotCom (full text).
+* @maintainer polopollo
+*/
+class DeveloppezDotComBridge extends BridgeAbstract{
+
+    public function collectData(array $param){
+
+        function DeveloppezDotComStripCDATA($string) {
+            $string = str_replace('<![CDATA[', '', $string);
+            $string = str_replace(']]>', '', $string);
+            return $string;
+        }
+
+        function convert_smart_quotes($string)//F***ing quotes from Microsoft Word badly encoded, here was the trick: http://stackoverflow.com/questions/1262038/how-to-replace-microsoft-encoded-quotes-in-php
+        {
+            $search = array(chr(145),
+                            chr(146),
+                            chr(147),
+                            chr(148),
+                            chr(151));
+
+            $replace = array("'",
+                             "'",
+                             '"',
+                             '"',
+                             '-');
+
+            return str_replace($search, $replace, $string);
+        }
+
+        function DeveloppezDotComExtractContent($url) {
+            $articleHTMLContent = file_get_html($url);
+            $text = convert_smart_quotes($articleHTMLContent->find('div.content', 0)->innertext);
+            $text = utf8_encode($text);
+            return trim($text);
+        }
+
+        $rssFeed = file_get_html('http://www.developpez.com/index/rss') or $this->returnError('Could not request http://www.developpez.com/index/rss', 404);
+    	$limit = 0;
+
+    	foreach($rssFeed->find('item') as $element) {
+            if($limit < 10) {
+                $item = new \Item();
+                $item->title = DeveloppezDotComStripCDATA($element->find('title', 0)->innertext);
+                $item->uri = DeveloppezDotComStripCDATA($element->find('guid', 0)->plaintext);
+                $item->timestamp = strtotime($element->find('pubDate', 0)->plaintext);
+                $content = DeveloppezDotComExtractContent($item->uri);
+                $item->content = strlen($content) ? $content : $element->description;//In case of it is a tutorial, we just keep the original description
+                $this->items[] = $item;
+                $limit++;
+            }
+    	}
+
+    }
+
+    public function getName(){
+        return 'DeveloppezDotCom';
+    }
+
+    public function getURI(){
+        return 'http://www.developpez.com/';
+    }
+
+    public function getCacheDuration(){
+        return 1800; // 30min
+    }
+}

+ 45 - 0
bridges/DilbertBridge.php

@@ -0,0 +1,45 @@
+<?php
+/**
+*
+* @name Dilbert Daily Strip 
+* @homepage http://dilbert.com/strips/
+* @description The Unofficial Dilbert Daily Comic Strip
+* @update 16/10/2013
+* initial maintainer: superbaillot.net
+*/
+class DilbertBridge extends BridgeAbstract{
+
+    public function collectData(array $param){
+        $html = file_get_html('http://dilbert.com/strips/') or $this->returnError('Could not request Dilbert.', 404);
+    
+        foreach($html->find('div.STR_Image') as $element) {
+            $item = new Item();
+            $href = $element->find('a',0)->href;
+            $item->uri = 'http://dilbert.com' . $href;
+            $content = str_replace('src="/', 'src="http://dilbert.com/',$element->innertext);
+            $content = str_replace('href="/', 'href="http://dilbert.com/',$content);
+            $item->content = $content;
+            $time = strtotime(substr($href, (strrpos($href, "/", -10) + 1), 10));
+            $item->title = date("d/m/Y", $time);
+            $item->timestamp = $time;
+            $this->items[] = $item;
+        }
+    }
+
+    public function getName(){
+        return 'Dilbert';
+    }
+
+    public function getURI(){
+        return 'http://dilbert.com';
+    }
+
+    public function getDescription(){
+        return 'Dilbert via rss-bridge';
+    }
+
+    public function getCacheDuration(){
+        return 14400; // 4 hours
+    }
+}
+?>

+ 50 - 0
bridges/DollbooruBridge.php

@@ -0,0 +1,50 @@
+<?php
+/**
+* RssBridgeDollbooru
+* Returns images from given page
+* 2014-05-25
+*
+* @name Dollbooru
+* @homepage http://dollbooru.org/
+* @description Returns images from given page
+* @maintainer mitsukarenai
+* @use1(p="page", t="tags")
+*/
+class DollbooruBridge extends BridgeAbstract{
+
+    public function collectData(array $param){
+	$page = 0;$tags='';
+        if (isset($param['p'])) { 
+            $page = (int)preg_replace("/[^0-9]/",'', $param['p']); 
+        }
+        if (isset($param['t'])) { 
+            $tags = urlencode($param['t']); 
+        }
+        $html = file_get_html("http://dollbooru.org/post/list/$tags/$page") or $this->returnError('Could not request Dollbooru.', 404);
+
+
+	foreach($html->find('div[class=shm-image-list] center[class=shm-thumb]') as $element) {
+		$item = new \Item();
+		$item->uri = 'http://dollbooru.org'.$element->find('a', 0)->href;
+		$item->postid = (int)preg_replace("/[^0-9]/",'', $element->getAttribute('data-post-id'));	
+		$item->timestamp = time();
+		$item->thumbnailUri = 'http://dollbooru.org'.$element->find('img', 0)->getAttribute('data-original');
+		$item->tags = $element->getAttribute('data-tags');
+		$item->title = 'Dollbooru | '.$item->postid;
+		$item->content = '<a href="' . $item->uri . '"><img src="' . $item->thumbnailUri . '" /></a><br>Tags: '.$item->tags;
+		$this->items[] = $item; 
+	}
+    }
+
+    public function getName(){
+        return 'Dollbooru';
+    }
+
+    public function getURI(){
+        return 'http://dollbooru.org/';
+    }
+
+    public function getCacheDuration(){
+        return 1800; // 30 minutes
+    }
+}

+ 42 - 0
bridges/DuckDuckGoBridge.php

@@ -0,0 +1,42 @@
+<?php
+/**
+* RssBridgeDuckDuckGo
+* Search DuckDuckGo for most recent pages regarding a specific topic.
+* Returns the most recent links in results, sorting by date (most recent first).
+* 2014-05-25
+*
+* @name DuckDuckGo
+* @homepage https://duckduckgo.com/
+* @description Returns most recent results from DuckDuckGo.
+* @maintainer Astalaseven
+* @use1(u="keyword")
+*/
+class DuckDuckGoBridge extends BridgeAbstract{
+
+    public function collectData(array $param){
+        $html = '';
+        $link = 'http://duckduckgo.com/html/?q='.$param[u].'+sort:date';
+
+        $html = file_get_html($link) or $this->returnError('Could not request DuckDuckGo.', 404);
+
+        foreach($html->find('div.results_links') as $element) {
+                $item = new \Item();
+                $item->uri = $element->find('a', 0)->href;
+                $item->title = $element->find('a', 1)->innertext;
+                $item->content = $element->find('div.snippet', 0)->plaintext;
+                $this->items[] = $item;
+        }
+    }
+
+    public function getName(){
+        return 'DuckDuckGo';
+    }
+
+    public function getURI(){
+        return 'https://duckduckgo.com';
+    }
+
+    public function getCacheDuration(){
+        return 21600; // 6 hours
+    }
+}

+ 60 - 0
bridges/FSBridge.php

@@ -0,0 +1,60 @@
+<?php
+/**
+* RssBridgeFS
+* Returns the 5 newest posts from http://www.futura-sciences.com (full text)
+*
+* @name Futurasciences
+* @description Returns the 5 newest posts from FS (full text)
+* @homepage http://www.futura-sciences.com
+*@maintainer qwertygc
+
+*/
+class FSBridge extends BridgeAbstract{
+
+
+
+
+
+    public function collectData(array $param){
+
+    function FS_StripCDATA($string) {
+    	$string = str_replace('<![CDATA[', '', $string);
+    	$string = str_replace(']]>', '', $string);
+    	return $string;
+    }
+    function FS_ExtractContent($url) {
+	$html2 = file_get_html($url);
+	$text = $html2->find('div.fiche-actualite', 0)->innertext;
+	$text = preg_replace('@<script[^>]*?>.*?</script>@si', '', $text);
+	return $text;
+    }
+        $html = file_get_html('http://www.futura-sciences.com/rss/actualites.xml') or $this->returnError('Could not request Futura Sciences.', 404);
+	$limit = 0;
+
+	foreach($html->find('item') as $element) {
+	 if($limit < 5) {
+	 $item = new \Item();
+	 $item->title = FS_StripCDATA($element->find('title', 0)->innertext);
+	 $item->uri = FS_StripCDATA($element->find('guid', 0)->plaintext);
+	 $item->timestamp = strtotime($element->find('pubDate', 0)->plaintext);
+	 $item->content = FS_ExtractContent($item->uri);
+	 $this->items[] = $item;
+	 $limit++;
+	 }
+	}
+    
+    }
+
+    public function getName(){
+        return 'Futura Sciences';
+    }
+
+    public function getURI(){
+        return 'http://www.futura-sciences.com/';
+    }
+
+    public function getCacheDuration(){
+        return 3600; // 1 hour
+        // return 0; // 1 hour
+    }
+}

+ 4 - 1
bridges/FlickrExploreBridge.php

@@ -2,9 +2,12 @@
 /**
 * RssBridgeFlickrExplore 
 * Returns the newest interesting images from http://www.flickr.com/explore
+* 2014-05-25
 *
 * @name Flickr Explore
+* @homepage http://www.flickr.com/explore
 * @description Returns the latest interesting images from Flickr
+* @maintainer sebsauvage
 */
 class FlickrExploreBridge extends BridgeAbstract{
 
@@ -32,4 +35,4 @@ class FlickrExploreBridge extends BridgeAbstract{
     public function getCacheDuration(){
         return 21600; // 6 hours
     }
-}
+}

+ 53 - 0
bridges/FlickrTagBridge.php

@@ -0,0 +1,53 @@
+<?php
+/**
+* RssBridgeFlickrTagUser 
+* Returns the tagged images from http://www.flickr.com/
+* 2014-05-26
+*
+* @name Flickr TagUser
+* @homepage http://www.flickr.com/
+* @description Returns the tagged or user images from Flickr
+* @maintainer erwang
+* @use1(q="keyword")
+* @use2(u="username")
+*/
+class FlickrTagBridge extends BridgeAbstract{
+
+    public function collectData(array $param){
+        $html = file_get_html('http://www.flickr.com/search/?q=vendee&s=rec') or $this->returnError('Could not request Flickr.', 404);
+        if (isset($param['q'])) {   /* keyword search mode */
+            $this->request = $param['q'];
+            $html = file_get_html('http://www.flickr.com/search/?q='.urlencode($this->request).'&s=rec') or $this->returnError('No results for this query.', 404);
+        }
+        elseif (isset($param['u'])) {   /* user timeline mode */
+            $this->request = $param['u'];
+            $html = file_get_html('http://www.flickr.com/photos/'.urlencode($this->request).'/') or $this->returnError('Requested username can\'t be found.', 404);
+        }
+        
+        else {
+            $this->returnError('You must specify a keyword or a Flickr username.', 400);
+        }
+
+        foreach($html->find('span.photo_container') as $element) {
+            $item = new \Item();
+            $item->uri = 'http://flickr.com'.$element->find('a',0)->href;
+            $item->thumbnailUri = $element->find('img',0)->getAttribute('data-defer-src');
+            $item->content = '<a href="' . $item->uri . '"><img src="' . $item->thumbnailUri . '" /></a>'; // FIXME: Filter javascript ?
+            $item->title = $element->find('a',0)->title;
+            $this->items[] = $item;
+        }
+    }
+
+    public function getName(){
+        return 'Flickr Tag';
+    }
+
+    public function getURI(){
+        return 'http://www.flickr.com/search/';
+    }
+
+    public function getCacheDuration(){
+        return 21600; // 6 hours
+    }
+}
+

+ 58 - 0
bridges/FootitoBridge.php

@@ -0,0 +1,58 @@
+<?php
+/**
+*
+* @name Footito
+* @homepage http://www.footito.fr/
+* @description Footito
+* @update 21/11/2013
+* initial maintainer: superbaillot.net
+*/
+class FootitoBridge extends BridgeAbstract{
+
+    public function collectData(array $param){
+        $html = file_get_html('http://www.footito.fr/') or $this->returnError('Could not request Footito.', 404);
+    
+        foreach($html->find('div.post') as $element) {
+            $item = new Item();
+            
+            $content = trim($element->innertext);
+            $content = str_replace("<img", "<img style='float : left;'", $content );
+            $content = str_replace("class=\"logo\"", "style='float : left;'", $content );
+            $content = str_replace("class=\"contenu\"", "style='margin-left : 60px;'", $content );
+            $content = str_replace("class=\"responsive-comment\"", "style='border-top : 1px #DDD solid; background-color : white; padding : 10px;'", $content );
+            $content = str_replace("class=\"jaime\"", "style='display : none;'", $content );
+            $content = str_replace("class=\"auteur-event responsive\"", "style='display : none;'", $content );
+            $content = str_replace("class=\"report-abuse-button\"", "style='display : none;'", $content );
+            $content = str_replace("class=\"reaction clearfix\"", "style='margin : 10px 0px; padding : 5px; border-bottom : 1px #DDD solid;'", $content );
+            $content = str_replace("class=\"infos\"", "style='font-size : 0.7em;'", $content );
+            
+            $item->content = $content;
+            
+            $title = $element->find('.contenu .texte ', 0)->plaintext;
+            $item->title = $title;
+            
+            $info = $element->find('div.infos', 0);
+            
+            $item->timestamp = strtotime($info->find('time', 0)->datetime);
+            $item->name = $info->find('a.auteur', 0)->plaintext;
+            
+            $this->items[] = $item;
+        }
+    }
+
+    public function getName(){
+        return 'footito';
+    }
+
+    public function getURI(){
+        return 'http://www.footito.fr/';
+    }
+
+    public function getCacheDuration(){
+        return 3600; // 1h hours
+    }
+    public function getDescription(){
+        return "Footito via rss-bridge";
+    }
+}
+?>

+ 52 - 0
bridges/GelbooruBridge.php

@@ -0,0 +1,52 @@
+<?php
+/**
+* RssBridgeGelbooru
+* Returns images from given page
+* 2014-05-25
+*
+* @name Gelbooru
+* @homepage http://gelbooru.com/
+* @description Returns images from given page
+* @maintainer mitsukarenai
+* @use1(p="page", t="tags")
+*/
+class GelbooruBridge extends BridgeAbstract{
+
+    public function collectData(array $param){
+	$page = 0;
+        if (isset($param['p'])) { 
+		$page = (int)preg_replace("/[^0-9]/",'', $param['p']); 
+		$page = $page - 1;
+		$page = $page * 63;
+        }
+        if (isset($param['t'])) { 
+            $tags = urlencode($param['t']); 
+        }
+        $html = file_get_html("http://gelbooru.com/index.php?page=post&s=list&tags=$tags&pid=$page") or $this->returnError('Could not request Gelbooru.', 404);
+
+
+	foreach($html->find('div[class=content] span') as $element) {
+		$item = new \Item();
+		$item->uri = 'http://gelbooru.com/'.$element->find('a', 0)->href;
+		$item->postid = (int)preg_replace("/[^0-9]/",'', $element->getAttribute('id'));	
+		$item->timestamp = time();
+		$item->thumbnailUri = $element->find('img', 0)->src;
+		$item->tags = $element->find('img', 0)->getAttribute('alt');
+		$item->title = 'Gelbooru | '.$item->postid;
+		$item->content = '<a href="' . $item->uri . '"><img src="' . $item->thumbnailUri . '" /></a><br>Tags: '.$item->tags;
+		$this->items[] = $item; 
+	}
+    }
+
+    public function getName(){
+        return 'Gelbooru';
+    }
+
+    public function getURI(){
+        return 'http://gelbooru.com/';
+    }
+
+    public function getCacheDuration(){
+        return 1800; // 30 minutes
+    }
+}

+ 57 - 0
bridges/GizmodoFRBridge.php

@@ -0,0 +1,57 @@
+<?php
+/**
+* RssBridgeGizmodoFR
+* Returns the 15 newest posts from http://www.gizmodo.fr (full text)
+* 2014-07-14
+*
+* @name GizmodoFR
+* @homepage http://www.gizmodo.fr/
+* @description Returns the 15 newest posts from GizmodoFR (full text).
+* @maintainer polopollo
+*/
+class GizmodoFRBridge extends BridgeAbstract{
+
+    public function collectData(array $param){
+
+        function GizmodoFRExtractContent($url) {
+            $articleHTMLContent = file_get_html($url);
+            $text = $articleHTMLContent->find('div.entry-thumbnail', 0)->innertext;
+            $text = $text.$articleHTMLContent->find('div.entry-excerpt', 0)->innertext;
+            $text = $text.$articleHTMLContent->find('div.entry-content', 0)->innertext;
+            foreach($articleHTMLContent->find('pagespeed_iframe') as $element) {
+                $text = $text.'<p>link to a iframe (could be a video): <a href="'.$element->src.'">'.$element->src.'</a></p><br>';
+            }
+
+            $text = strip_tags($text, '<p><b><a><blockquote><img><em>');
+            return $text;
+        }
+
+        $rssFeed = file_get_html('http://www.gizmodo.fr/feed') or $this->returnError('Could not request http://www.gizmodo.fr/feed', 404);
+    	$limit = 0;
+
+    	foreach($rssFeed->find('item') as $element) {
+            if($limit < 15) {
+                $item = new \Item();
+                $item->title = $element->find('title', 0)->innertext;
+                $item->uri = $element->find('guid', 0)->plaintext;
+                $item->timestamp = strtotime($element->find('pubDate', 0)->plaintext);
+                $item->content = GizmodoFRExtractContent($item->uri);
+                $this->items[] = $item;
+                $limit++;
+            }
+    	}
+
+    }
+
+    public function getName(){
+        return 'GizmodoFR';
+    }
+
+    public function getURI(){
+        return 'http://www.gizmodo.fr/';
+    }
+
+    public function getCacheDuration(){
+        return 1800; // 30min
+    }
+}

+ 125 - 0
bridges/GooglePlusPostBridge.php

@@ -0,0 +1,125 @@
+<?php
+
+/**
+ * Google Plus Post Bridge
+ * Freely inspired by tweeter bridge
+ * 2014-07-20
+ *
+ * @name Google Plus Post Bridge
+ * @homepage http://plus.google.com/
+ * @description Returns user public post (without API).
+ * @maintainer Grummfy
+ * @use1(username="usernameOrId")
+ */
+class GooglePlusPostBridge extends BridgeAbstract
+{
+	protected $_title;
+	protected $_url;
+
+	const GOOGLE_PLUS_BASE_URL = 'https://plus.google.com/';
+
+	public function collectData(array $param)
+	{
+		if (!isset($param['username']))
+		{
+			$this->returnError('You must specify a username (?username=...).', 400);
+		}
+
+		$this->request = $param['username'];
+		// get content parsed
+//		$html = file_get_html(__DIR__ . '/../posts2.html'
+		$html = file_get_html(self::GOOGLE_PLUS_BASE_URL . urlencode($this->request) . '/posts'
+			// force language
+			, false, stream_context_create(array('http'=> array(
+			'header'    => 'Accept-Language: fr,fr-be,fr-fr;q=0.8,en;q=0.4,en-us;q=0.2;*' . "\r\n"
+			)))
+		) OR $this->returnError('No results for this query.', 404);
+
+		// get title, url, ... there is a lot of intresting stuff in meta
+		$this->_title = $html->find('meta[property]', 0)->getAttribute('content');
+		$this->_url = $html->find('meta[itemprop=url]', 0)->getAttribute('content');
+
+//		foreach ($html->find('meta') as $e)
+//		{
+//			$item = new \Item();
+//			$item->content = var_export($e->attr, true);
+//			$this->items[] = $item;
+//		}
+
+		// div[jsmodel=XNmfOc]
+		foreach($html->find('div.yt') as $post)
+		{
+			$item = new \Item();
+//			$item->content = $post->find('div.Al', 0)->innertext;
+			$item->username = $item->fullname = $post->find('header.lea h3 a', 0)->innertext;
+			$item->id = $post->getAttribute('id');
+//			$item->title = $item->fullname = $post->find('header.lea', 0)->plaintext;
+			$item->avatar = $post->find('div.ys img', 0)->src;
+//			var_dump((($post->find('a.o-U-s', 0)->getAllAttributes())));
+			$item->uri = $post->find('a.o-U-s', 0)->href;
+			$item->timestamp = strtotime($post->find('a.o-U-s', 0)->plaintext);
+			$this->items[] = $item;
+
+			// hashtag to treat : https://plus.google.com/explore/tag
+			$hashtags = array();
+			foreach($post->find('a.d-s') as $hashtag)
+			{
+				$hashtags[ trim($hashtag->plaintext) ] = self::GOOGLE_PLUS_BASE_URL . $hashtag->href;
+			}
+
+			$item->content = '';
+
+			// avatar display
+			$item->content .= '<div style="float:left; margin: 0 0.5em 0.5em 0;"><a href="' . self::GOOGLE_PLUS_BASE_URL . urlencode($this->request);
+			$item->content .= '"><img align="top" alt="avatar" src="' . $item->avatar.'" />' . $item->username . '</a></div>';
+
+			$content = $post->find('div.Al', 0);
+
+			// alter link
+//			$content = $content->innertext;
+//			$content = str_replace('href="./', 'href="' . self::GOOGLE_PLUS_BASE_URL, $content);
+//			$content = str_replace('href="photos', 'href="' . self::GOOGLE_PLUS_BASE_URL . 'photos', $content);
+			// XXX ugly but I don't have any idea how to do a better stuff, str_replace on link doesn't work as expected and ask too many checks
+			foreach($content->find('a') as $link)
+			{
+				$hasHttp = strpos($link->href, 'http');
+				$hasDoubleSlash = strpos($link->href, '//');
+
+				if ((!$hasHttp && !$hasDoubleSlash)
+					|| (false !== $hasHttp && strpos($link->href, 'http') != 0)
+					|| (false === $hasHttp && false !== $hasDoubleSlash && $hasDoubleSlash != 0))
+				{
+					// skipp bad link, for some hashtag or other stuff
+					if (strpos($link->href, '/') == 0)
+					{
+						$link->href = substr($link->href, 1);
+					}
+					$link->href = self::GOOGLE_PLUS_BASE_URL . $link->href;
+				}
+			}
+			$content = $content->innertext;
+
+			$item->content .= '<div style="margin-top: -1.5em">' .  $content . '</div>';
+
+			// extract plaintext
+			$item->content_simple = $post->find('div.Al', 0)->plaintext;
+		}
+
+//		$html->save(__DIR__ . '/../posts2.html');
+	}
+
+	public function getName()
+	{
+		return $this->_title ?: 'Google Plus Post Bridge';
+	}
+
+	public function getURI()
+	{
+		return $this->_url ?: 'http://plus.google.com/';
+	}
+
+	public function getCacheDuration()
+	{
+		return 1; // 600; // 10 minutes
+	}
+}

+ 4 - 1
bridges/GoogleSearchBridge.php

@@ -8,9 +8,12 @@
 *    complete=0&num=100 : get 100 results
 *    qdr:y : in past year
 *    sbd:1 : sort by date (will only work if qdr: is specified)
+* 2014-05-25
 *
 * @name Google search
+* @homepage https://www.google.com/
 * @description Returns most recent results from Google search.
+* @maintainer sebsauvage
 * @use1(q="keyword")
 */
 class GoogleSearchBridge extends BridgeAbstract{
@@ -56,4 +59,4 @@ class GoogleSearchBridge extends BridgeAbstract{
     public function getCacheDuration(){
         return 1800; // 30 minutes
     }
-}
+}

+ 57 - 0
bridges/GuruMedBridge.php

@@ -0,0 +1,57 @@
+<?php
+/**
+* RssBridgeGuruMed 
+* Returns the 5 newest posts from http://www.gurumed.org (full text)
+*
+* @name GuruMed
+* @description Returns the 5 newest posts from Gurumed (full text)
+* @homepage http://www.gurumed.org
+*@maintainer qwertygc
+*/
+class GuruMedBridge extends BridgeAbstract{
+
+
+
+
+
+    public function collectData(array $param){
+
+    function GurumedStripCDATA($string) {
+    	$string = str_replace('<![CDATA[', '', $string);
+    	$string = str_replace(']]>', '', $string);
+    	return $string;
+    }
+    function GurumedExtractContent($url) {
+	$html2 = file_get_html($url);
+	$text = $html2->find('div.entry', 0)->innertext;
+	return $text;
+    }
+        $html = file_get_html('http://gurumed.org/feed') or $this->returnError('Could not request Gurumed.', 404);
+	$limit = 0;
+
+	foreach($html->find('item') as $element) {
+	 if($limit < 5) {
+	 $item = new \Item();
+	 $item->title = GurumedStripCDATA($element->find('title', 0)->innertext);
+	 $item->uri = GurumedStripCDATA($element->find('guid', 0)->plaintext);
+	 $item->timestamp = strtotime($element->find('pubDate', 0)->plaintext);
+	 $item->content = GurumedExtractContent($item->uri);
+	 $this->items[] = $item;
+	 $limit++;
+	 }
+	}
+    
+    }
+
+    public function getName(){
+        return 'Gurumed';
+    }
+
+    public function getURI(){
+        return 'http://gurumed.org/';
+    }
+
+    public function getCacheDuration(){
+        return 3600; // 1 hour
+    }
+}

+ 62 - 0
bridges/HumbleStoreDiscountBridge.php

@@ -0,0 +1,62 @@
+<?php
+/**
+* RssBridgeHumbleStoreDiscount
+* Returns the 10 first sales from the Humble Store
+* Enjoy your indie games :)
+*
+* @name Humble Store Discount Bridge
+* @homepage https://www.humblebundle.com/store
+* @description Returns the 10 first sales from the Humble Store
+* @maintainer 16mhz
+* @update 2014-07-18
+*/
+class HumbleStoreDiscountBridge extends BridgeAbstract{
+
+    public function collectData(array $param){
+
+        $result = file_get_html('https://www.humblebundle.com/store/api/humblebundle?request=2&page_size=20&sort=discount&page=0')
+                or $this->returnError('Could not request the Humble Store.', 404);
+        $string = json_decode($result, true);
+        $items = $string['results'];
+        $store_link = 'https://www.humblebundle.com/store/p/';
+        $limit = 0;
+
+        foreach ($items as $key => $value) {
+            if ($limit < 10) {
+                $new_price = $value['current_price'][0] . ' ' . $value['current_price'][1];
+                $full_price = $value['full_price'][0] . ' ' . $value['full_price'][1];
+                $product_name = $value['human_name'];
+		$sale_end = (int)$value['sale_end'];
+                $product_uri = $store_link . $value['machine_name'];
+                $platforms = str_replace('\'', '', implode("','", $value['platforms']));
+                $delivery_methods = str_replace('\'', '', implode("','", $value['delivery_methods']));
+                $thumbnail = 'https://www.humblebundle.com' . $value['storefront_featured_image_small'];
+
+                $content = '<img src="' . $thumbnail . '" alt="' . $value['storefront_featured_image_small'] . '"><br/><br/><b>' . $product_name
+                    . '</b><br/><br/><b>Current price:</b> ' . $new_price . '<br/><b>Full price:</b> ' . $full_price .'<br/><b>Sale ends:</b> '. date(DATE_ATOM, $sale_end)
+                    . '<br/><b>Developer:</b> ' . $value['developer_name'] . '<br/><b>Delivery methods:</b> ' . $delivery_methods
+                    . '<br/><b>Platforms:</b> ' . $platforms . '<br/>' . $value['description'];
+
+                $item = new \Item();
+                $item->title = $product_name . ' - ' . $new_price;
+                $item->uri = $product_uri;
+		$item->timestamp = $sale_end - 10*24*3600; // just a hack, stamping game as 10 days before sales end (better than no timestamp)
+                $item->content = $content;
+                $this->items[] = $item;
+                $limit++;
+            }
+        }
+    }
+
+    public function getName(){
+        return 'HumbleStoreDiscount';
+    }
+
+    public function getURI(){
+        return 'https://www.humblebundle.com/store';
+    }
+
+    public function getCacheDuration(){
+        return 21600; // 6 hours
+    }
+}

+ 3 - 0
bridges/IdenticaBridge.php

@@ -1,9 +1,12 @@
 <?php
 /**
 * RssBridgeIdentica 
+* 2014-05-25
 *
 * @name Identica Bridge
+* @homepage https://identi.ca/
 * @description Returns user timelines
+* @maintainer mitsukarenai
 * @use1(u="username")
 */
 class IdenticaBridge extends BridgeAbstract{

+ 3 - 0
bridges/InstagramBridge.php

@@ -2,9 +2,12 @@
 /**
  * RssBridgeInstagram
  * Returns the newest photos
+* 2014-05-25
  *
  * @name Instagram Bridge
+* @homepage http://instagram.com/
  * @description Returns the newest images
+* @maintainer pauder
  * @use1(u="username")
  */
 class InstagramBridge extends BridgeAbstract{

+ 54 - 0
bridges/KonachanBridge.php

@@ -0,0 +1,54 @@
+<?php
+/**
+* RssBridgeKonachan 
+* Returns images from given page
+* 2014-05-25
+*
+* @name Konachan
+* @homepage http://konachan.com/
+* @description Returns images from given page
+* @maintainer mitsukarenai
+* @use1(p="page",t="tags")
+*/
+class KonachanBridge extends BridgeAbstract{
+
+    public function collectData(array $param){
+	$page = 1;$tags='';
+        if (isset($param['p'])) { 
+            $page = (int)preg_replace("/[^0-9]/",'', $param['p']); 
+        }
+        if (isset($param['t'])) { 
+            $tags = urlencode($param['t']); 
+        }
+        $html = file_get_html("http://konachan.com/post?page=$page&tags=$tags") or $this->returnError('Could not request Konachan.', 404);
+	$input_json = explode('Post.register(', $html);
+	foreach($input_json as $element)
+	 $data[] = preg_replace('/}\)(.*)/', '}', $element);
+	unset($data[0]);
+    
+        foreach($data as $datai) {
+	    $json = json_decode($datai, TRUE);
+            $item = new \Item();
+            $item->uri = 'http://konachan.com/post/show/'.$json['id'];
+            $item->postid = $json['id'];
+            $item->timestamp = $json['created_at'];
+            $item->imageUri = $json['file_url'];
+            $item->thumbnailUri = $json['preview_url'];
+            $item->title = 'Konachan | '.$json['id'];
+            $item->content = '<a href="' . $item->imageUri . '"><img src="' . $item->thumbnailUri . '" /></a><br>Tags: '.$json['tags']; 
+            $this->items[] = $item;
+        }
+    }
+
+    public function getName(){
+        return 'Konachan';
+    }
+
+    public function getURI(){
+        return 'http://konachan.com/post';
+    }
+
+    public function getCacheDuration(){
+        return 1800; // 30 minutes
+    }
+}

+ 56 - 0
bridges/KoreusBridge.php

@@ -0,0 +1,56 @@
+<?php
+/**
+* RssBridgeKoreus
+* Returns the 5 newest posts from Koreus (full text)
+*
+* @name Koreus
+* @homepage http://www.koreus.com/
+* @description Returns the 5 newest posts from Koreus (full text)
+* @maintainer pit-fgfjiudghdf
+* @update 2014-05-26
+*/
+class KoreusBridge extends BridgeAbstract{
+
+    public function collectData(array $param){
+
+    function KoreusStripCDATA($string) {
+        $string = str_replace('<![CDATA[', '', $string);
+        $string = str_replace(']]>', '', $string);
+        return $string;
+    }
+    function KoreusExtractContent($url) {
+        $html2 = file_get_html($url);
+        $text = $html2->find('p[class=itemText]', 0)->innertext;
+        $text = utf8_encode(preg_replace('/(Sur le m.+?)+$/i','',$text));
+        return $text;
+    }
+        $html = file_get_html('http://feeds.feedburner.com/Koreus-articles') or $this->returnError('Could not request Koreus.', 404);
+        $limit = 0;
+
+        foreach($html->find('item') as $element) {
+         if($limit < 5) {
+         $item = new \Item();
+         $item->title = KoreusStripCDATA($element->find('title', 0)->innertext);
+         $item->uri = KoreusStripCDATA($element->find('guid', 0)->plaintext);
+         $item->timestamp = strtotime($element->find('pubDate', 0)->plaintext);
+         $item->content = KoreusExtractContent($item->uri);
+         $this->items[] = $item;
+         $limit++;
+         }
+        }
+
+    }
+
+    public function getName(){
+        return 'Koreus';
+    }
+
+    public function getURI(){
+        return 'http://www.koreus.com/';
+    }
+
+    public function getCacheDuration(){
+        return 3600; // 1 hour
+    }
+}
+

+ 61 - 0
bridges/LeBonCoinBridge.php

@@ -0,0 +1,61 @@
+<?php
+/**
+* RssBridgeLeBonCoin
+* Search LeBonCoin for most recent ads in a specific region and topic.
+* Returns the most recent classified ads in results, sorting by date (most recent first).
+* Region identifiers : alsace, aquitaine, auvergne, basse_normandie, bourgogne, bretagne, centre,
+*     champagne_ardenne, corse, franche_comte, haute_normandie, ile_de_france, languedoc_roussillon,
+*     limousin, lorraine, midi_pyrenees, nord_pas_de_calais, pays_de_la_loire, picardie,
+*     poitou_charentes, provence_alpes_cote_d_azur, rhone_alpes, guadeloupe, martinique, guyane, reunion.
+* 2014-07-22
+*
+* @name LeBonCoin
+* @homepage http://www.leboncoin.fr
+* @description Returns most recent results from LeBonCoin for a region and a keyword.
+* @maintainer 16mhz
+* @use1(r="Region identifier", k="Keyword")
+*/
+
+class LeBonCoinBridge extends BridgeAbstract{
+
+    public function collectData(array $param){
+
+        $html = '';
+        $link = 'http://www.leboncoin.fr/annonces/offres/' . $param[r] . '/?f=a&th=1&q=' . $param[k];
+        $html = file_get_html($link) or $this->returnError('Could not request LeBonCoin.', 404);
+
+        $list = $html->find('.list-lbc', 0);
+        $tags = $list->find('a');
+
+        foreach($tags as $element) {
+                $item = new \Item();
+                $item->uri = $element->href;
+                $title = $element->getAttribute('title');
+
+                $content = '<img src="' . $element->find('div.image', 0)->find('img', 0)->getAttribute('src') . '" alt="thumbnail">';
+                $date = $element->find('div.date', 0)->find('div', 0) . $element->find('div.date', 0)->find('div', 1) . '<br/>';
+                $detailsList = $element->find('div.detail', 0);
+
+                for ($i = 1; $i < 4; $i++) {
+                    $line = $detailsList->find('div', $i);
+                    $content .= $line;
+                }
+
+                $item->title = $title . ' - ' . $detailsList->find('div', 3);
+                $item->content = $content . $date;
+                $this->items[] = $item;
+        }
+  }
+
+    public function getName(){
+        return 'LeBonCoin';
+    }
+
+    public function getURI(){
+        return 'http://www.leboncoin.fr';
+    }
+
+    public function getCacheDuration(){
+        return 3600; // 1 hour
+    }
+}

+ 69 - 0
bridges/LeJournalDuGeekBridge.php

@@ -0,0 +1,69 @@
+<?php
+/**
+* RssBridgeLeJournalDuGeek
+* Returns the 15 newest posts from http://www.journaldugeek.com (full text)
+* 2014-07-14
+*
+* @name journaldugeek.com (FR)
+* @homepage http://www.journaldugeek.com/
+* @description Returns the 5 newest posts from LeJournalDuGeek (full text).
+* @maintainer polopollo
+*/
+class LeJournalDuGeekBridge extends BridgeAbstract{
+
+    public function collectData(array $param){
+
+        function LeJournalDuGeekStripCDATA($string) {
+            $string = str_replace('<![CDATA[', '', $string);
+            $string = str_replace(']]>', '', $string);
+            return $string;
+        }
+
+        function LeJournalDuGeekExtractContent($url) {
+            $articleHTMLContent = file_get_html($url);
+            $text = $text.$articleHTMLContent->find('div.post-content', 0)->innertext;
+            foreach($articleHTMLContent->find('a.more') as $element) {
+                if ($element->innertext == "Source") {
+                    $text = $text.'<p><a href="'.$element->href.'">Source : '.$element->href.'</a></p>';
+                    break;
+                }
+            }
+            foreach($articleHTMLContent->find('iframe') as $element) {
+                if (preg_match("/youtube/i", $element->src)) {
+                    $text = $text.'// An IFRAME to Youtube was included in the article: <a href="'.$element->src.'">'.$element->src.'</a><br>';
+                }
+            }
+
+            $text = strip_tags($text, '<p><b><a><blockquote><img><em><br/><br><ul><li>');
+            return $text;
+        }
+
+        $rssFeed = file_get_html('http://www.journaldugeek.com/rss') or $this->returnError('Could not request http://www.journaldugeek.com/rss', 404);
+    	$limit = 0;
+
+    	foreach($rssFeed->find('item') as $element) {
+            if($limit < 5) {
+                $item = new \Item();
+                $item->title = LeJournalDuGeekStripCDATA($element->find('title', 0)->innertext);
+                $item->uri = LeJournalDuGeekStripCDATA($element->find('guid', 0)->plaintext);
+                $item->timestamp = strtotime($element->find('pubDate', 0)->plaintext);
+                $item->content = LeJournalDuGeekExtractContent($item->uri);
+                $this->items[] = $item;
+                $limit++;
+            }
+    	}
+
+    }
+
+    public function getName(){
+        return 'LeJournalDuGeek';
+    }
+
+    public function getURI(){
+        return 'http://www.journaldugeek.com/';
+    }
+
+    public function getCacheDuration(){
+        return 1800; // 30min
+    }
+}

+ 52 - 0
bridges/LeMotDuJourBridge.php

@@ -0,0 +1,52 @@
+<?php
+/**
+* 2014-05-25
+* @name LeMotDuJour Bridge
+* @homepage http://www.lemotdujour.com/
+* @description Returns the newest articles.
+* @maintainer qwertygc
+*/
+class LeMotDuJourBridge extends BridgeAbstract{
+    
+        public function collectData(array $param){
+
+			function StripCDATA($string) {
+			$string = str_replace('<![CDATA[', '', $string);
+			$string = str_replace(']]>', '', $string);
+			return $string;
+		}
+		function ExtractContent($url) {
+		$html2 = file_get_html($url);
+		$text = $html2->find('div.single-contenu', 0)->innertext;
+		return $text;
+		}
+		$html = file_get_html('http://feeds2.feedburner.com/lemotdujour/lemotdujour') or $this->returnError('Could not request LeMotDuJour.', 404);
+		$limit = 0;
+
+		foreach($html->find('item') as $element) {
+		 if($limit < 10) {
+		 $item = new \Item();
+		 $item->title = StripCDATA($element->find('title', 0)->innertext);
+		 $item->uri = StripCDATA($element->find('guid', 0)->plaintext);
+		 $item->timestamp = strtotime($element->find('pubDate', 0)->plaintext);
+		 $item->content = ExtractContent($item->uri);
+		 $this->items[] = $item;
+		 $limit++;
+		 }
+		}
+    
+    }
+
+    public function getName(){
+        return 'LeMotDuJour Bridge';
+    }
+
+    public function getURI(){
+        return 'http://lemotdujour.com/';
+    }
+
+    public function getCacheDuration(){
+        return 3600*2; // 2 hours
+        // return 0; // 2 hours
+    }
+}

+ 58 - 0
bridges/LesJoiesDuCodeBridge.php

@@ -0,0 +1,58 @@
+<?php
+/**
+*
+* @name Les Joies Du Code
+* @homepage http://lesjoiesducode.fr/
+* @description LesJoiesDuCode
+* @update 30/01/2014
+* initial maintainer: superbaillot.net
+*/
+class LesJoiesDuCodeBridge extends BridgeAbstract{
+
+    public function collectData(array $param){
+        $html = file_get_html('http://lesjoiesducode.fr/') or $this->returnError('Could not request LesJoiesDuCode.', 404);
+    
+        foreach($html->find('div.post') as $element) {
+            $item = new Item();
+            $temp = $element->find('h3 a', 0);
+            
+            $titre = $temp->innertext;
+            $url = $temp->href;
+            
+            $temp = $element->find('div.bodytype', 0);
+            $content = $temp->innertext;
+            
+            $auteur = $temp->find('.c1 em', 0);
+            $pos = strpos($auteur->innertext, "by");
+            
+            if($pos > 0)
+            {
+                $auteur = trim(str_replace("*/", "", substr($auteur->innertext, ($pos + 2))));
+                $item->name = $auteur;
+            }
+            
+            
+            $item->content .= trim($content);
+            $item->uri = $url;
+            $item->title = trim($titre);
+            
+            $this->items[] = $item;
+        }
+    }
+
+    public function getName(){
+        return 'Les Joies Du Code';
+    }
+
+    public function getURI(){
+        return 'http://lesjoiesducode.fr/';
+    }
+
+    public function getCacheDuration(){
+        return 7200; // 2h hours
+    }
+    public function getDescription(){
+        return "Les Joies Du Code via rss-bridge";
+    }
+}
+?>

+ 60 - 0
bridges/MalikiBridge.php

@@ -0,0 +1,60 @@
+<?php
+/**
+* RssBridgeMaliki 
+* Returns Maliki's newest strips
+*
+* @name Maliki
+* @homepage http://www.maliki.com/
+* @description Returns Maliki's newest strips
+* @maintainer mitsukarenai
+* @update 2014-05-30
+*/
+class MalikiBridge extends BridgeAbstract{
+
+    public function collectData(array $param){
+        $html = file_get_html('http://www.maliki.com/') or $this->returnError('Could not request Maliki.', 404);
+	$count=0;
+	$latest=1; $latest_title="";
+	$latest = $html->find('div.conteneur_page a', 1)->href;
+	$latest_title = $html->find('div.conteneur_page img', 0)->title;
+
+	function MalikiExtractContent($url) {
+		$html2 = file_get_html($url);
+		$text = 'http://www.maliki.com/'.$html2->find('img', 0)->src;
+		$text = '<img alt="" src="'.$text.'"/><br>'.$html2->find('div.imageetnews', 0)->plaintext;
+		return $text;
+    	}
+
+            $item = new \Item();
+            $item->uri = 'http://www.maliki.com/'.$latest;
+            $item->title = $latest_title;
+            $item->timestamp = time();
+            $item->content = MalikiExtractContent($item->uri);
+            $this->items[] = $item;
+	
+
+        foreach($html->find('div.boite_strip') as $element) {
+	  if(!empty($element->find('a',0)->href) and $count < 3) {
+            $item = new \Item();
+            $item->uri = 'http://www.maliki.com/'.$element->find('a',0)->href;
+            $item->title = $element->find('img',0)->title;
+            $item->timestamp = strtotime(str_replace('/', '-', $element->find('span.stylepetit', 0)->innertext));
+            $item->content = MalikiExtractContent($item->uri);
+            $this->items[] = $item;
+	    $count++;
+          }
+        }
+    }
+
+    public function getName(){
+        return 'Maliki';
+    }
+
+    public function getURI(){
+        return 'http://www.maliki.com/';
+    }
+
+    public function getCacheDuration(){
+        return 86400*6; // 6 days
+    }
+}

+ 54 - 0
bridges/MemoLinuxBridge.php

@@ -0,0 +1,54 @@
+<?php
+/**
+* @name MemoLinux
+* @homepage http://memo-linux.com/
+* @description Returns the 10 newest posts from MemoLinux (full text)
+* @maintainer qwertygc
+* @update 2014-07-07
+*/
+class MemoLinuxBridge extends BridgeAbstract{
+
+    public function collectData(array $param){
+
+    function StripCDATA($string) {
+    	$string = str_replace('<![CDATA[', '', $string);
+    	$string = str_replace(']]>', '', $string);
+    	return $string;
+    }
+    function ExtractContent($url) {
+	$html2 = file_get_html($url);
+	$text = $html2->find('div.post-content', 0)->innertext;
+	$text = preg_replace('@<script[^>]*?>.*?</script>@si', '', $text);
+	$text = preg_replace('@<div[^>]*?>.*?</div>@si', '', $text);
+	$text = preg_replace("/<h1.*/", '', $text);
+	return $text;
+    }
+        $html = file_get_html('http://memo-linux.com/feed/') or $this->returnError('Could not request MemoLinux.', 404);
+	$limit = 0;
+
+	foreach($html->find('item') as $element) {
+	 if($limit < 10) {
+	 $item = new \Item();
+	 $item->title = StripCDATA($element->find('title', 0)->innertext);
+	 $item->uri = StripCDATA($element->find('guid', 0)->plaintext);
+	 $item->timestamp = strtotime($element->find('pubDate', 0)->plaintext);
+	 $item->content = ExtractContent($item->uri);
+	 $this->items[] = $item;
+	 $limit++;
+	 }
+	}
+    
+    }
+
+    public function getName(){
+        return 'MemoLinux';
+    }
+
+    public function getURI(){
+        return 'http://memo-linux.com/feed/';
+    }
+
+    public function getCacheDuration(){
+        return 3600*12; // 12 hours
+    }
+}

+ 50 - 0
bridges/MilbooruBridge.php

@@ -0,0 +1,50 @@
+<?php
+/**
+* RssBridgeMilbooru
+* Returns images from given page
+* 2014-05-25
+*
+* @name Milbooru
+* @homepage http://sheslostcontrol.net/moe/shimmie/
+* @description Returns images from given page
+* @maintainer mitsukarenai
+* @use1(p="page",t="tags")
+*/
+class MilbooruBridge extends BridgeAbstract{
+
+    public function collectData(array $param){
+	$page = 0;$tags='';
+        if (isset($param['p'])) { 
+            $page = (int)preg_replace("/[^0-9]/",'', $param['p']); 
+        }
+        if (isset($param['t'])) { 
+            $tags = urlencode($param['t']); 
+        }
+        $html = file_get_html("http://sheslostcontrol.net/moe/shimmie/index.php?q=/post/list/$tags/$page") or $this->returnError('Could not request Milbooru.', 404);
+
+
+	foreach($html->find('div[class=shm-image-list] span[class=thumb]') as $element) {
+		$item = new \Item();
+		$item->uri = 'http://sheslostcontrol.net/moe/shimmie/'.$element->find('a', 0)->href;
+		$item->postid = (int)preg_replace("/[^0-9]/",'', $element->find('a', 0)->getAttribute('data-post-id'));	
+		$item->timestamp = time();
+		$item->thumbnailUri = 'http://sheslostcontrol.net/moe/shimmie/'.$element->find('img', 0)->src;
+		$item->tags = $element->find('a', 0)->getAttribute('data-tags');
+		$item->title = 'Milbooru | '.$item->postid;
+		$item->content = '<a href="' . $item->uri . '"><img src="' . $item->thumbnailUri . '" /></a><br>Tags: '.$item->tags;
+		$this->items[] = $item; 
+	}
+    }
+
+    public function getName(){
+        return 'Milbooru';
+    }
+
+    public function getURI(){
+        return 'http://sheslostcontrol.net/moe/shimmie/';
+    }
+
+    public function getCacheDuration(){
+        return 1800; // 30 minutes
+    }
+}

+ 62 - 0
bridges/MondeDiploBridge.php

@@ -0,0 +1,62 @@
+<?php
+/**
+* RssBridgeMondeDiplo
+* Search MondeDiplo for most recent pages.
+* Returns the most recent links in results.
+* 2014-07-22
+*
+* @name MondeDiplo
+* @homepage http://www.monde-diplomatique.fr
+* @description Returns most recent results from MondeDiplo.
+* @maintainer Pitchoule
+*/
+
+class MondeDiploBridge extends BridgeAbstract{
+
+    public function collectData(array $param){
+        $link = 'http://www.monde-diplomatique.fr';
+        
+        $html = file_get_html($link) or $this->returnError('Could not request MondeDiplo. for : ' . $link , 404);
+    
+        foreach($html->find('div.laune') as $element) {
+            $item = new Item();
+            $item->uri = 'http://www.monde-diplomatique.fr'.$element->find('a', 0)->href;
+            $item->title = $element->find('h3', 0)->plaintext;
+            $item->content = $element->find('div.dates_auteurs', 0)->plaintext. '<br>' .strstr($element->find('div', 0)->plaintext, $element->find('div.dates_auteurs', 0)->plaintext, true);
+            $this->items[] = $item;
+        }
+
+        $liste = $html->find('div.listes', 0); // First list
+        foreach ($liste->find('li') as $e) {
+            
+            $item = new Item();
+            $item->uri = 'http://www.monde-diplomatique.fr' . $e->find('a', 0)->href;
+            $item->title = $e->find('a', 0)->plaintext;
+            $item->content = $e->find('div.dates_auteurs', 0)->plaintext;
+            $this->items[] = $item;
+        }
+
+        foreach($html->find('div.liste ul li') as $element) {
+            if ($element->getAttribute('class') != 'intrapub') {
+                 $item = new Item();
+                $item->uri = 'http://www.monde-diplomatique.fr'.$element->find('a', 0)->href;
+                $item->title = $element->find('h3', 0)->plaintext;
+                $item->content = $element->find('div.dates_auteurs', 0)->plaintext . ' <br> ' . $element->find('div.intro', 0)->plaintext;
+                $this->items[] = $item;
+            }
+        }
+        
+    }
+
+    public function getName(){
+        return 'Monde Diplomatique';
+    }
+
+    public function getURI(){
+        return 'http://www.monde-diplomatique.fr';
+    }
+
+    public function getCacheDuration(){
+        return 21600; // 6 hours
+    }
+}

+ 55 - 0
bridges/MsnMondeBridge.php

@@ -0,0 +1,55 @@
+<?php
+/**
+* RssBridgeMsnMonde 
+* Returns the 10 newest posts from MSN Actualités (full text)
+*
+* @name MSN Actu Monde
+* @homepage http://news.fr.msn.com/m6-actualite/monde/
+* @description Returns the 10 newest posts from MSN Actualités (full text)
+* @maintainer pit-fgfjiudghdf 
+* @update 2014-05-26
+*/
+class MsnMondeBridge extends BridgeAbstract{
+
+    public function collectData(array $param){
+
+    function MsnMondeExtractContent($url) {
+        $html2 = file_get_html($url);
+        $html2->find('div[id=m6_diaponews_placeholder]', 0)->outertext=''; //Supression de la partie "et aussi"
+        $text = $html2->find('div[class=svsubtorabs]', 0)->innertext; // ajout du resume
+        $text .= $html2->find('div[id=page1]', 0)->innertext;   // article
+        $text = preg_replace('/<p><strong>Lire aussi.*/i','',$text); //Supression de la partie "Lire aussi"
+
+        return $text;
+    }
+
+        $html = file_get_html('http://news.fr.msn.com/m6-actualite/RSS/News_RSS_Monde.aspx') or $this->returnError('Could not request MsnMonde.', 404);
+        $limit = 0;
+
+        foreach($html->find('item') as $element) {
+         if($limit < 10) {
+         $item = new \Item();
+         $item->title = $element->find('title', 0)->innertext;
+         $item->uri = $element->find('guid', 0)->plaintext;
+         $item->timestamp = strtotime($element->find('pubDate', 0)->plaintext);
+         $item->content = MsnMondeExtractContent($item->uri);
+         $this->items[] = $item;
+         $limit++;
+         }
+        }
+
+    }
+
+    public function getName(){
+        return 'MSN Actu Monde';
+    }
+
+    public function getURI(){
+        return 'http://news.fr.msn.com/m6-actualite/monde/';
+    }
+
+    public function getCacheDuration(){
+        return 3600; // 1 hour
+    }
+}
+

+ 52 - 0
bridges/MspabooruBridge.php

@@ -0,0 +1,52 @@
+<?php
+/**
+* RssBridgeMspabooru
+* Returns images from given page
+* 2014-05-25
+*
+* @name Mspabooru
+* @homepage http://mspabooru.com/
+* @description Returns images from given page
+* @maintainer mitsukarenai
+* @use1(p="page",t="tags")
+*/
+class MspabooruBridge extends BridgeAbstract{
+
+    public function collectData(array $param){
+	$page = 0;$tags='';
+        if (isset($param['p'])) { 
+		$page = (int)preg_replace("/[^0-9]/",'', $param['p']); 
+		$page = $page - 1;
+		$page = $page * 50;
+        }
+        if (isset($param['t'])) { 
+            $tags = urlencode($param['t']); 
+        }
+        $html = file_get_html("http://mspabooru.com/index.php?page=post&s=list&tags=$tags&pid=$page") or $this->returnError('Could not request Mspabooru.', 404);
+
+
+	foreach($html->find('div[class=content] span') as $element) {
+		$item = new \Item();
+		$item->uri = 'http://mspabooru.com/'.$element->find('a', 0)->href;
+		$item->postid = (int)preg_replace("/[^0-9]/",'', $element->getAttribute('id'));	
+		$item->timestamp = time();
+		$item->thumbnailUri = $element->find('img', 0)->src;
+		$item->tags = $element->find('img', 0)->getAttribute('alt');
+		$item->title = 'Mspabooru | '.$item->postid;
+		$item->content = '<a href="' . $item->uri . '"><img src="' . $item->thumbnailUri . '" /></a><br>Tags: '.$item->tags;
+		$this->items[] = $item; 
+	}
+    }
+
+    public function getName(){
+        return 'Mspabooru';
+    }
+
+    public function getURI(){
+        return 'http://mspabooru.com/';
+    }
+
+    public function getCacheDuration(){
+        return 1800; // 30 minutes
+    }
+}

+ 56 - 0
bridges/NasaApodBridge.php

@@ -0,0 +1,56 @@
+<?php
+/**
+* 2014-08-27
+* @name NASA APOD Bridge
+* @homepage http://apod.nasa.gov/apod/astropix.html
+* @description Returns the 3 latest NASA APOD pictures and explanations
+* @maintainer corenting
+*/
+class NasaApodBridge extends BridgeAbstract{
+
+  public function collectData(array $param) {
+
+    $html = file_get_html('http://apod.nasa.gov/apod/archivepix.html') or $this->returnError('Error while downloading the website content', 404);
+    $list = explode("<br>", $html->find('b', 0)->innertext);
+
+    for($i = 0; $i < 3;$i++)
+    {
+      $line = $list[$i];
+      $item = new \Item();
+
+      $uri_page = $html->find('a',$i + 3)->href;
+      $uri = 'http://apod.nasa.gov/apod/'.$uri_page;
+      $item->uri = $uri;
+
+      $picture_html = file_get_html($uri);
+      $picture_html_string = $picture_html->innertext;
+
+      //Extract image and explanation
+      $media = $picture_html->find('p',1)->innertext;
+      $media = strstr($media, '<br>');
+      $media = preg_replace('/<br>/', '', $media, 1);
+      $explanation = $picture_html->find('p',2)->innertext;
+
+      //Extract date from the picture page
+      $date = explode(" ", $picture_html->find('p',1)->innertext);
+      $item->timestamp = strtotime($date[4].$date[3].$date[2]);
+
+      //Other informations
+      $item->content = $media.'<br />'.$explanation;
+      $item->title = $picture_html->find('b',0)->innertext;
+      $this->items[] = $item;
+    }
+  }
+
+  public function getName(){
+    return 'NASA APOD';
+  }
+
+  public function getURI(){
+    return 'http://apod.nasa.gov/apod/astropix.html';
+  }
+
+  public function getCacheDuration(){
+    return 3600*12; // 12 hours
+  }
+}

+ 56 - 0
bridges/NextInpactBridge.php

@@ -0,0 +1,56 @@
+<?php
+/**
+* RssBridgeNextinpact
+* Returns the newest articles
+* 2014-05-25
+*
+* @name Nextinpact Bridge
+* @homepage http://www.nextinpact.com/
+* @description Returns the newest articles.
+* @maintainer qwertygc
+*/
+class NextInpactBridge extends BridgeAbstract{
+
+        public function collectData(array $param){
+
+			function StripCDATA($string) {
+			$string = str_replace('<![CDATA[', '', $string);
+			$string = str_replace(']]>', '', $string);
+			return $string;
+		}
+		function ExtractContent($url) {
+		$html2 = file_get_html($url);
+    $text = '<h2>'.$html2->find('div#actu_entete > h2', 0)->innertext.'</h2><br><br>';
+		$text = $text.$html2->find('div[itemprop=articleBody]', 0)->innertext;
+		return $text;
+		}
+		$html = file_get_html('http://www.nextinpact.com/rss/news.xml') or $this->returnError('Could not request Nextinpact.', 404);
+		$limit = 0;
+
+		foreach($html->find('item') as $element) {
+		 if($limit < 3) {
+		 $item = new \Item();
+		 $item->title = StripCDATA($element->find('title', 0)->innertext);
+		 $item->uri = StripCDATA($element->find('guid', 0)->plaintext);
+		 $item->timestamp = strtotime($element->find('pubDate', 0)->plaintext);
+		 $item->content = ExtractContent($item->uri);
+		 $this->items[] = $item;
+		 $limit++;
+		 }
+		}
+
+    }
+
+    public function getName(){
+        return 'Nextinpact Bridge';
+    }
+
+    public function getURI(){
+        return 'http://www.nextinpact.com/';
+    }
+
+    public function getCacheDuration(){
+        return 3600; // 1 hour
+		// return 0;
+    }
+}

+ 62 - 0
bridges/NiceMatinBridge.php

@@ -0,0 +1,62 @@
+<?php
+/**
+* RssBridgeNiceMatin 
+* Returns the 10 newest posts from Nice Matin (full text)
+*
+* @name NiceMatin
+* @homepage http://www.nicematin.com/
+* @description Returns the 10 newest posts from NiceMatin (full text)
+* @maintainer pit-fgfjiudghdf
+* @update 2014-05-26
+*/
+class NiceMatinBridge extends BridgeAbstract{
+
+    public function collectData(array $param){
+
+    function NiceMatinUrl($string) {
+        $string = str_replace('</link>', '', $string);
+        //$string = str_replace('.+', '', $string);
+        $string = preg_replace('/html.*http.*/i','html',$string);
+        $string = preg_replace('/.*http/i','http',$string);
+        return $string;
+    }
+
+    function NiceMatinExtractContent($url) {
+        $html2 = file_get_html($url);
+        $text = $html2->find('figure[itemprop=associatedMedia]', 0)->innertext;
+        $text .= $html2->find('div[id=content-article]', 0)->innertext;
+        return $text;
+    }
+
+        $html = file_get_html('http://www.nicematin.com/derniere-minute/rss') or $this->returnError('Could not request NiceMatin.', 404);
+        $limit = 0;
+
+        foreach($html->find('item') as $element) {
+         if($limit < 10) {
+         $item = new \Item();
+         //$item->title = NiceMatinStripCDATA($element->find('title', 0)->innertext);
+         $item->title = $element->find('title', 0)->innertext;
+         $item->uri = NiceMatinUrl($element->plaintext);
+
+         $item->timestamp = strtotime($element->find('pubDate', 0)->plaintext);
+         $item->content = NiceMatinExtractContent($item->uri);
+         $this->items[] = $item;
+         $limit++;
+         }
+        }
+
+    }
+
+    public function getName(){
+        return 'NiceMatin';
+    }
+
+    public function getURI(){
+        return 'http://www.nicematin.com/';
+    }
+
+    public function getCacheDuration(){
+        return 3600; // 1 hour
+    }
+}
+

+ 56 - 0
bridges/NumeramaBridge.php

@@ -0,0 +1,56 @@
+<?php
+/**
+* RssBridgeNumerama
+* Returns the 5 newest posts from http://www.numerama.com (full text)
+* 2014-05-25
+*
+* @name Numerama
+* @homepage http://www.numerama.com/
+* @description Returns the 5 newest posts from Numerama (full text)
+* @maintainer mitsukarenai
+*/
+class NumeramaBridge extends BridgeAbstract{
+
+    public function collectData(array $param){
+
+    function NumeramaStripCDATA($string) {
+    	$string = str_replace('<![CDATA[', '', $string);
+    	$string = str_replace(']]>', '', $string);
+    	return $string;
+    }
+    function NumeramaExtractContent($url) {
+	$html2 = file_get_html($url);
+	$text = $html2->find('h2.intro', 0)->innertext;
+	$text = $text.$html2->find('div.content', 0)->innertext;
+	$text = strip_tags($text, '<p><b><a><blockquote><img><em><ul><ol>');
+	return $text;
+    }
+        $html = file_get_html('http://www.numerama.com/rss/news.rss') or $this->returnError('Could not request Numerama.', 404);
+	$limit = 0;
+
+	foreach($html->find('item') as $element) {
+	 if($limit < 5) {
+	 $item = new \Item();
+	 $item->title = NumeramaStripCDATA($element->find('title', 0)->innertext);
+	 $item->uri = NumeramaStripCDATA($element->find('guid', 0)->plaintext);
+	 $item->timestamp = strtotime($element->find('pubDate', 0)->plaintext);
+	 $item->content = NumeramaExtractContent($item->uri);
+	 $this->items[] = $item;
+	 $limit++;
+	 }
+	}
+
+    }
+
+    public function getName(){
+        return 'Numerama';
+    }
+
+    public function getURI(){
+        return 'http://www.numerama.com/';
+    }
+
+    public function getCacheDuration(){
+        return 1800; // 30min
+    }
+}

+ 47 - 0
bridges/OpenClassroomsBridge.php

@@ -0,0 +1,47 @@
+<?php
+/**
+* RssBridgeOpenClassrooms
+* Retrieve lastest tutorials from OpenClassrooms.
+* Returns the most recent tutorials, sorting by date (most recent first).
+* 2014-05-25
+*
+* @name OpenClassrooms Bridge
+* @homepage http://fr.openclassrooms.com/
+* @description Returns latest tutorials from OpenClassrooms.
+* @maintainer sebsauvage
+* @use1(u="informatique or sciences")
+*/
+class OpenClassroomsBridge extends BridgeAbstract{
+
+    public function collectData(array $param){
+        if ($param['u']!='informatique' && $param['u']!='sciences')
+        {
+            $this->returnError('Error: You must chose "informatique" or "science".', 404);
+        }
+    
+        $html = '';
+        $link = 'http://fr.openclassrooms.com/'.$param['u'].'/cours?title=&sort=updatedAt+desc';
+
+        $html = file_get_html($link) or $this->returnError('Could not request OpenClassrooms.', 404);
+
+        foreach($html->find('li.col6') as $element) {
+                $item = new \Item();
+                $item->uri = 'http://fr.openclassrooms.com'.$element->find('a', 0)->href;
+                $item->title = $element->find('div.courses-content strong', 0)->innertext;
+                $item->content = $element->find('span.course-tags', 0)->innertext;
+                $this->items[] = $item;
+        }
+    }
+
+    public function getName(){
+        return 'OpenClassrooms';
+    }
+
+    public function getURI(){
+        return 'http://fr.openclassrooms.com';
+    }
+
+    public function getCacheDuration(){
+        return 21600; // 6 hours
+    }
+}

+ 59 - 0
bridges/OpenTheoryBridge.php

@@ -0,0 +1,59 @@
+<?php
+/**
+* RssBridgeOpenTheory
+* Returns the 5 newest posts from http://open1theory.com (full text)
+*
+* @name Opentheory
+* @description Returns the 5 newest posts from OpenTheory (full text)
+* @homepage http://open1theory.com
+*@maintainer qwertygc
+*/
+class OpenTheoryBridge extends BridgeAbstract{
+
+
+
+
+
+    public function collectData(array $param){
+
+    function StripCDATA($string) {
+    	$string = str_replace('<![CDATA[', '', $string);
+    	$string = str_replace(']]>', '', $string);
+    	return $string;
+    }
+    function ExtractContent($url) {
+	$html2 = file_get_html($url);
+	$text = $html2->find('div.entry-content', 0)->innertext;
+	$text = preg_replace('@<script[^>]*?>.*?</script>@si', '', $text);
+	return $text;
+    }
+        $html = file_get_html('http://open1theory.com/feed') or $this->returnError('Could not request OpenTheory.', 404);
+	$limit = 0;
+
+	foreach($html->find('item') as $element) {
+	 if($limit < 5) {
+	 $item = new \Item();
+	 $item->title = StripCDATA($element->find('title', 0)->innertext);
+	 $item->uri = StripCDATA($element->find('guid', 0)->plaintext);
+	 $item->timestamp = strtotime($element->find('pubDate', 0)->plaintext);
+	 $item->content = ExtractContent($item->uri);
+	 $this->items[] = $item;
+	 $limit++;
+	 }
+	}
+    
+    }
+
+    public function getName(){
+        return 'OpenTheory';
+    }
+
+    public function getURI(){
+        return 'http://open1theory.com/feed';
+    }
+
+    public function getCacheDuration(){
+        return 3600; // 1 hour
+        // return 0; // 1 hour
+    }
+}

+ 15 - 1
bridges/PinterestBridge.php

@@ -2,9 +2,12 @@
 /**
  * RssBridgePinterest
  * Returns the newest photos on a board
+* 2014-05-25
  *
  * @name Pinterest Bridge
+* @homepage http://www.pinterest.com/
  * @description Returns the newest images on a board
+* @maintainer pauder
  * @use1(u="username",b="board")
  * @use2(q="keyword")
  */
@@ -16,7 +19,18 @@ class PinterestBridge extends BridgeAbstract{
     
     public function collectData(array $param){
         $html = '';
-        if (isset($param['u']) && isset($param['b'])) {
+        if (isset($param['u']) || isset($param['b'])) {
+        
+            if (empty($param['u']))
+            {
+                $this->returnError('You must specify a Pinterest username (?u=...).', 400);
+            }
+
+            if (empty($param['b']))
+            {
+                $this->returnError('You must specify a Pinterest board for this username (?b=...).', 400);
+            }
+            
             $this->username = $param['u'];
             $this->board = $param['b'];
             $html = file_get_html($this->getURI().'/'.urlencode($this->username).'/'.urlencode($this->board)) or $this->returnError('Could not request Pinterest.', 404);

+ 45 - 0
bridges/PlanetLibreBridge.php

@@ -0,0 +1,45 @@
+<?php
+/**
+* RssBridgePlanetLibre
+* Returns the 5 newest posts from PlanetLibre (full text)
+*
+* @name PlanetLibre
+* @homepage http://www.planet-libre.org
+* @description Returns the 5 newest posts from PlanetLibre (full text)
+* @maintainer pit-fgfjiudghdf 
+* @update 2014-05-26
+*/
+class PlanetLibreBridge extends BridgeAbstract{
+    public function collectData(array $param){
+
+    function PlanetLibreExtractContent($url) {
+        $html2 = file_get_html($url);
+        $text = $html2->find('div[class="post-text"]', 0)->innertext;
+        return $text;
+    }
+        $html = file_get_html('http://www.planet-libre.org/') or $this->returnError('Could not request PlanetLibre.', 404);
+        $limit = 0;
+        foreach($html->find('div.post') as $element) {
+         if($limit < 5) {
+         $item = new \Item();
+         $item->title = $element->find('h1', 0)->plaintext;
+         $item->uri = $element->find('a', 0)->href;
+         $item->timestamp = strtotime(str_replace('/', '-', $element->find('div[class="post-date"]', 0)->plaintext));
+         $item->content = PlanetLibreExtractContent($item->uri);
+         $this->items[] = $item;
+         $limit++;
+         }
+        }
+
+    }
+    public function getName(){
+        return 'PlanetLibre';
+    }
+    public function getURI(){
+        return 'http://www.planet-libre.org/';
+    }
+    public function getCacheDuration(){
+        return 3600*2; // 1 hour
+    }
+}
+

+ 43 - 0
bridges/ProjectMGameBridge.php

@@ -0,0 +1,43 @@
+<?php
+/**
+* 2014-08-27
+* @name Project M Game Bridge
+* @homepage http://projectmgame.com/en/
+* @description Returns the newest articles.
+* @maintainer corenting
+*/
+class ProjectMGameBridge extends BridgeAbstract{
+
+  public function collectData(array $param){
+    $html = '';
+    $html = file_get_html('http://projectmgame.com/en/') or $this->returnError('Error while downloading the Project M homepage', 404);
+
+    foreach($html->find('article') as $article) {
+      $item = new \Item();
+      $item->uri = 'http://projectmgame.com/en/'.$article->find('section div.info_block a',0)->href;
+      $item->title = $article->find('h1 p',0)->innertext;
+
+      $p_list = $article->find('section p');
+      $content = '';
+      foreach($p_list as $p) $content .= $p->innertext;
+      $item->content = $content;
+
+      // get publication date
+      $str_date = $article->find('section div.info_block a',0)->innertext;
+      $item->timestamp = strtotime($str_date);
+      $this->items[] = $item;
+    }
+  }
+
+  public function getName(){
+    return 'Project M Game Bridge';
+  }
+
+  public function getURI(){
+    return 'http://projectmgame.com/en/';
+  }
+
+  public function getCacheDuration(){
+    return 10800; //3 hours
+  }
+}

+ 52 - 0
bridges/RaymondBridge.php

@@ -0,0 +1,52 @@
+<?php
+/**
+* RssBridgeRaymond
+* Returns the 3 newest posts from Raymond.cc (full text)
+*
+* @name Raymond
+* @homepage http://www.raymond.cc
+* @description Returns the 3 newest posts from Raymond.cc (full text)
+* @maintainer pit-fgfjiudghdf
+* @update 2014-05-26
+*/
+class RaymondBridge extends BridgeAbstract{
+    public function collectData(array $param){
+    function raymondStripCDATA($string) {
+        $string = str_replace('<![CDATA[', '', $string);
+        $string = str_replace(']]>', '', $string);
+        return $string;
+    }
+    function raymondExtractContent($url) {
+        $html2 = file_get_html($url);
+        $text = $html2->find('div.entry-content', 0)->innertext;
+	$text = preg_replace('/class="ad".*/', '', $text);
+	$text = strip_tags($text, '<p><a><i><strong><em><img>');
+	$text = str_replace('(adsbygoogle = window.adsbygoogle || []).push({});', '', $text);
+        return $text;
+    }
+        $html = file_get_html('http://www.raymond.cc/blog/feed') or $this->returnError('Could not request raymond.', 404);
+        $limit = 0;
+        foreach($html->find('item') as $element) {
+         if($limit < 3) {
+         $item = new \Item();
+         $item->title = raymondStripCDATA($element->find('title', 0)->innertext);
+         $item->uri = raymondStripCDATA($element->find('guid', 0)->plaintext);
+         $item->timestamp = strtotime($element->find('pubDate', 0)->plaintext);
+         $item->content = raymondExtractContent($item->uri);
+         $this->items[] = $item;
+         $limit++;
+         }
+        }
+
+    }
+    public function getName(){
+        return 'raymond';
+    }
+    public function getURI(){
+        return 'http://www.raymond.cc/blog';
+    }
+    public function getCacheDuration(){
+        return 3600*12; // 12 hour
+    }
+}
+

+ 52 - 0
bridges/Rule34Bridge.php

@@ -0,0 +1,52 @@
+<?php
+/**
+* RssBridgeRule34
+* Returns images from given page
+* 2014-05-25
+*
+* @name Rule34
+* @homepage http://rule34.xxx/
+* @description Returns images from given page
+* @maintainer mitsukarenai
+* @use1(p="page",t="tags")
+*/
+class Rule34Bridge extends BridgeAbstract{
+
+    public function collectData(array $param){
+	$page = 0;$tags='';
+        if (isset($param['p'])) { 
+		$page = (int)preg_replace("/[^0-9]/",'', $param['p']); 
+		$page = $page - 1;
+		$page = $page * 50;
+        }
+        if (isset($param['t'])) { 
+            $tags = urlencode($param['t']); 
+        }
+        $html = file_get_html("http://rule34.xxx/index.php?page=post&s=list&tags=$tags&pid=$page") or $this->returnError('Could not request Rule34.', 404);
+
+
+	foreach($html->find('div[class=content] span') as $element) {
+		$item = new \Item();
+		$item->uri = 'http://rule34.xxx/'.$element->find('a', 0)->href;
+		$item->postid = (int)preg_replace("/[^0-9]/",'', $element->getAttribute('id'));	
+		$item->timestamp = time();
+		$item->thumbnailUri = $element->find('img', 0)->src;
+		$item->tags = $element->find('img', 0)->getAttribute('alt');
+		$item->title = 'Rule34 | '.$item->postid;
+		$item->content = '<a href="' . $item->uri . '"><img src="' . $item->thumbnailUri . '" /></a><br>Tags: '.$item->tags;
+		$this->items[] = $item; 
+	}
+    }
+
+    public function getName(){
+        return 'Rule34';
+    }
+
+    public function getURI(){
+        return 'http://rule34.xxx/';
+    }
+
+    public function getCacheDuration(){
+        return 1800; // 30 minutes
+    }
+}

+ 50 - 0
bridges/Rule34pahealBridge.php

@@ -0,0 +1,50 @@
+<?php
+/**
+* RssBridgeRule34paheal
+* Returns images from given page
+* 2014-05-25
+*
+* @name Rule34paheal
+* @homepage http://rule34.paheal.net/
+* @description Returns images from given page
+* @maintainer mitsukarenai
+* @use1(p="page",t='tags")
+*/
+class Rule34pahealBridge extends BridgeAbstract{
+
+    public function collectData(array $param){
+	$page = 0;$tags='';
+        if (isset($param['p'])) { 
+            $page = (int)preg_replace("/[^0-9]/",'', $param['p']); 
+        }
+        if (isset($param['t'])) { 
+            $tags = urlencode($param['t']); 
+        }
+        $html = file_get_html("http://rule34.paheal.net/post/list/$tags/$page") or $this->returnError('Could not request Rule34paheal.', 404);
+
+
+	foreach($html->find('div[class=shm-image-list] div[class=shm-thumb]') as $element) {
+		$item = new \Item();
+		$item->uri = 'http://rule34.paheal.net'.$element->find('a', 0)->href;
+		$item->postid = (int)preg_replace("/[^0-9]/",'', $element->find('img', 0)->getAttribute('id'));	
+		$item->timestamp = time();
+		$item->thumbnailUri = $element->find('img', 0)->src;
+		$item->tags = $element->getAttribute('data-tags');
+		$item->title = 'Rule34paheal | '.$item->postid;
+		$item->content = '<a href="' . $item->uri . '"><img src="' . $item->thumbnailUri . '" /></a><br>Tags: '.$item->tags;
+		$this->items[] = $item; 
+	}
+    }
+
+    public function getName(){
+        return 'Rule34paheal';
+    }
+
+    public function getURI(){
+        return 'http://rule34.paheal.net/';
+    }
+
+    public function getCacheDuration(){
+        return 1800; // 30 minutes
+    }
+}

+ 52 - 0
bridges/SafebooruBridge.php

@@ -0,0 +1,52 @@
+<?php
+/**
+* RssBridgeSafebooru
+* Returns images from given page
+* 2014-05-25
+*
+* @name Safebooru
+* @homepage http://safebooru.org/
+* @description Returns images from given page
+* @maintainer mitsukarenai
+* @use1(p="page",t="tags")
+*/
+class SafebooruBridge extends BridgeAbstract{
+
+    public function collectData(array $param){
+	$page = 0;$tags='';
+        if (isset($param['p'])) { 
+		$page = (int)preg_replace("/[^0-9]/",'', $param['p']); 
+		$page = $page - 1;
+		$page = $page * 40;
+        }
+        if (isset($param['t'])) { 
+            $tags = urlencode($param['t']); 
+        }
+        $html = file_get_html("http://safebooru.org/index.php?page=post&s=list&tags=$tags&pid=$page") or $this->returnError('Could not request Safebooru.', 404);
+
+
+	foreach($html->find('div[class=content] span') as $element) {
+		$item = new \Item();
+		$item->uri = 'http://safebooru.org/'.$element->find('a', 0)->href;
+		$item->postid = (int)preg_replace("/[^0-9]/",'', $element->getAttribute('id'));	
+		$item->timestamp = time();
+		$item->thumbnailUri = $element->find('img', 0)->src;
+		$item->tags = $element->find('img', 0)->getAttribute('alt');
+		$item->title = 'Safebooru | '.$item->postid;
+		$item->content = '<a href="' . $item->uri . '"><img src="' . $item->thumbnailUri . '" /></a><br>Tags: '.$item->tags;
+		$this->items[] = $item; 
+	}
+    }
+
+    public function getName(){
+        return 'Safebooru';
+    }
+
+    public function getURI(){
+        return 'http://safebooru.org/';
+    }
+
+    public function getCacheDuration(){
+        return 1800; // 30 minutes
+    }
+}

+ 54 - 0
bridges/SakugabooruBridge.php

@@ -0,0 +1,54 @@
+<?php
+/**
+* RssBridgeSakugabooru 
+* Returns images from given page
+* 2014-05-25
+*
+* @name Sakugabooru
+* @homepage http://sakuga.yshi.org/
+* @description Returns images from given page
+* @maintainer mitsukarenai
+* @use1(p="page",t="tags")
+*/
+class SakugabooruBridge extends BridgeAbstract{
+
+    public function collectData(array $param){
+	$page = 1;$tags='';
+        if (isset($param['p'])) { 
+            $page = (int)preg_replace("/[^0-9]/",'', $param['p']); 
+        }
+        if (isset($param['t'])) { 
+            $tags = urlencode($param['t']); 
+        }
+        $html = file_get_html("http://sakuga.yshi.org/post?page=$page&tags=$tags") or $this->returnError('Could not request Sakugabooru.', 404);
+	$input_json = explode('Post.register(', $html);
+	foreach($input_json as $element)
+	 $data[] = preg_replace('/}\)(.*)/', '}', $element);
+	unset($data[0]);
+    
+        foreach($data as $datai) {
+	    $json = json_decode($datai, TRUE);
+            $item = new \Item();
+            $item->uri = 'http://sakuga.yshi.org/post/show/'.$json['id'];
+            $item->postid = $json['id'];
+            $item->timestamp = $json['created_at'];
+            $item->imageUri = $json['file_url'];
+            $item->thumbnailUri = $json['preview_url'];
+            $item->title = 'Sakugabooru | '.$json['id'];
+            $item->content = '<a href="' . $item->imageUri . '"><img src="' . $item->thumbnailUri . '" /></a><br>Tags: '.$json['tags']; 
+            $this->items[] = $item;
+        }
+    }
+
+    public function getName(){
+        return 'Sakugabooru';
+    }
+
+    public function getURI(){
+        return 'http://sakuga.yshi.org/post';
+    }
+
+    public function getCacheDuration(){
+        return 1800; // 30 minutes
+    }
+}

+ 54 - 0
bridges/ScilogsBridge.php

@@ -0,0 +1,54 @@
+<?php
+/**
+* RssBridgeScilogs
+* Returns the newest articles
+* 2014-05-25
+*
+* @name Scilogs Bridge
+* @homepage http://www.scilogs.fr/
+* @description Returns the newest articles.
+* @maintainer qwertygc
+*/
+class ScilogsBridge extends BridgeAbstract{
+    
+        public function collectData(array $param){
+
+			function ScilogsStripCDATA($string) {
+			$string = str_replace('<![CDATA[', '', $string);
+			$string = str_replace(']]>', '', $string);
+			return $string;
+		}
+		function ScilogsExtractContent($url) {
+		$html2 = file_get_html($url);
+		$text = $html2->find('div.entrybody', 0)->innertext;
+		return $text;
+		}
+		$html = file_get_html('http://www.scilogs.fr/?wpmu-feed=posts') or $this->returnError('Could not request Scilogs.', 404);
+		$limit = 0;
+
+		foreach($html->find('item') as $element) {
+		 if($limit < 10) {
+		 $item = new \Item();
+		 $item->title = ScilogsStripCDATA($element->find('title', 0)->innertext);
+		 $item->uri = ScilogsStripCDATA($element->find('guid', 0)->plaintext);
+		 $item->timestamp = strtotime($element->find('pubDate', 0)->plaintext);
+		 $item->content = ScilogsExtractContent($item->uri);
+		 $this->items[] = $item;
+		 $limit++;
+		 }
+		}
+    
+    }
+
+    public function getName(){
+        return 'Scilogs Bridge';
+    }
+
+    public function getURI(){
+        return 'http://scilogs.fr/';
+    }
+
+    public function getCacheDuration(){
+        return 3600*2; // 2 hours
+    }
+}

+ 52 - 0
bridges/ScmbBridge.php

@@ -0,0 +1,52 @@
+<?php
+/**
+* RssBridgeSeCoucherMoinsBete 
+* Returns the newest anecdotes
+* 2014-05-25
+*
+* @name Se Coucher Moins Bête Bridge
+* @homepage http://secouchermoinsbete.fr/
+* @description Returns the newest anecdotes.
+* @maintainer Astalaseven
+*/
+class ScmbBridge extends BridgeAbstract{
+    
+    public function collectData(array $param){
+        $html = '';
+        $html = file_get_html('http://secouchermoinsbete.fr/') or $this->returnError('Could not request Se Coucher Moins Bete.', 404);
+    
+        foreach($html->find('article') as $article) {
+        	$item = new \Item();
+			$item->uri = 'http://secouchermoinsbete.fr'.$article->find('p.summary a',0)->href;
+			$item->title = $article->find('header h1 a',0)->innertext;
+			
+			$article->find('span.read-more',0)->outertext=''; // remove text "En savoir plus" from anecdote content
+			$content = $article->find('p.summary a',0)->innertext;
+			$content =substr($content,0,strlen($content)-17); // remove superfluous spaces at the end
+			
+			// get publication date
+			$str_date = $article->find('time',0)->datetime;
+			list($date, $time) = explode(' ', $str_date);
+			list($y, $m, $d) = explode('-', $date);
+			list($h, $i) = explode(':', $time);
+			$timestamp = mktime($h,$i,0,$m,$d,$y);
+			$item->timestamp = $timestamp;
+			
+			
+			$item->content = $content;
+			$this->items[] = $item;
+		}
+    }
+
+    public function getName(){
+        return 'Se Coucher Moins Bête Bridge';
+    }
+
+    public function getURI(){
+        return 'http://secouchermoinsbete.fr/';
+    }
+
+    public function getCacheDuration(){
+        return 21600; // 6 hours
+    }
+}

+ 48 - 0
bridges/ScoopItBridge.php

@@ -0,0 +1,48 @@
+<?php
+/**
+* RssBridgeScoopIt
+* Search DScoopIt for most recent pages regarding a specific topic.
+* Returns the most recent links in results, sorting by date (most recent first).
+* 2014-06-13
+*
+* @name ScoopIt
+* @homepage http://www.scoop.it
+* @description Returns most recent results from ScoopIt.
+* @maintainer Pitchoule
+* @use1(u="keyword")
+*/
+class ScoopItBridge extends BridgeAbstract{
+
+    public function collectData(array $param){
+        $html = '';
+        if ($param['u'] != '') {
+            $this->request = $param['u'];
+            $link = 'http://scoop.it/search?q=' .urlencode($this->request);
+            
+            $html = file_get_html($link) or $this->returnError('Could not request ScoopIt. for : ' . $link , 404);
+            
+            foreach($html->find('div.post-view') as $element) {
+                $item = new Item();
+                $item->uri = $element->find('a', 0)->href;
+                $item->title = preg_replace('~[[:cntrl:]]~', '', $element->find('div.tCustomization_post_title',0)->plaintext);
+                $item->content = preg_replace('~[[:cntrl:]]~', '', $element->find('div.tCustomization_post_description', 0)->plaintext);
+                $this->items[] = $item;
+            }
+        } else {
+            $this->returnError('You must specify a keyword', 404);
+        }
+    }
+
+    public function getName(){
+        return 'ScooptIt';
+    }
+
+    public function getURI(){
+        return 'http://Scoop.it';
+    }
+
+    public function getCacheDuration(){
+        return 21600; // 6 hours
+    }
+}
+

+ 56 - 0
bridges/SegfaultMintBridge.php

@@ -0,0 +1,56 @@
+<?php
+/**
+* @name SegfaultMint
+* @homepage http://segfault.linuxmint.com/
+* @description Returns the 5 newest posts from SegfaultMint (full text)
+* @maintainer qwertygc
+* @update 2014-07-05
+*/
+class SegfaultMintBridge extends BridgeAbstract{
+
+
+
+
+
+    public function collectData(array $param){
+
+    function StripCDATA($string) {
+    	$string = str_replace('<![CDATA[', '', $string);
+    	$string = str_replace(']]>', '', $string);
+    	return $string;
+    }
+    function ExtractContent($url) {
+	$html2 = file_get_html($url);
+	$text = $html2->find('div.post-bodycopy', 0)->innertext;
+	$text = preg_replace('@<script[^>]*?>.*?</script>@si', '', $text);
+	return $text;
+    }
+        $html = file_get_html('http://segfault.linuxmint.com/feed/') or $this->returnError('Could not request segfault.', 404);
+	$limit = 0;
+
+	foreach($html->find('item') as $element) {
+	 if($limit < 5) {
+	 $item = new \Item();
+	 $item->title = StripCDATA($element->find('title', 0)->innertext);
+	 $item->uri = StripCDATA($element->find('guid', 0)->plaintext);
+	 $item->timestamp = strtotime($element->find('pubDate', 0)->plaintext);
+	 $item->content = ExtractContent($item->uri);
+	 $this->items[] = $item;
+	 $limit++;
+	 }
+	}
+    
+    }
+
+    public function getName(){
+        return 'Segfault Mint';
+    }
+
+    public function getURI(){
+        return 'http://segfault.linuxmint.com/feed/';
+    }
+
+    public function getCacheDuration(){
+        return 3600*24; // 24 hours
+    }
+}

+ 91 - 0
bridges/Sexactu.php

@@ -0,0 +1,91 @@
+<?php
+/**
+* 2014-05-26
+*
+* @name Sexactu
+* @homepage http://www.gqmagazine.fr/sexactu
+* @description Sexactu
+* @maintainer Riduidel
+* @update 04/02/2014
+*/
+define("GQ", "http://www.gqmagazine.fr");
+class Sexactu extends BridgeAbstract{
+
+    public function collectData(array $param){
+$find = array('janvier', 'février', 'mars', 'avril', 'mai', 'juin', 'juillet', 'août', 'septembre', 'novembre', 'décembre');
+$replace = array('January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December');
+
+    $html = file_get_html($this->getURI()) or $this->returnError('Could not request '.$this->getURI(), 404);
+
+        foreach($html->find('.content-holder') as $contentHolder) {
+            // only use first list as second one only contains pages numbers 
+            $articles = $contentHolder->find('ul', 0);
+            foreach($articles->find('li') as $element) {
+                // if you ask about that method_exists, there seems to be a bug in simple html dom
+                // see stackoverflow for more details : http://stackoverflow.com/a/10828479/15619
+                if(is_object($element)) {
+                    $item = new Item();
+                    // various metadata
+                    $titleBlock = $element->find('.title-holder', 0);
+                    if(is_object($titleBlock)) {
+                        $titleDetails = $titleBlock->find('.article-title',0);
+                        $titleData = $titleDetails->find('h2', 0)->find('a',0);
+                        $titleTimestamp =$titleDetails->find('h4',0);
+                        $item->title = $this->correctCase(trim($titleData->innertext));
+                        $item->uri = GQ.$titleData->href;
+
+                        // Fugly date parsing due to the fact my DNS-323 doesn't support php intl extension
+                        $dateText = $titleTimestamp->innertext;
+                        $dateText = substr($dateText, strpos($dateText,',')+1);
+                        $dateText = str_replace($find, $replace, strtolower($dateText));
+                        $date = strtotime($dateText); 
+                        $item->timestamp = $date;
+
+                        $item->name = "Maïa Mazaurette";
+                        $elementText = $element->find('.text-container', 0);
+                        // don't forget to replace images server url with gq one
+                        foreach($elementText->find('img') as $image) {
+                            $image->src = GQ.$image->src;
+                        }
+                        $item->content = $elementText->innertext;
+                        $this->items[] = $item;
+                    }
+                    
+                }
+                
+            }
+        }
+    }
+
+    public function getName(){
+        return 'Sexactu';
+    }
+
+    public function getURI(){
+        return GQ.'/sexactu';
+    }
+
+    public function getCacheDuration(){
+        return 7200; // 2h hours
+    }
+    public function getDescription(){
+        return "Sexactu";
+    }
+    
+    public function correctCase($str) {
+        $sentences=explode('.', mb_strtolower($str, "UTF-8"));
+        $str="";
+        $sep="";
+        foreach ($sentences as $sentence)
+        {
+           //upper case first char
+           $sentence=ucfirst(trim($sentence));
+        
+           //append sentence to output
+           $str=$str.$sep.$sentence;
+           $sep=". ";
+        }
+        return $str;
+    }
+}
+

+ 55 - 0
bridges/SoundcloudBridge.php

@@ -0,0 +1,55 @@
+<?php
+/**
+* SoundcloudBridge
+* Returns the newest music from user
+*
+* @name Soundcloud Bridge
+* @homepage http://www.soundcloud.com/
+* @description Returns 10 newest music from user profile 
+* @maintainer kranack
+* @update 2014-07-24
+* @use1(u="username")
+*
+*/
+class SoundCloudBridge extends BridgeAbstract{
+    
+	private $request;
+	private $name;
+    
+	public function collectData(array $param){
+		
+		if (isset($param['u']) && !empty($param['u']))
+		{
+			$this->request = $param['u'];
+           	
+			$res = json_decode(file_get_contents('http://api.soundcloud.com/resolve.json?url=http://www.soundcloud.com/'. urlencode($this->request) .'&consumer_key=apigee')) or $this->returnError('No results for this query', 404);
+			$tracks = json_decode(file_get_contents('http://api.soundcloud.com/users/'. urlencode($res->id) .'/tracks.json?consumer_key=apigee')) or $this->returnError('No results for this user', 404);
+		} 
+		else
+		{
+			$this->returnError('You must specify username', 400);
+		}
+
+		for ($i=0; $i < 10; $i++) {
+		    $item = new \Item();
+		    $item->name = $tracks[$i]->user->username .' - '. $tracks[$i]->title;
+		    $item->title = $tracks[$i]->user->username .' - '. $tracks[$i]->title;
+		    $item->content = '<audio src="'. $tracks[$i]->uri .'/stream?consumer_key=apigee">';
+		    $item->id = 'https://soundcloud.com/'. urlencode($this->request) .'/'. urlencode($tracks[$i]->permalink);
+		    $item->uri = 'https://soundcloud.com/'. urlencode($this->request) .'/'. urlencode($tracks[$i]->permalink);
+		    $this->items[] = $item;
+		}
+		
+    }
+	public function getName(){
+		return (!empty($this->name) ? $this->name .' - ' : '') .'Soundcloud Bridge';
+	}
+
+	public function getURI(){
+		return 'http://www.soundcloud.com/';
+	}
+
+	public function getCacheDuration(){
+		return 600; // 10 minutes
+	}
+}

+ 50 - 0
bridges/TagBoardBridge.php

@@ -0,0 +1,50 @@
+<?php
+/**
+* RssBridgeTagBoard
+* Search TagBoard for most recent pages regarding a specific topic.
+* Returns the most recent links in results, sorting by date (most recent first).
+* 2014-09-10
+*
+* @name TagBoard
+* @homepage http://www.TagBoard.com
+* @description Returns most recent results from TagBoard.
+* @maintainer Pitchoule
+* @use1(u="keyword")
+*/
+class TagBoardBridge extends BridgeAbstract{
+
+    public function collectData(array $param){
+        $html = '';
+        $this->request = $param['u'];
+        $link = 'https://post-cache.tagboard.com/search/' .$this->request;
+		
+        $html = file_get_html($link) or $this->returnError('Could not request TagBoard for : ' . $link , 404);
+        $parsed_json = json_decode($html);
+
+        foreach($parsed_json->{'posts'} as $element) {
+                $item = new Item();
+                $item->uri = $element->{'permalink'};
+		$item->title = $element->{'text'};
+                $item->thumbnailUri = $element->{'photos'}[0]->{'m'};
+                if (isset($item->thumbnailUri)) {
+                  $item->content = '<a href="' . $item->uri . '"><img src="' . $item->thumbnailUri . '" /></a>';
+                }else{
+                  $item->content = $element->{'html'};
+                }
+                $this->items[] = $item;
+        }
+    }
+
+    public function getName(){
+        return 'tagboard - ' .$this->request;
+    }
+
+    public function getURI(){
+        return 'http://TagBoard.com';
+    }
+
+    public function getCacheDuration(){
+        return 21600; // 6 hours
+    }
+}
+							

+ 52 - 0
bridges/TbibBridge.php

@@ -0,0 +1,52 @@
+<?php
+/**
+* RssBridgeTbib
+* Returns images from given page
+* 2014-05-25
+*
+* @name Tbib
+* @homepage http://tbib.org/
+* @description Returns images from given page
+* @maintainer mitsukarenai
+* @use1(p="page",t="tags")
+*/
+class TbibBridge extends BridgeAbstract{
+
+    public function collectData(array $param){
+	$page = 0;$tags='';
+        if (isset($param['p'])) { 
+		$page = (int)preg_replace("/[^0-9]/",'', $param['p']); 
+		$page = $page - 1;
+		$page = $page * 50;
+        }
+        if (isset($param['t'])) { 
+            $tags = urlencode($param['t']); 
+        }
+        $html = file_get_html("http://tbib.org/index.php?page=post&s=list&tags=$tags&pid=$page") or $this->returnError('Could not request Tbib.', 404);
+
+
+	foreach($html->find('div[class=content] span') as $element) {
+		$item = new \Item();
+		$item->uri = 'http://tbib.org/'.$element->find('a', 0)->href;
+		$item->postid = (int)preg_replace("/[^0-9]/",'', $element->getAttribute('id'));	
+		$item->timestamp = time();
+		$item->thumbnailUri = $element->find('img', 0)->src;
+		$item->tags = $element->find('img', 0)->getAttribute('alt');
+		$item->title = 'Tbib | '.$item->postid;
+		$item->content = '<a href="' . $item->uri . '"><img src="' . $item->thumbnailUri . '" /></a><br>Tags: '.$item->tags;
+		$this->items[] = $item; 
+	}
+    }
+
+    public function getName(){
+        return 'Tbib';
+    }
+
+    public function getURI(){
+        return 'http://tbib.org/';
+    }
+
+    public function getCacheDuration(){
+        return 1800; // 30 minutes
+    }
+}

+ 58 - 0
bridges/TheCodingLoveBridge.php

@@ -0,0 +1,58 @@
+<?php
+/**
+*
+* @name The Coding Love
+* @homepage http://thecodinglove.com/
+* @description The Coding Love
+* @update 30/01/2014
+* initial maintainer: superbaillot.net
+*/
+class TheCodingLoveBridge extends BridgeAbstract{
+
+    public function collectData(array $param){
+        $html = file_get_html('http://thecodinglove.com/') or $this->returnError('Could not request The Coding Love.', 404);
+    
+        foreach($html->find('div.post') as $element) {
+            $item = new Item();
+            $temp = $element->find('h3 a', 0);
+            
+            $titre = $temp->innertext;
+            $url = $temp->href;
+            
+            $temp = $element->find('div.bodytype', 0);
+            $content = $temp->innertext;
+            
+            $auteur = $temp->find('.c1 em', 0);
+            $pos = strpos($auteur->innertext, "by");
+            
+            if($pos > 0)
+            {
+                $auteur = trim(str_replace("*/", "", substr($auteur->innertext, ($pos + 2))));
+                $item->name = $auteur;
+            }
+            
+            
+            $item->content .= trim($content);
+            $item->uri = $url;
+            $item->title = trim($titre);
+            
+            $this->items[] = $item;
+        }
+    }
+
+    public function getName(){
+        return 'The Coding Love';
+    }
+
+    public function getURI(){
+        return 'http://thecodinglove.com/';
+    }
+
+    public function getCacheDuration(){
+        return 7200; // 2h hours
+    }
+    public function getDescription(){
+        return "The Coding Love via rss-bridge";
+    }
+}
+?>

+ 90 - 0
bridges/ThePirateBayBridge.php

@@ -0,0 +1,90 @@
+<?php
+/**
+* RssBridgeThePirateBay
+* Returns results for the keywords. You can put several list of keywords by separating them with a semicolon (e.g. "one show;another show")
+* 2014-05-25
+*
+* @name The Pirate Bay
+* @homepage https://thepiratebay.se/
+* @description Returns results for the keywords. You can put several list of keywords by separating them with a semicolon (e.g. "one show;another show")
+* @maintainer mitsukarenai
+* @update 2014-05-26
+* @use1(q="first list;second list;...")
+ */
+
+class ThePirateBayBridge extends BridgeAbstract{
+
+	public function collectData(array $param){
+
+        function parseDateTimestamp($element){
+                $guessedDate = $element->find('font',0)->plaintext;
+                $guessedDate = explode("Uploaded ",$guessedDate)[1];
+                $guessedDate = explode(",",$guessedDate)[0];
+                if (count(explode(":",$guessedDate)) == 1)
+                {
+                    $guessedDate = strptime($guessedDate, '%m-%d&nbsp;%Y');
+                    $timestamp   = mktime(0, 0, 0,
+                                          $guessedDate['tm_mon'] + 1, $guessedDate['tm_mday'], 1900+$guessedDate['tm_year']);
+                }
+                else if (explode("&nbsp;",$guessedDate)[0] == 'Today')
+                {
+                    $guessedDate = strptime(explode("&nbsp;",$guessedDate)[1], '%H:%M');
+                    $timestamp   = mktime($guessedDate['tm_hour'],    $guessedDate['tm_min'],  0,
+                                          date('m'), date('d'), date('Y'));
+
+                }
+                else if (explode("&nbsp;",$guessedDate)[0] == 'Y-day')
+                {
+                    $guessedDate = strptime(explode("&nbsp;",$guessedDate)[1], '%H:%M');
+                    $timestamp   = mktime($guessedDate['tm_hour'],    $guessedDate['tm_min'],  0,
+                                          date('m',time()-24*60*60), date('d',time()-24*60*60), date('Y',time()-24*60*60));
+
+                }
+                else
+                {
+                    $guessedDate = strptime($guessedDate, '%m-%d&nbsp;%H:%M');
+                    $timestamp   = mktime($guessedDate['tm_hour'],    $guessedDate['tm_min'],  0,
+                                          $guessedDate['tm_mon'] + 1, $guessedDate['tm_mday'], date('Y'));
+                }
+                return $timestamp;
+        }
+
+
+		if (!isset($param['q']))
+			$this->returnError('You must specify keywords (?q=...)', 400);
+
+        $keywordsList = explode(";",$param['q']); 
+        foreach($keywordsList as $keywords){
+            $html = file_get_html('https://thepiratebay.se/search/'.rawurlencode($keywords).'/0/3/0') or $this->returnError('Could not request TPB.', 404);
+
+            if ($html->find('table#searchResult', 0) == FALSE)
+                $this->returnError('No result for query '.$keywords, 404);
+
+
+            foreach($html->find('tr') as $element) {
+                $item = new \Item();
+                $item->uri = 'https://thepiratebay.se/'.$element->find('a.detLink',0)->href;
+                $item->id = $item->uri;
+                $item->timestamp = parseDateTimestamp($element);
+                $item->title = $element->find('a.detLink',0)->plaintext;
+                $item->seeders = (int)$element->find('td',2)->plaintext;
+                $item->leechers = (int)$element->find('td',3)->plaintext;
+                $item->content = $element->find('font',0)->plaintext.'<br>seeders: '.$item->seeders.' | leechers: '.$item->leechers.'<br><a href="'.$element->find('a',3)->href.'">download</a>';
+                if(!empty($item->title))
+                    $this->items[] = $item;
+            }
+        }
+	}
+    
+    public function getName(){
+        return 'The Pirate Bay';
+    }
+
+    public function getURI(){
+        return 'https://thepiratebay.se/';
+    }
+
+    public function getCacheDuration(){
+        return 3600; // 1 hour
+    }
+}

+ 62 - 0
bridges/TuxboardBridge.php

@@ -0,0 +1,62 @@
+<?php
+/**
+ *
+* @name Tuxboard
+* @homepage http://www.tuxboard.com/
+* @description Tuxboard
+* @update 2014-07-08
+* initial maintainer: superbaillot.net
+ */
+class TuxboardBridge extends BridgeAbstract{
+
+    public function collectData(array $param){
+
+    function StripCDATA($string) {
+    	$string = str_replace('<![CDATA[', '', $string);
+    	$string = str_replace(']]>', '', $string);
+    	return $string;
+    }
+
+    function ExtractContent($url) {
+	$html2 = file_get_html($url);
+	$text = $html2->find('article#page', 0)->innertext;
+	$text = preg_replace('@<script[^>]*?>.*?</script>@si', '', $text);
+	return $text;
+    }
+
+        $html = file_get_html('http://www.tuxboard.com/feed/atom/') or $this->returnError('Could not request Tuxboard.', 404);
+	$limit = 0;
+
+	foreach($html->find('entry') as $element) {
+	 if($limit < 10) {
+	 $item = new \Item();
+	 $item->title = StripCDATA($element->find('title', 0)->innertext);
+	 $item->uri = $element->find('link', 0)->href;
+	 $item->timestamp = strtotime($element->find('published', 0)->plaintext);
+	 $item->content = ExtractContent($item->uri);
+	 $this->items[] = $item;
+	 $limit++;
+	 }
+	}
+
+       
+        
+    }
+
+    public function getName(){
+        return 'Tuxboard';
+    }
+
+    public function getURI(){
+        return 'http://www.tuxboard.com';
+    }
+
+    public function getDescription(){
+        return 'Tuxboard via rss-bridge';
+    }
+
+    public function getCacheDuration(){
+        return 3600; // 1 hour
+    }
+}
+?>

+ 20 - 5
bridges/TwitterBridge.php

@@ -2,9 +2,12 @@
 /**
 * RssBridgeTwitter 
 * Based on https://github.com/mitsukarenai/twitterbridge-noapi
+* 2014-05-25
 *
 * @name Twitter Bridge
+* @homepage http://twitter.com/
 * @description Returns user timelines or keyword/hashtag search results (without using their API).
+* @maintainer mitsukarenai
 * @use1(q="keyword or #hashtag")
 * @use2(u="username")
 */
@@ -26,15 +29,27 @@ class TwitterBridge extends BridgeAbstract{
             $this->returnError('You must specify a keyword (?q=...) or a Twitter username (?u=...).', 400);
         }
 
-        foreach($html->find('div.tweet') as $tweet) {
+        foreach($html->find('div.js-stream-tweet') as $tweet) {
             $item = new \Item();
-            $item->username = trim(substr($tweet->find('span.username', 0)->plaintext, 1));	// extract username and sanitize
+            $item->username = $tweet->getAttribute('data-screen-name');	// extract username and sanitize
             $item->fullname = $tweet->getAttribute('data-name'); // extract fullname (pseudonym)
             $item->avatar = $tweet->find('img', 0)->src;	// get avatar link
             $item->id = $tweet->getAttribute('data-tweet-id');	// get TweetID
-            $item->uri = 'https://twitter.com'.$tweet->find('a.details', 0)->getAttribute('href');	// get tweet link
-            $item->timestamp = $tweet->find('span._timestamp', 0)->getAttribute('data-time');	// extract tweet timestamp
-            $item->content = str_replace('href="/', 'href="https://twitter.com/', strip_tags($tweet->find('p.tweet-text', 0)->innertext, '<a>'));	// extract tweet text
+            $item->uri = 'https://twitter.com'.$tweet->find('a.js-permalink', 0)->getAttribute('href');	// get tweet link
+            $item->timestamp = $tweet->find('span.js-short-timestamp', 0)->getAttribute('data-time');	// extract tweet timestamp
+		// processing content links
+		foreach($tweet->find('a') as $link) {
+			if($link->hasAttribute('data-expanded-url') ) {
+				$link->href = $link->getAttribute('data-expanded-url');
+			}
+			$link->removeAttribute('data-expanded-url');
+			$link->removeAttribute('data-query-source');
+			$link->removeAttribute('rel');
+			$link->removeAttribute('class');
+			$link->removeAttribute('target');
+			$link->removeAttribute('title');
+		}
+            $item->content = str_replace('href="/', 'href="https://twitter.com/', strip_tags($tweet->find('p.js-tweet-text', 0)->innertext, '<a>'));	// extract tweet text
             $item->title = $item->fullname . ' (@'. $item->username . ') | ' . $item->content;
             $this->items[] = $item;
         }

+ 78 - 0
bridges/TwitterBridgeExtended.php

@@ -0,0 +1,78 @@
+<?php
+/**
+* RssBridgeTwitter 
+* Based on https://github.com/mitsukarenai/twitterbridge-noapi
+* 2014-05-25
+*
+* @name Twitter Bridge Extended
+* @homepage https://twitter.com/
+* @description (same as Twitter Bridge, but with avatar, replies and RTs)
+* @maintainer mitsukarenai
+* @use1(q="keyword or hashtag")
+* @use2(u="username")
+*/
+class TwitterBridgeExtended extends BridgeAbstract{
+
+	public function collectData(array $param){
+		$html = ''; 
+		if (isset($param['q'])) {   /* keyword search mode */
+			$html = file_get_html('https://twitter.com/search/realtime?q='.urlencode($param['q']).'+include:retweets&src=typd') or $this->returnError('No results for this query.', 404);
+		}
+		elseif (isset($param['u'])) {   /* user timeline mode */
+			$html = file_get_html('https://twitter.com/'.urlencode($param['u']).'/with_replies') or $this->returnError('Requested username can\'t be found.', 404);
+		}
+		else {
+			$this->returnError('You must specify a keyword (?q=...) or a Twitter username (?u=...).', 400);
+		}
+
+		foreach($html->find('div.js-stream-tweet') as $tweet) {
+			$item = new \Item();
+			// extract username and sanitize
+			$item->username = $tweet->getAttribute('data-screen-name');
+			// extract fullname (pseudonym)
+			$item->fullname = $tweet->getAttribute('data-name'); 
+			// get avatar link
+			$item->avatar = $tweet->find('img', 0)->src;	
+			// get TweetID
+			$item->id = $tweet->getAttribute('data-tweet-id');
+			// get tweet link	
+			$item->uri = 'https://twitter.com'.$tweet->find('a.js-permalink', 0)->getAttribute('href');	
+			// extract tweet timestamp
+			$item->timestamp = $tweet->find('span.js-short-timestamp', 0)->getAttribute('data-time');
+			// extract plaintext	
+			$item->content_simple = str_replace('href="/', 'href="https://twitter.com/', html_entity_decode(strip_tags($tweet->find('p.js-tweet-text', 0)->innertext, '<a>'))); 
+	
+			// processing content links
+			foreach($tweet->find('a') as $link) {
+				if($link->hasAttribute('data-expanded-url') ) {
+					$link->href = $link->getAttribute('data-expanded-url');
+				}
+				$link->removeAttribute('data-expanded-url');
+				$link->removeAttribute('data-query-source');
+				$link->removeAttribute('rel');
+				$link->removeAttribute('class');
+				$link->removeAttribute('target');
+				$link->removeAttribute('title');
+			}
+
+			// get tweet text
+			$item->content = '<a href="https://twitter.com/'.$item->username.'"><img style="align:top;width:75px;" alt="avatar" src="'.$item->avatar.'" />'.$item->username.'</a> '.$item->fullname.'<br/><blockquote>'.str_replace('href="/', 'href="https://twitter.com/', $tweet->find('p.js-tweet-text', 0)->innertext).'</blockquote>';
+			// generate the title
+			$item->title = $item->fullname . ' (@'. $item->username . ') | ' . $item->content_simple;
+			// put out
+			$this->items[] = $item;
+		}
+	}
+
+	public function getName(){
+		return 'Twitter Bridge Extended';
+	}
+
+	public function getURI(){
+		return 'http://twitter.com';
+	}
+
+	public function getCacheDuration(){
+		return 300; // 5 minutes
+	}
+}

+ 54 - 0
bridges/WakkuwakkuBridge.php

@@ -0,0 +1,54 @@
+<?php
+/**
+* RssBridgeWakkuWakku 
+* Returns images from given page
+* 2014-05-25
+*
+* @name WakkuWakku
+* @homepage http://wakku.to/
+* @description Returns images from given page
+* @maintainer mitsukarenai
+* @use1(p="page",t="tags")
+*/
+class WakkuWakkuBridge extends BridgeAbstract{
+
+    public function collectData(array $param){
+	$page = 1;$tags='';
+        if (isset($param['p'])) { 
+            $page = (int)preg_replace("/[^0-9]/",'', $param['p']); 
+        }
+        if (isset($param['t'])) { 
+            $tags = urlencode($param['t']); 
+        }
+        $html = file_get_html("http://wakku.to/post?page=$page&tags=$tags") or $this->returnError('Could not request WakkuWakku.', 404);
+	$input_json = explode('Post.register(', $html);
+	foreach($input_json as $element)
+	 $data[] = preg_replace('/}\)(.*)/', '}', $element);
+	unset($data[0]);
+    
+        foreach($data as $datai) {
+	    $json = json_decode($datai, TRUE);
+            $item = new \Item();
+            $item->uri = 'http://wakku.to/post/show/'.$json['id'];
+            $item->postid = $json['id'];
+            $item->timestamp = strtotime($json['created_at']);
+            $item->imageUri = 'http://wakku.to/'.$json['file_url'];
+            $item->thumbnailUri = 'http://wakku.to/'.$json['preview_url'];
+            $item->title = 'WakkuWakku | '.$json['id'];
+            $item->content = '<a href="' . $item->imageUri . '"><img src="' . $item->thumbnailUri . '" /></a><br>Tags: '.$json['tags']; 
+            if(isset($json['id']) and !empty($json['id'])) $this->items[] = $item;
+        }
+    }
+
+    public function getName(){
+        return 'WakkuWakku';
+    }
+
+    public function getURI(){
+        return 'http://wakku.to/';
+    }
+
+    public function getCacheDuration(){
+        return 1800; // 30 minutes
+    }
+}

+ 66 - 0
bridges/WhydBridge.php

@@ -0,0 +1,66 @@
+<?php
+/**
+* WhydBridge
+* Returns the newest music from user
+*
+* @name Whyd Bridge
+* @homepage http://www.whyd.com/
+* @description Returns 10 newest music from user profile 
+* @maintainer kranack
+* @update 2014-07-18
+* @use1(u="username/id")
+*
+*/
+class WhydBridge extends BridgeAbstract{
+    
+	private $request;
+	private $name;
+    
+	public function collectData(array $param){
+		$html = '';
+		if (isset($param['u']))
+		{
+			$this->request = $param['u'];
+            if (strlen(preg_replace("/[^0-9a-f]/",'', $this->request)) == 24) { // is input the userid ?
+				$html = file_get_html('http://www.whyd.com/u/'.preg_replace("/[^0-9a-f]/",'', $this->request)) or $this->returnError('No results for this query.', 404);
+			} else { // input may be the username
+				$html = file_get_html('http://www.whyd.com/search?q='.urlencode($this->request)) or $this->returnError('No results for this query.', 404);
+				for ($j = 0; $j < 5; $j++) {
+					if (strtolower($html->find('div.user', $j)->find('a',0)->plaintext) == strtolower($this->request)) {
+						$html = file_get_html('http://www.whyd.com' . $html->find('div.user', $j)->find('a', 0)->getAttribute('href')) or $this->returnError('No results for this query', 404);
+						break;
+					}
+				}
+			}
+            $this->name = $html->find('div#profileTop', 0)->find('h1', 0)->plaintext;
+		} 
+		else
+		{
+			$this->returnError('You must specify username', 400);
+		}
+
+		for($i = 0; $i < 10; $i++) {
+			$track = $html->find('div.post', $i);
+            $item = new \Item();
+            $item->name = $track->find('h2', 0)->plaintext;
+            $item->title = $track->find('h2', 0)->plaintext;
+            $item->content = $track->find('a.thumb',0) . '<br/>' . $track->find('h2', 0)->plaintext;
+            $item->id = 'http://www.whyd.com' . $track->find('a.no-ajaxy',0)->getAttribute('href');
+            $item->uri = 'http://www.whyd.com' . $track->find('a.no-ajaxy',0)->getAttribute('href');
+            $this->items[] = $item;
+        }
+    }
+	public function getName(){
+		return (!empty($this->name) ? $this->name .' - ' : '') .'Whyd Bridge';
+	}
+
+	public function getURI(){
+		return 'http://www.whyd.com/';
+	}
+
+	public function getCacheDuration(){
+		return 600; // 10 minutes
+	}
+}
+
+

+ 44 - 0
bridges/WikipediaENBridge.php

@@ -0,0 +1,44 @@
+<?php
+/**
+* RssBridgeWikipediaEN
+* Retrieve latest highlighted articles from Wikipedia in English.
+* 2014-05-25
+*
+* @name Wikipedia EN "Today's Featured Article..."
+* @homepage https://en.wikipedia.org/
+* @description Returns the highlighted en.wikipedia.org article.
+* @maintainer gsurrel
+*/
+class WikipediaENBridge extends BridgeAbstract{
+
+    public function collectData(array $param){
+        $html = '';
+        $host = 'http://en.wikipedia.org';
+        // If you want HTTPS access instead, uncomment the following line:
+        //$host = 'https://en.wikipedia.org';
+        $link = '/wiki/Main_Page';
+
+        $html = file_get_html($host.$link) or $this->returnError('Could not request Wikipedia EN.', 404);
+
+		$element = $html->find('div[id=mp-tfa]', 0);
+		// Clean the bottom of the featured article
+		$element->find('div', -1)->outertext = '';
+		$item = new \Item();
+		$item->uri = $host.$element->find('p', 0)->find('a', 0)->href;
+		$item->title = $element->find('p',0)->find('a',0)->title;
+		$item->content = str_replace('href="/', 'href="'.$host.'/', $element->innertext);
+		$this->items[] = $item;
+    }
+
+    public function getName(){
+        return 'Wikipedia EN "Today\'s Featued Article"';
+    }
+
+    public function getURI(){
+        return 'https://en.wikipedia.org/wiki/Main_Page';
+    }
+
+    public function getCacheDuration(){
+        return 3600*4; // 4 hours
+    }
+}

+ 44 - 0
bridges/WikipediaEOBridge.php

@@ -0,0 +1,44 @@
+<?php
+/**
+* RssBridgeWikipediaEO
+* Retrieve latest highlighted articles from Wikipedia in Esperanto.
+* 2014-05-25
+*
+* @name Wikipedia EO "Artikolo de la semajno"
+* @homepage https://eo.wikipedia.org/
+* @description Returns the highlighted eo.wikipedia.org article.
+* @maintainer gsurrel
+*/
+class WikipediaEOBridge extends BridgeAbstract{
+
+    public function collectData(array $param){
+        $html = '';
+        $host = 'http://eo.wikipedia.org';
+        // If you want HTTPS access instead, uncomment the following line:
+        //$host = 'https://eo.wikipedia.org';
+        $link = '/wiki/Vikipedio:%C4%88efpa%C4%9Do';
+
+        $html = file_get_html($host.$link) or $this->returnError('Could not request Wikipedia EO.', 404);
+
+		$element = $html->find('div[id=mf-tfa]', 0);
+		// Link to article
+		$link = $element->find('p', -2)->find('a', 0);
+		$item = new \Item();
+		$item->uri = $host.$link->href;
+		$item->title = $link->title;
+		$item->content = str_replace('href="/', 'href="'.$host.'/', $element->innertext);
+		$this->items[] = $item;
+    }
+
+    public function getName(){
+        return 'Wikipedia EO "Artikolo de la semajno"';
+    }
+
+    public function getURI(){
+        return 'https://eo.wikipedia.org/wiki/Vikipedio:%C4%88efpa%C4%9Do';
+    }
+
+    public function getCacheDuration(){
+        return 3600*12; // 12 hours
+    }
+}

+ 42 - 0
bridges/WikipediaFRBridge.php

@@ -0,0 +1,42 @@
+<?php
+/**
+* RssBridgeWikipediaFR
+* Retrieve latest highlighted articles from Wikipedia in French.
+* 2014-05-25
+*
+* @name Wikipedia FR "Lumière sur..."
+* @homepage https://fr.wikipedia.org/
+* @description Returns the highlighted fr.wikipedia.org article.
+* @maintainer gsurrel
+*/
+class WikipediaFRBridge extends BridgeAbstract{
+
+    public function collectData(array $param){
+        $html = '';
+        $host = 'http://fr.wikipedia.org';
+        // If you want HTTPS access instead, uncomment the following line:
+        //$host = 'https://fr.wikipedia.org';
+        $link = '/wiki/Wikip%C3%A9dia:Accueil_principal';
+
+        $html = file_get_html($host.$link) or $this->returnError('Could not request Wikipedia FR.', 404);
+
+		$element = $html->find('div[id=accueil-lumieresur]', 0);
+		$item = new \Item();
+		$item->uri = $host.$element->find('p', 0)->find('a', 0)->href;
+		$item->title = $element->find('p',0)->find('a',0)->title;
+		$item->content = str_replace('href="/', 'href="'.$host.'/', $element->find('div[id=mf-lumieresur]', 0)->innertext);
+		$this->items[] = $item;
+    }
+
+    public function getName(){
+        return 'Wikipedia FR "Lumière sur..."';
+    }
+
+    public function getURI(){
+        return 'https://fr.wikipedia.org/wiki/Wikip%C3%A9dia:Accueil_principal';
+    }
+
+    public function getCacheDuration(){
+        return 3600*4; // 4 hours
+    }
+}

+ 95 - 0
bridges/WordPressBridge.php

@@ -0,0 +1,95 @@
+<?php
+
+/**
+ * RssBridgeWordpress
+ * Returns the 3 newest full posts of a Wordpress blog
+ *
+ * @name Wordpress Bridge
+ * @homepage https://wordpress.com/
+ * @description Returns the 3 newest full posts of a Wordpress blog
+ * @maintainer aledeg
+ * @update 2014-05-26
+ * @use1(url="blog URL (required)", name="blog name")
+ */
+class WordPressBridge extends BridgeAbstract {
+
+	private $url;
+	private $name;
+
+	public function collectData(array $param) {
+		$this->processParams($param);
+
+		if (!$this->hasUrl()) {
+			$this->returnError('You must specify a URL', 400);
+		}
+
+		$html = file_get_html($this->url) or $this->returnError("Could not request {$this->url}.", 404);
+
+                $posts = $html->find('.post');
+		if(!empty($posts) ) {
+			$i=0;
+			foreach ($html->find('.post') as $article) {
+				if($i < 3) {
+					$uri = $article->find('a', 0)->href;
+					$this->items[] = $this->getDetails($uri);
+					$i++;
+				}
+			}
+		}
+		else {
+			$this->returnError("Sorry, {$this->url} doesn't seem to be a Wordpress blog.", 404);
+		}
+	}
+
+	private function getDetails($uri) {
+		$html = file_get_html($uri) or exit;
+
+		$item = new \Item();
+
+		$article = $html->find('.post', 0);
+		$item->uri = $uri;
+		$item->title = $article->find('h1', 0)->innertext;
+		$item->content = $this->clearContent($article->find('.entry-content,.entry', 0)->innertext);
+		$item->timestamp = $this->getDate($uri);
+
+		return $item;
+	}
+
+	private function clearContent($content) {
+		$content = preg_replace('/<script.*\/script>/', '', $content);
+		$content = preg_replace('/<div class="wpa".*/', '', $content);
+		return $content;
+	}
+
+	private function getDate($uri) {
+		preg_match('/\d{4}\/\d{2}\/\d{2}/', $uri, $matches);
+		$date = new \DateTime($matches[0]);
+		return $date->format('U');
+	}
+
+	public function getName() {
+		return "{$this->name} - Wordpress Bridge";
+	}
+
+	public function getURI() {
+		return $this->url;
+	}
+
+	public function getCacheDuration() {
+		return 3600*3; // 3 hours
+	}
+
+	private function hasUrl() {
+		if (empty($this->url)) {
+			return false;
+		}
+		return true;
+	}
+
+	private function processParams($param) {
+		$this->url = $param['url'];
+		$this->name = $param['name'];
+	}
+
+}
+

+ 52 - 0
bridges/XbooruBridge.php

@@ -0,0 +1,52 @@
+<?php
+/**
+* RssBridgeXbooru
+* Returns images from given page
+* 2014-05-25
+*
+* @name Xbooru
+* @homepage http://xbooru.com/
+* @description Returns images from given page
+* @maintainer mitsukarenai
+* @use1(p="page",t="tags")
+*/
+class XbooruBridge extends BridgeAbstract{
+
+    public function collectData(array $param){
+	$page = 0;$tags='';
+        if (isset($param['p'])) { 
+		$page = (int)preg_replace("/[^0-9]/",'', $param['p']); 
+		$page = $page - 1;
+		$page = $page * 50;
+        }
+        if (isset($param['t'])) { 
+            $tags = urlencode($param['t']); 
+        }
+        $html = file_get_html("http://xbooru.com/index.php?page=post&s=list&tags=$tags&pid=$page") or $this->returnError('Could not request Xbooru.', 404);
+
+
+	foreach($html->find('div[class=content] span') as $element) {
+		$item = new \Item();
+		$item->uri = 'http://xbooru.com/'.$element->find('a', 0)->href;
+		$item->postid = (int)preg_replace("/[^0-9]/",'', $element->getAttribute('id'));	
+		$item->timestamp = time();
+		$item->thumbnailUri = $element->find('img', 0)->src;
+		$item->tags = $element->find('img', 0)->getAttribute('alt');
+		$item->title = 'Xbooru | '.$item->postid;
+		$item->content = '<a href="' . $item->uri . '"><img src="' . $item->thumbnailUri . '" /></a><br>Tags: '.$item->tags;
+		$this->items[] = $item; 
+	}
+    }
+
+    public function getName(){
+        return 'Xbooru';
+    }
+
+    public function getURI(){
+        return 'http://xbooru.com/';
+    }
+
+    public function getCacheDuration(){
+        return 1800; // 30 minutes
+    }
+}

+ 54 - 0
bridges/YandereBridge.php

@@ -0,0 +1,54 @@
+<?php
+/**
+* RssBridgeYandere
+* Returns images from given page and tags
+* 2014-05-25
+*
+* @name Yande.re
+* @homepage https://yande.re/
+* @description Returns images from given page and tags
+* @maintainer mitsukarenai
+* @use1(p="page", t="tags")
+*/
+class YandereBridge extends BridgeAbstract{
+
+    public function collectData(array $param){
+	$page = 1; $tags = '';
+        if (isset($param['p'])) { 
+            $page = (int)preg_replace("/[^0-9]/",'', $param['p']); 
+        }
+        if (isset($param['t'])) { 
+            $tags = urlencode($param['t']); 
+        }
+        $html = file_get_html("https://yande.re/post?page=$page&tags=$tags") or $this->returnError('Could not request Yandere.', 404);
+	$input_json = explode('Post.register(', $html);
+	foreach($input_json as $element)
+	 $data[] = preg_replace('/}\)(.*)/', '}', $element);
+	unset($data[0]);
+    
+        foreach($data as $datai) {
+	    $json = json_decode($datai, TRUE);
+            $item = new \Item();
+            $item->uri = 'http://yande.re/post/show/'.$json['id'];
+            $item->postid = $json['id'];
+            $item->timestamp = $json['created_at'];
+            $item->imageUri = $json['file_url'];
+            $item->thumbnailUri = $json['preview_url'];
+            $item->title = 'Yandere | '.$json['id'];
+            $item->content = '<a href="' . $item->imageUri . '"><img src="' . $item->thumbnailUri . '" /></a><br>Tags: '.$json['tags']; 
+            $this->items[] = $item;
+        }
+    }
+
+    public function getName(){
+        return 'Yande.re';
+    }
+
+    public function getURI(){
+        return 'http://yande.re/post';
+    }
+
+    public function getCacheDuration(){
+        return 1800; // 30 minutes
+    }
+}

+ 121 - 34
bridges/YoutubeBridge.php

@@ -4,43 +4,130 @@
 * Returns the newest videos
 *
 * @name Youtube Bridge
-* @description Returns the newest videos
+* @homepage https://www.youtube.com/
+* @description Returns the 10 newest videos by username/channel/playlist or search
+* @maintainer mitsukarenai
+* @update 2014-06-20
 * @use1(u="username")
+* @use2(c="channel id")
+* @use3(p="playlist id")
+* @use4(s="search keyword",pa="page")
+* 
+* WARNING: to parse big playlists (over ~90 videos), you need to edit simple_html_dom.php: 
+* change: define('MAX_FILE_SIZE', 600000);
+* into:   define('MAX_FILE_SIZE', 900000);  (or more)
 */
 class YoutubeBridge extends BridgeAbstract{
     
-    private $request;
+	private $request;
     
-    public function collectData(array $param){
-        $html = '';
-        if (isset($param['u'])) {   /* user timeline mode */
-            $this->request = $param['u'];
-            $html = file_get_html('https://www.youtube.com/user/'.urlencode($this->request).'/videos') or $this->returnError('Could not request Youtube.', 404);
-        }
-        else {
-            $this->returnError('You must specify a Youtbe username (?u=...).', 400);
-        }
-        
-    
-        foreach($html->find('li.channels-content-item') as $element) {
-            $item = new \Item();
-            $item->uri = 'https://www.youtube.com'.$element->find('a',0)->href;
-            $item->thumbnailUri = 'https:'.$element->find('img',0)->src;
-            $item->title = trim($element->find('h3',0)->plaintext);
-            $item->content = '<a href="' . $item->uri . '"><img src="' . $item->thumbnailUri . '" /></a><br><a href="' . $item->uri . '">' . $item->title . '</a>';
-            $this->items[] = $item;
-        }
-    }
-
-    public function getName(){
-        return (!empty($this->request) ? $this->request .' - ' : '') .'Youtube Bridge';
-    }
-
-    public function getURI(){
-        return 'https://www.youtube.com/';
-    }
-
-    public function getCacheDuration(){
-        return 21600; // 6 hours
-    }
+	public function collectData(array $param){
+
+		function getPublishDate($id) {
+			// relies on Youtube API; deprecated
+			$json = json_decode(file_get_contents("https://gdata.youtube.com/feeds/api/videos/$id?v=2&alt=json"), TRUE);
+			$timestamp = strtotime($json['entry']['published']['$t']);
+			return $timestamp;
+		} 
+
+
+        	$html = '';
+		$limit = 10;
+		$count = 0;
+
+		if (isset($param['u'])) {   /* user timeline mode */
+			$this->request = $param['u'];
+			$html = file_get_html('https://www.youtube.com/user/'.urlencode($this->request).'/videos') or $this->returnError('Could not request Youtube.', 404);
+
+			foreach($html->find('li.channels-content-item') as $element) {
+				if($count < $limit) {
+					$item = new \Item();
+						$videoquery = parse_url($element->find('a',0)->href, PHP_URL_QUERY); parse_str($videoquery, $videoquery);
+					$item->id = $videoquery['v'];
+					$item->uri = 'https://www.youtube.com/watch?v='.$item->id;
+					$item->thumbnailUri = 'https:'.$element->find('img',0)->src;
+					$item->title = trim($element->find('h3',0)->plaintext);
+					$item->timestamp = getPublishDate($item->id);
+					$item->content = '<a href="' . $item->uri . '"><img src="' . $item->thumbnailUri . '" /></a><br><a href="' . $item->uri . '">' . $item->title . '</a>';
+					$this->items[] = $item;
+					$count++;
+				}
+			}
+		}
+
+		else if (isset($param['c'])) {   /* channel timeline mode */
+			$this->request = $param['c'];
+			$html = file_get_html('https://www.youtube.com/channel/'.urlencode($this->request).'/videos') or $this->returnError('Could not request Youtube.', 404);
+
+			foreach($html->find('li.channels-content-item') as $element) {
+				if($count < $limit) {
+					$item = new \Item();
+						$videoquery = parse_url($element->find('a',0)->href, PHP_URL_QUERY); parse_str($videoquery, $videoquery);
+					$item->id = $videoquery['v'];
+					$item->uri = 'https://www.youtube.com/watch?v='.$item->id;
+					$item->thumbnailUri = 'https:'.$element->find('img',0)->src;
+					$item->title = trim($element->find('h3',0)->plaintext);
+					$item->timestamp = getPublishDate($item->id);
+					$item->content = '<a href="' . $item->uri . '"><img src="' . $item->thumbnailUri . '" /></a><br><a href="' . $item->uri . '">' . $item->title . '</a>';
+					$this->items[] = $item;
+					$count++;
+				}
+			}
+		}
+
+		else if (isset($param['p'])) {   /* playlist mode */
+			$this->request = $param['p'];
+			$html = file_get_html('https://www.youtube.com/playlist?list='.urlencode($this->request).'') or $this->returnError('Could not request Youtube.', 404);
+
+			foreach($html->find('tr.pl-video') as $element) {
+				if($count < $limit) {
+					$item = new \Item();
+					$item->uri = 'https://www.youtube.com'.$element->find('.pl-video-title a',0)->href;
+					$item->thumbnailUri = 'https:'.str_replace('/default.','/mqdefault.',$element->find('.pl-video-thumbnail img',0)->src);
+					$item->title = trim($element->find('.pl-video-title a',0)->plaintext);
+					$item->id = str_replace('/watch?v=', '', $element->find('a',0)->href);
+					$item->timestamp = getPublishDate($item->id);
+					$item->content = '<a href="' . $item->uri . '"><img src="' . $item->thumbnailUri . '" /></a><br><a href="' . $item->uri . '">' . $item->title . '</a>';
+					$this->items[] = $item;
+					$count++;
+				}
+				$this->request = 'Playlist '.trim(str_replace(' - YouTube', '', $html->find('title', 0)->plaintext)).', by '.$html->find('h1', 0)->plaintext;
+			}
+		}
+
+			else if (isset($param['s'])) {   /* search mode */
+				$this->request = $param['s']; $page = 1; if (isset($param['pa'])) $page = (int)preg_replace("/[^0-9]/",'', $param['pa']); 
+				$html = file_get_html('https://www.youtube.com/results?search_query='.urlencode($this->request).'&&page='.$page.'&filters=video&search_sort=video_date_uploaded') or $this->returnError('Could not request Youtube.', 404);
+
+				foreach($html->find('li.yt-lockup') as $element) {
+					$item = new \Item();
+					$item->uri = 'https://www.youtube.com'.$element->find('a',0)->href;
+					$checkthumb = $element->find('img', 0)->getAttribute('data-thumb');
+					if($checkthumb !== FALSE)
+						$item->thumbnailUri = $checkthumb;
+					else
+						$item->thumbnailUri = ''.$element->find('img',0)->src;
+					$item->title = trim($element->find('h3',0)->plaintext);
+					$item->id = str_replace('/watch?v=', '', $element->find('a',0)->href);
+					//$item->timestamp = getPublishDate($item->id);  /* better not use it here */
+					$item->content = '<a href="' . $item->uri . '"><img src="' . $item->thumbnailUri . '" /></a><br><a href="' . $item->uri . '">' . $item->title . '</a>';
+					$this->items[] = $item;
+				}
+				$this->request = 'Search: '.str_replace(' - YouTube', '', $html->find('title', 0)->plaintext);
+			}
+			else
+				$this->returnError('You must either specify a Youtube username (?u=...) or a channel id (?c=...) or a playlist id (?p=...) or search (?s=...)', 400);
+		}
+
+	public function getName(){
+		return (!empty($this->request) ? $this->request .' - ' : '') .'Youtube Bridge';
+	}
+
+	public function getURI(){
+		return 'https://www.youtube.com/';
+	}
+
+	public function getCacheDuration(){
+		return 10800; // 3 hours
+	}
 }

+ 16 - 2
css/style.css

@@ -87,6 +87,10 @@ h2 {
     margin-bottom: 10px;
 }
 
+h2 a {
+    color:black;
+}
+
 form {
     margin-bottom: 10px;
 }
@@ -97,9 +101,13 @@ header,footer {
     padding: 20px;
 }
 
+header h2 {
+	margin-left:1em;
+}
+
 footer {
     text-align: center;
-    font-size: 60%;
+    font-size: 80%;
 }
 
 section {
@@ -120,10 +128,16 @@ section {
     color: #555;
 }
 
+.maintainer {
+    font-size:60%;
+    float:right;
+    color: #777;
+}
+
 .placeholder {
     color: #aaa;
 }
 
 input, label {
     font-size: 80%;
-}
+}

+ 13 - 6
formats/AtomFormat.php

@@ -18,13 +18,14 @@ class AtomFormat extends FormatAbstract{
         $extraInfos = $this->getExtraInfos();
         $title = htmlspecialchars($extraInfos['name']);
         $uri = htmlspecialchars($extraInfos['uri']);
+        $icon = 'http://g.etfv.co/'. $uri .'?icon.jpg';
 
         $entries = '';
         foreach($this->getDatas() as $data){
-            $entryName = is_null($data->name) ? $title : $data->name;
-            $entryAuthor = is_null($data->author) ? $uri : $data->author;
-            $entryTitle = is_null($data->title) ? '' : $data->title;
-            $entryUri = is_null($data->uri) ? '' : $data->uri;
+            $entryName = strip_tags(is_null($data->name) ? $title : $data->name);
+            $entryAuthor = strip_tags(is_null($data->author) ? $uri : $data->author);
+            $entryTitle = strip_tags(is_null($data->title) ? '' : $data->title);
+            $entryUri = htmlspecialchars(is_null($data->uri) ? '' : $data->uri);
             $entryTimestamp = is_null($data->timestamp) ? '' : date(DATE_ATOM, $data->timestamp);
             // We prevent content from closing the CDATA too early.
             $entryContent = is_null($data->content) ? '' : '<![CDATA[' . $this->sanitizeHtml(str_replace(']]>','',$data->content)) . ']]>';
@@ -53,6 +54,10 @@ EOD;
         - <content type="html"> : RFC look with xhtml, keep this in spite of ?
         */
 
+// ####  TEMPORARY FIX ###
+$feedTimestamp = date(DATE_ATOM, time());
+//  ################ 
+
         /* Data are prepared, now let's begin the "MAGIE !!!" */
         $toReturn  = '<?xml version="1.0" encoding="UTF-8"?>';
         $toReturn .= <<<EOD
@@ -60,7 +65,9 @@ EOD;
 
     <title type="text">{$title}</title>
     <id>http{$https}://{$httpHost}{$httpInfo}/</id>
-    <updated></updated>
+    <icon>{$icon}</icon>
+    <logo>{$icon}</logo>
+    <updated>{$feedTimestamp}</updated>
     <link rel="alternate" type="text/html" href="{$uri}" />
     <link rel="self" href="http{$https}://{$httpHost}{$serverRequestUri}" />
 {$entries}
@@ -85,4 +92,4 @@ EOD;
 
         return parent::display();
     }
-}
+}

+ 37 - 15
formats/HtmlFormat.php

@@ -12,39 +12,61 @@ class HtmlFormat extends FormatAbstract{
         $extraInfos = $this->getExtraInfos();
         $title = htmlspecialchars($extraInfos['name']);
         $uri = htmlspecialchars($extraInfos['uri']);
+	$atomquery = str_replace('format=HtmlFormat', 'format=AtomFormat', htmlentities($_SERVER['QUERY_STRING']));
 
         $entries = '';
         foreach($this->getDatas() as $data){
             $entryUri = is_null($data->uri) ? $uri : $data->uri;
             $entryTitle = is_null($data->title) ? '' : $this->sanitizeHtml(strip_tags($data->title));
-            $entryTimestamp = is_null($data->timestamp) ? '' : '<small>' . date(DATE_ATOM, $data->timestamp) . '</small>';
+            $entryTimestamp = is_null($data->timestamp) ? '' : '<small><time datetime="' . date(DATE_ATOM, $data->timestamp) . '">' . date(DATE_ATOM, $data->timestamp) . '</time></small>';
             $entryContent = is_null($data->content) ? '' : '<p>' . $this->sanitizeHtml($data->content). '</p>';
             $entries .= <<<EOD
 
-        <div class="rssitem">
-            <h2><a href="{$entryUri}">{$entryTitle}</a></h2>
-            {$entryTimestamp}
-            {$entryContent}
-        </div>
+<div class="feeditem">
+	<h2><a href="{$entryUri}">{$entryTitle}</a></h2>
+	{$entryTimestamp}
+		{$entryContent}
+</div>
 
 EOD;
         }
 
         $styleCss = <<<'EOD'
-body{font-family:"Trebuchet MS",Verdana,Arial,Helvetica,sans-serif;font-size:10pt;background-color:#aaa;}div.rssitem{border:1px solid black;padding:5px;margin:10px;background-color:#fff;}
+body{
+	font-family:"Trebuchet MS",Verdana,Arial,Helvetica,sans-serif;
+	font-size:10pt;
+	background-color:#aaa;
+	background-image:linear-gradient(#eee, #aaa);
+	background-attachment:fixed;
+}
+div.feeditem{border:1px solid black;padding:1em;margin:1em;background-color:#fff;}
+div.feeditem:hover { background-color:#ebf7ff; }
+h1 {border-bottom:dotted #bbb;margin:0 1em 1em 1em;}
+h2 {margin:0;}
+h2 a {color:black;text-decoration:none;}
+h2 a:hover {text-decoration:underline;}
+span.menu {margin-left:1em;}
+span.menu img {vertical-align:middle;}
+span.menu a { color:black; text-decoration:none;  padding:0.4em; }
+span.menu a:hover { background-color:white; }
+
 EOD;
 
         /* Data are prepared, now let's begin the "MAGIE !!!" */
         $toReturn = <<<EOD
+<!DOCTYPE html>
 <html>
-    <head>
-        <title>{$title}</title>
-        <style type="text/css">{$styleCss}</style>
-    </head>
-    <body>
-        <h1>{$title}</h1>
+<head>
+	<meta charset="UTF-8">
+	<title>{$title}</title>
+	<style type="text/css">{$styleCss}</style>
+	<meta name="robots" content="noindex, follow">
+</head>
+<body>
+	<h1>{$title}</h1>
+<span class="menu"><a href="./" onclick="window.history.back()">← back to rss-bridge</a> <a title="Get the ATOM feed" href="./?{$atomquery}"><img alt="feed" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABkAAAAZAgMAAAC5h23wAAAABGdBTUEAALGPC/xhBQAAAAFzUkdCAK7OHOkAAAAJUExURf+cCv/8+P/OhD2VurcAAAABdFJOU/4a4wd9AAAAbklEQVQI13XOMQ6AIAwFUGzCgLtHYPEUHMHBTwgTRyFOXoDdUT2ltCKbXV6b/iZV6qcMYmY1EJtw3MwFQRJU/Bfl6VaEzENQxbHIUxKTrY4Xgk4ct0EvcrYa9iTPGrxqbE3XHWSfRfIk1trp6O8+R38aBYbaAE4AAAAASUVORK5CYII="></a></span>
 {$entries}
-    </body>
+</body>
 </html>
 EOD;
 
@@ -58,4 +80,4 @@ EOD;
 
         return parent::display();
     }
-}
+}

+ 152 - 43
index.php

@@ -15,6 +15,52 @@ date_default_timezone_set('UTC');
 error_reporting(0);
 //ini_set('display_errors','1'); error_reporting(E_ALL);  // For debugging only.
 
+// extensions check
+if (!extension_loaded('openssl'))
+	die('"openssl" extension not loaded. Please check "php.ini"');
+
+// FIXME : beta test UA spoofing, please report any blacklisting by PHP-fopen-unfriendly websites
+ini_set('user_agent', 'Mozilla/5.0 (X11; Linux x86_64; rv:30.0) Gecko/20121202 Firefox/30.0 (rss-bridge/0.1; +https://github.com/sebsauvage/rss-bridge)');
+// -------
+
+// default whitelist
+$whitelist_file = './whitelist.txt';
+$whitelist_default = array(
+	"BandcampBridge",
+	"CryptomeBridge",
+	"DansTonChatBridge",
+	"DuckDuckGoBridge",
+	"FlickrExploreBridge",
+	"GooglePlusPostBridge",
+	"GoogleSearchBridge",
+	"IdenticaBridge",
+	"InstagramBridge",
+	"OpenClassroomsBridge",
+	"PinterestBridge",
+	"ScmbBridge",
+	"TwitterBridge",
+	"WikipediaENBridge",
+	"WikipediaEOBridge",
+	"WikipediaFRBridge",
+	"YoutubeBridge");
+
+if (!file_exists($whitelist_file)) {
+	$whitelist_selection = $whitelist_default;
+	$whitelist_write = implode("\n", $whitelist_default);
+	file_put_contents($whitelist_file, $whitelist_write);
+}
+else {
+	$whitelist_selection = explode("\n", file_get_contents($whitelist_file));
+}
+
+// whitelist control function
+function BridgeWhitelist( $whitelist, $name ) {
+	if(in_array("$name", $whitelist) or in_array("$name.php", $whitelist))
+		return TRUE;
+	else
+		return FALSE;
+}
+
 try{
     require_once __DIR__ . '/lib/RssBridge.php';
 
@@ -32,8 +78,11 @@ try{
                     $format = $_REQUEST['format'];
                     unset($_REQUEST['format']);
 
-                    // FIXME : necessary ?
-                    // ini_set('user_agent', 'Mozilla/5.0 (X11; Linux x86_64; rv:20.0) Gecko/20100101 Firefox/20.0');
+			// whitelist control
+			if(!BridgeWhitelist($whitelist_selection, $bridge)) {
+				throw new \HttpException('This bridge is not whitelisted', 401);
+				die; 
+			}
 
                     $cache = Cache::create('FileCache');
 
@@ -71,6 +120,84 @@ function getHelperButtonFormat($value, $name){
     return '<button type="submit" name="format" value="' . $value . '">' . $name . '</button>';
 }
 
+function getHelperButtonsFormat($formats){
+	$buttons = '';
+		foreach( $formats as $name => $infos )
+		{
+			if ( isset($infos['name']) )
+			{
+				$buttons .= getHelperButtonFormat($name, $infos['name']) . PHP_EOL;
+			}
+		}
+	return $buttons;
+}
+
+function displayBridgeCard($bridgeReference, $bridgeInformations, $formats, $isActive = true)
+{
+	$name = isset($bridgeInformations['homepage']) ? '<a href="'.$bridgeInformations['homepage'].'">'.$bridgeInformations['name'].'</a>' : $bridgeInformations['name'];
+	$description = isset($bridgeInformations['description']) ? $bridgeInformations['description'] : 'No description provided';
+	$card = <<<CARD
+	<section id="bridge-{$bridgeReference}" data-ref="{$bridgeReference}">
+		<h2>{$name}</h2>
+		<p class="description">
+			{$description}
+		</p>
+CARD;
+		if( isset($bridgeInformations['use']) && count($bridgeInformations['use']) > 0 )
+		{
+			$card .= '<ol class="list-use">' . PHP_EOL;
+			foreach($bridgeInformations['use'] as $anUseNum => $anUse)
+			{
+				$card .= '<li data-use="' . $anUseNum . '">' . PHP_EOL;
+				$card .= '<form method="GET" action="?">
+							<input type="hidden" name="action" value="display" />
+							<input type="hidden" name="bridge" value="' . $bridgeReference . '" />' . PHP_EOL;
+
+				foreach($anUse as $argName => $argDescription)
+				{
+					$idArg = 'arg-' . $bridgeReference . '-' . $anUseNum . '-' . $argName;
+					$card .= '<input id="' . $idArg . '" type="text" value="" placeholder="' . $argDescription . '" name="' . $argName . '" />' . PHP_EOL;
+				}
+
+				$card .= '<br />';
+
+				if ($isActive)
+				{
+					$card .= getHelperButtonsFormat($formats);
+				}
+				else
+				{
+					$card .= '<span style="font-weight: bold;">Inactive</span>';
+				}
+
+				$card .= '</form></li>' . PHP_EOL;
+			}
+			$card .= '</ol>' . PHP_EOL;
+		}
+		else
+		{
+			$card .= '<form method="GET" action="?">
+				<input type="hidden" name="action" value="display" />
+				<input type="hidden" name="bridge" value="' . $bridgeReference . '" />' . PHP_EOL;
+
+			if ($isActive)
+			{
+				$card .= getHelperButtonsFormat($formats);
+			}
+			else
+			{
+				$card .= '<span style="font-weight: bold;">Inactive</span>';
+			}
+			$card .= '</form>' . PHP_EOL;
+		}
+
+
+	$card .= isset($bridgeInformations['maintainer']) ? '<span class="maintainer">'.$bridgeInformations['maintainer'].'</span>' : '';
+	$card .= '</section>';
+
+	return $card;
+}
+
 $bridges = Bridge::searchInformation();
 $formats = Format::searchInformation();
 ?>
@@ -91,48 +218,30 @@ $formats = Format::searchInformation();
 
     <header>
         <h1>RSS-Bridge</h1>
+        <h2>·Reconnecting the Web·</h2>
     </header>
-
-        <?php foreach($bridges as $bridgeReference => $bridgeInformations): ?>
-            <section id="bridge-<?php echo $bridgeReference ?>" data-ref="<?php echo $bridgeReference ?>">
-                <h2><?php echo $bridgeInformations['name'] ?></h2>
-                <p class="description">
-                    <?php echo isset($bridgeInformations['description']) ? $bridgeInformations['description'] : 'No description provided' ?>
-                </p> 
-
-                <?php if( isset($bridgeInformations['use']) && count($bridgeInformations['use']) > 0 ): ?>
-                <ol class="list-use">
-                    <?php foreach($bridgeInformations['use'] as $anUseNum => $anUse): ?>
-                    <li data-use="<?php echo $anUseNum ?>">
-                        <form method="GET" action="?">
-                            <input type="hidden" name="action" value="display" />
-                            <input type="hidden" name="bridge" value="<?php echo $bridgeReference ?>" />
-                            <?php foreach($anUse as $argName => $argDescription): ?>
-                            <?php
-                                $idArg = 'arg-' . $bridgeReference . '-' . $anUseNum . '-' . $argName;
-                            ?>
-                            <input id="<?php echo $idArg ?>" type="text" value="" placeholder="<?php echo $argDescription; ?>" name="<?php echo $argName ?>" placeholder="<?php echo $argDescription ?>" />
-                            <?php endforeach; ?>
-                            <?php foreach( $formats as $name => $infos ): ?>
-                                <?php if( isset($infos['name']) ){ echo getHelperButtonFormat($name, $infos['name']); } ?>
-                            <?php endforeach; ?>
-                        </form>
-                    </li>
-                    <?php endforeach; ?>
-                </ol>
-                <?php else: ?>
-                <form method="GET" action="?">
-                    <input type="hidden" name="action" value="display" />
-                    <input type="hidden" name="bridge" value="<?php echo $bridgeReference ?>" />
-                    <?php foreach( $formats as $name => $infos ): ?>
-                        <?php if( isset($infos['name']) ){ echo getHelperButtonFormat($name, $infos['name']); } ?>
-                    <?php endforeach; ?>
-                </form>
-                <?php endif; ?>
-            </section>
-            <?php endforeach; ?>
+	<?php
+	    $activeFoundBridgeCount = 0;
+		$showInactive = isset($_REQUEST['show_inactive']) && $_REQUEST['show_inactive'] == 1;
+		$inactiveBridges = '';
+	    foreach($bridges as $bridgeReference => $bridgeInformations)
+	    {
+			if(BridgeWhitelist($whitelist_selection, $bridgeReference))
+			{
+				echo displayBridgeCard($bridgeReference, $bridgeInformations, $formats);
+	            $activeFoundBridgeCount++;
+			}
+			elseif ($showInactive)
+			{
+				// inactive bridges
+				$inactiveBridges .= displayBridgeCard($bridgeReference, $bridgeInformations, $formats, false) . PHP_EOL;
+			}
+		}
+		echo '<hr />' . $inactiveBridges;
+	?>
     <footer>
-        <a href="https://github.com/sebsauvage/rss-bridge">RSS-Bridge</a> alpha 0.1
+		<?= $activeFoundBridgeCount; ?>/<?= count($bridges) ?> active bridges (<a href="?show_inactive=1">Show inactive</a>)<br />
+        <a href="https://github.com/sebsauvage/rss-bridge">RSS-Bridge alpha 0.1 ~ Public Domain</a>
     </footer>  
     </body>
-</html>
+</html>

+ 2 - 2
lib/Bridge.php

@@ -137,7 +137,7 @@ class Bridge{
 
         $listBridge = array();
 
-        $searchCommonPattern = array('description', 'name');
+        $searchCommonPattern = array('maintainer', 'description', 'homepage', 'name');
 
         $dirFiles = scandir($pathDirBridge);
         if( $dirFiles !== false ){
@@ -184,4 +184,4 @@ class Bridge{
 
         return $listBridge;
     }
-}
+}

+ 1742 - 0
vendor/simplehtmldom/simple_html_dom.php

@@ -0,0 +1,1742 @@
+<?php
+/**
+ * Website: http://sourceforge.net/projects/simplehtmldom/
+ * Additional projects that may be used: http://sourceforge.net/projects/debugobject/
+ * Acknowledge: Jose Solorzano (https://sourceforge.net/projects/php-html/)
+ * Contributions by:
+ *	 Yousuke Kumakura (Attribute filters)
+ *	 Vadim Voituk (Negative indexes supports of "find" method)
+ *	 Antcs (Constructor with automatically load contents either text or file/url)
+ *
+ * all affected sections have comments starting with "PaperG"
+ *
+ * Paperg - Added case insensitive testing of the value of the selector.
+ * Paperg - Added tag_start for the starting index of tags - NOTE: This works but not accurately.
+ *  This tag_start gets counted AFTER \r\n have been crushed out, and after the remove_noice calls so it will not reflect the REAL position of the tag in the source,
+ *  it will almost always be smaller by some amount.
+ *  We use this to determine how far into the file the tag in question is.  This "percentage will never be accurate as the $dom->size is the "real" number of bytes the dom was created from.
+ *  but for most purposes, it's a really good estimation.
+ * Paperg - Added the forceTagsClosed to the dom constructor.  Forcing tags closed is great for malformed html, but it CAN lead to parsing errors.
+ * Allow the user to tell us how much they trust the html.
+ * Paperg add the text and plaintext to the selectors for the find syntax.  plaintext implies text in the innertext of a node.  text implies that the tag is a text node.
+ * This allows for us to find tags based on the text they contain.
+ * Create find_ancestor_tag to see if a tag is - at any level - inside of another specific tag.
+ * Paperg: added parse_charset so that we know about the character set of the source document.
+ *  NOTE:  If the user's system has a routine called get_last_retrieve_url_contents_content_type availalbe, we will assume it's returning the content-type header from the
+ *  last transfer or curl_exec, and we will parse that and use it in preference to any other method of charset detection.
+ *
+ * Found infinite loop in the case of broken html in restore_noise.  Rewrote to protect from that.
+ * PaperG (John Schlick) Added get_display_size for "IMG" tags.
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @author S.C. Chen <me578022@gmail.com>
+ * @author John Schlick
+ * @author Rus Carroll
+ * @version 1.5 ($Rev: 208 $)
+ * @package PlaceLocalInclude
+ * @subpackage simple_html_dom
+ */
+
+/**
+ * All of the Defines for the classes below.
+ * @author S.C. Chen <me578022@gmail.com>
+ */
+define('HDOM_TYPE_ELEMENT', 1);
+define('HDOM_TYPE_COMMENT', 2);
+define('HDOM_TYPE_TEXT',	3);
+define('HDOM_TYPE_ENDTAG',  4);
+define('HDOM_TYPE_ROOT',	5);
+define('HDOM_TYPE_UNKNOWN', 6);
+define('HDOM_QUOTE_DOUBLE', 0);
+define('HDOM_QUOTE_SINGLE', 1);
+define('HDOM_QUOTE_NO',	 3);
+define('HDOM_INFO_BEGIN',   0);
+define('HDOM_INFO_END',	 1);
+define('HDOM_INFO_QUOTE',   2);
+define('HDOM_INFO_SPACE',   3);
+define('HDOM_INFO_TEXT',	4);
+define('HDOM_INFO_INNER',   5);
+define('HDOM_INFO_OUTER',   6);
+define('HDOM_INFO_ENDSPACE',7);
+define('DEFAULT_TARGET_CHARSET', 'UTF-8');
+define('DEFAULT_BR_TEXT', "\r\n");
+define('DEFAULT_SPAN_TEXT', " ");
+define('MAX_FILE_SIZE', 10000000);
+// helper functions
+// -----------------------------------------------------------------------------
+// get html dom from file
+// $maxlen is defined in the code as PHP_STREAM_COPY_ALL which is defined as -1.
+function file_get_html($url, $use_include_path = false, $context=null, $offset = -1, $maxLen=-1, $lowercase = true, $forceTagsClosed=true, $target_charset = DEFAULT_TARGET_CHARSET, $stripRN=true, $defaultBRText=DEFAULT_BR_TEXT, $defaultSpanText=DEFAULT_SPAN_TEXT)
+{
+	// We DO force the tags to be terminated.
+	$dom = new simple_html_dom(null, $lowercase, $forceTagsClosed, $target_charset, $stripRN, $defaultBRText, $defaultSpanText);
+	// For sourceforge users: uncomment the next line and comment the retreive_url_contents line 2 lines down if it is not already done.
+	$contents = file_get_contents($url, $use_include_path, $context, $offset);
+	// Paperg - use our own mechanism for getting the contents as we want to control the timeout.
+	//$contents = retrieve_url_contents($url);
+	if (empty($contents) || strlen($contents) > MAX_FILE_SIZE)
+	{
+		return false;
+	}
+	// The second parameter can force the selectors to all be lowercase.
+	$dom->load($contents, $lowercase, $stripRN);
+	return $dom;
+}
+
+// get html dom from string
+function str_get_html($str, $lowercase=true, $forceTagsClosed=true, $target_charset = DEFAULT_TARGET_CHARSET, $stripRN=true, $defaultBRText=DEFAULT_BR_TEXT, $defaultSpanText=DEFAULT_SPAN_TEXT)
+{
+	$dom = new simple_html_dom(null, $lowercase, $forceTagsClosed, $target_charset, $stripRN, $defaultBRText, $defaultSpanText);
+	if (empty($str) || strlen($str) > MAX_FILE_SIZE)
+	{
+		$dom->clear();
+		return false;
+	}
+	$dom->load($str, $lowercase, $stripRN);
+	return $dom;
+}
+
+// dump html dom tree
+function dump_html_tree($node, $show_attr=true, $deep=0)
+{
+	$node->dump($node);
+}
+
+
+/**
+ * simple html dom node
+ * PaperG - added ability for "find" routine to lowercase the value of the selector.
+ * PaperG - added $tag_start to track the start position of the tag in the total byte index
+ *
+ * @package PlaceLocalInclude
+ */
+class simple_html_dom_node
+{
+	public $nodetype = HDOM_TYPE_TEXT;
+	public $tag = 'text';
+	public $attr = array();
+	public $children = array();
+	public $nodes = array();
+	public $parent = null;
+	// The "info" array - see HDOM_INFO_... for what each element contains.
+	public $_ = array();
+	public $tag_start = 0;
+	private $dom = null;
+
+	function __construct($dom)
+	{
+		$this->dom = $dom;
+		$dom->nodes[] = $this;
+	}
+
+	function __destruct()
+	{
+		$this->clear();
+	}
+
+	function __toString()
+	{
+		return $this->outertext();
+	}
+
+	// clean up memory due to php5 circular references memory leak...
+	function clear()
+	{
+		$this->dom = null;
+		$this->nodes = null;
+		$this->parent = null;
+		$this->children = null;
+	}
+
+	// dump node's tree
+	function dump($show_attr=true, $deep=0)
+	{
+		$lead = str_repeat('	', $deep);
+
+		echo $lead.$this->tag;
+		if ($show_attr && count($this->attr)>0)
+		{
+			echo '(';
+			foreach ($this->attr as $k=>$v)
+				echo "[$k]=>\"".$this->$k.'", ';
+			echo ')';
+		}
+		echo "\n";
+
+		if ($this->nodes)
+		{
+			foreach ($this->nodes as $c)
+			{
+				$c->dump($show_attr, $deep+1);
+			}
+		}
+	}
+
+
+	// Debugging function to dump a single dom node with a bunch of information about it.
+	function dump_node($echo=true)
+	{
+
+		$string = $this->tag;
+		if (count($this->attr)>0)
+		{
+			$string .= '(';
+			foreach ($this->attr as $k=>$v)
+			{
+				$string .= "[$k]=>\"".$this->$k.'", ';
+			}
+			$string .= ')';
+		}
+		if (count($this->_)>0)
+		{
+			$string .= ' $_ (';
+			foreach ($this->_ as $k=>$v)
+			{
+				if (is_array($v))
+				{
+					$string .= "[$k]=>(";
+					foreach ($v as $k2=>$v2)
+					{
+						$string .= "[$k2]=>\"".$v2.'", ';
+					}
+					$string .= ")";
+				} else {
+					$string .= "[$k]=>\"".$v.'", ';
+				}
+			}
+			$string .= ")";
+		}
+
+		if (isset($this->text))
+		{
+			$string .= " text: (" . $this->text . ")";
+		}
+
+		$string .= " HDOM_INNER_INFO: '";
+		if (isset($node->_[HDOM_INFO_INNER]))
+		{
+			$string .= $node->_[HDOM_INFO_INNER] . "'";
+		}
+		else
+		{
+			$string .= ' NULL ';
+		}
+
+		$string .= " children: " . count($this->children);
+		$string .= " nodes: " . count($this->nodes);
+		$string .= " tag_start: " . $this->tag_start;
+		$string .= "\n";
+
+		if ($echo)
+		{
+			echo $string;
+			return;
+		}
+		else
+		{
+			return $string;
+		}
+	}
+
+	// returns the parent of node
+	// If a node is passed in, it will reset the parent of the current node to that one.
+	function parent($parent=null)
+	{
+		// I am SURE that this doesn't work properly.
+		// It fails to unset the current node from it's current parents nodes or children list first.
+		if ($parent !== null)
+		{
+			$this->parent = $parent;
+			$this->parent->nodes[] = $this;
+			$this->parent->children[] = $this;
+		}
+
+		return $this->parent;
+	}
+
+	// verify that node has children
+	function has_child()
+	{
+		return !empty($this->children);
+	}
+
+	// returns children of node
+	function children($idx=-1)
+	{
+		if ($idx===-1)
+		{
+			return $this->children;
+		}
+		if (isset($this->children[$idx]))
+		{
+			return $this->children[$idx];
+		}
+		return null;
+	}
+
+	// returns the first child of node
+	function first_child()
+	{
+		if (count($this->children)>0)
+		{
+			return $this->children[0];
+		}
+		return null;
+	}
+
+	// returns the last child of node
+	function last_child()
+	{
+		if (($count=count($this->children))>0)
+		{
+			return $this->children[$count-1];
+		}
+		return null;
+	}
+
+	// returns the next sibling of node
+	function next_sibling()
+	{
+		if ($this->parent===null)
+		{
+			return null;
+		}
+
+		$idx = 0;
+		$count = count($this->parent->children);
+		while ($idx<$count && $this!==$this->parent->children[$idx])
+		{
+			++$idx;
+		}
+		if (++$idx>=$count)
+		{
+			return null;
+		}
+		return $this->parent->children[$idx];
+	}
+
+	// returns the previous sibling of node
+	function prev_sibling()
+	{
+		if ($this->parent===null) return null;
+		$idx = 0;
+		$count = count($this->parent->children);
+		while ($idx<$count && $this!==$this->parent->children[$idx])
+			++$idx;
+		if (--$idx<0) return null;
+		return $this->parent->children[$idx];
+	}
+
+	// function to locate a specific ancestor tag in the path to the root.
+	function find_ancestor_tag($tag)
+	{
+		global $debug_object;
+		if (is_object($debug_object)) { $debug_object->debug_log_entry(1); }
+
+		// Start by including ourselves in the comparison.
+		$returnDom = $this;
+
+		while (!is_null($returnDom))
+		{
+			if (is_object($debug_object)) { $debug_object->debug_log(2, "Current tag is: " . $returnDom->tag); }
+
+			if ($returnDom->tag == $tag)
+			{
+				break;
+			}
+			$returnDom = $returnDom->parent;
+		}
+		return $returnDom;
+	}
+
+	// get dom node's inner html
+	function innertext()
+	{
+		if (isset($this->_[HDOM_INFO_INNER])) return $this->_[HDOM_INFO_INNER];
+		if (isset($this->_[HDOM_INFO_TEXT])) return $this->dom->restore_noise($this->_[HDOM_INFO_TEXT]);
+
+		$ret = '';
+		foreach ($this->nodes as $n)
+			$ret .= $n->outertext();
+		return $ret;
+	}
+
+	// get dom node's outer text (with tag)
+	function outertext()
+	{
+		global $debug_object;
+		if (is_object($debug_object))
+		{
+			$text = '';
+			if ($this->tag == 'text')
+			{
+				if (!empty($this->text))
+				{
+					$text = " with text: " . $this->text;
+				}
+			}
+			$debug_object->debug_log(1, 'Innertext of tag: ' . $this->tag . $text);
+		}
+
+		if ($this->tag==='root') return $this->innertext();
+
+		// trigger callback
+		if ($this->dom && $this->dom->callback!==null)
+		{
+			call_user_func_array($this->dom->callback, array($this));
+		}
+
+		if (isset($this->_[HDOM_INFO_OUTER])) return $this->_[HDOM_INFO_OUTER];
+		if (isset($this->_[HDOM_INFO_TEXT])) return $this->dom->restore_noise($this->_[HDOM_INFO_TEXT]);
+
+		// render begin tag
+		if ($this->dom && $this->dom->nodes[$this->_[HDOM_INFO_BEGIN]])
+		{
+			$ret = $this->dom->nodes[$this->_[HDOM_INFO_BEGIN]]->makeup();
+		} else {
+			$ret = "";
+		}
+
+		// render inner text
+		if (isset($this->_[HDOM_INFO_INNER]))
+		{
+			// If it's a br tag...  don't return the HDOM_INNER_INFO that we may or may not have added.
+			if ($this->tag != "br")
+			{
+				$ret .= $this->_[HDOM_INFO_INNER];
+			}
+		} else {
+			if ($this->nodes)
+			{
+				foreach ($this->nodes as $n)
+				{
+					$ret .= $this->convert_text($n->outertext());
+				}
+			}
+		}
+
+		// render end tag
+		if (isset($this->_[HDOM_INFO_END]) && $this->_[HDOM_INFO_END]!=0)
+			$ret .= '</'.$this->tag.'>';
+		return $ret;
+	}
+
+	// get dom node's plain text
+	function text()
+	{
+		if (isset($this->_[HDOM_INFO_INNER])) return $this->_[HDOM_INFO_INNER];
+		switch ($this->nodetype)
+		{
+			case HDOM_TYPE_TEXT: return $this->dom->restore_noise($this->_[HDOM_INFO_TEXT]);
+			case HDOM_TYPE_COMMENT: return '';
+			case HDOM_TYPE_UNKNOWN: return '';
+		}
+		if (strcasecmp($this->tag, 'script')===0) return '';
+		if (strcasecmp($this->tag, 'style')===0) return '';
+
+		$ret = '';
+		// In rare cases, (always node type 1 or HDOM_TYPE_ELEMENT - observed for some span tags, and some p tags) $this->nodes is set to NULL.
+		// NOTE: This indicates that there is a problem where it's set to NULL without a clear happening.
+		// WHY is this happening?
+		if (!is_null($this->nodes))
+		{
+			foreach ($this->nodes as $n)
+			{
+				$ret .= $this->convert_text($n->text());
+			}
+
+			// If this node is a span... add a space at the end of it so multiple spans don't run into each other.  This is plaintext after all.
+			if ($this->tag == "span")
+			{
+				$ret .= $this->dom->default_span_text;
+			}
+
+
+		}
+		return $ret;
+	}
+
+	function xmltext()
+	{
+		$ret = $this->innertext();
+		$ret = str_ireplace('<![CDATA[', '', $ret);
+		$ret = str_replace(']]>', '', $ret);
+		return $ret;
+	}
+
+	// build node's text with tag
+	function makeup()
+	{
+		// text, comment, unknown
+		if (isset($this->_[HDOM_INFO_TEXT])) return $this->dom->restore_noise($this->_[HDOM_INFO_TEXT]);
+
+		$ret = '<'.$this->tag;
+		$i = -1;
+
+		foreach ($this->attr as $key=>$val)
+		{
+			++$i;
+
+			// skip removed attribute
+			if ($val===null || $val===false)
+				continue;
+
+			$ret .= $this->_[HDOM_INFO_SPACE][$i][0];
+			//no value attr: nowrap, checked selected...
+			if ($val===true)
+				$ret .= $key;
+			else {
+				switch ($this->_[HDOM_INFO_QUOTE][$i])
+				{
+					case HDOM_QUOTE_DOUBLE: $quote = '"'; break;
+					case HDOM_QUOTE_SINGLE: $quote = '\''; break;
+					default: $quote = '';
+				}
+				$ret .= $key.$this->_[HDOM_INFO_SPACE][$i][1].'='.$this->_[HDOM_INFO_SPACE][$i][2].$quote.$val.$quote;
+			}
+		}
+		$ret = $this->dom->restore_noise($ret);
+		return $ret . $this->_[HDOM_INFO_ENDSPACE] . '>';
+	}
+
+	// find elements by css selector
+	//PaperG - added ability for find to lowercase the value of the selector.
+	function find($selector, $idx=null, $lowercase=false)
+	{
+		$selectors = $this->parse_selector($selector);
+		if (($count=count($selectors))===0) return array();
+		$found_keys = array();
+
+		// find each selector
+		for ($c=0; $c<$count; ++$c)
+		{
+			// The change on the below line was documented on the sourceforge code tracker id 2788009
+			// used to be: if (($levle=count($selectors[0]))===0) return array();
+			if (($levle=count($selectors[$c]))===0) return array();
+			if (!isset($this->_[HDOM_INFO_BEGIN])) return array();
+
+			$head = array($this->_[HDOM_INFO_BEGIN]=>1);
+
+			// handle descendant selectors, no recursive!
+			for ($l=0; $l<$levle; ++$l)
+			{
+				$ret = array();
+				foreach ($head as $k=>$v)
+				{
+					$n = ($k===-1) ? $this->dom->root : $this->dom->nodes[$k];
+					//PaperG - Pass this optional parameter on to the seek function.
+					$n->seek($selectors[$c][$l], $ret, $lowercase);
+				}
+				$head = $ret;
+			}
+
+			foreach ($head as $k=>$v)
+			{
+				if (!isset($found_keys[$k]))
+				{
+					$found_keys[$k] = 1;
+				}
+			}
+		}
+
+		// sort keys
+		ksort($found_keys);
+
+		$found = array();
+		foreach ($found_keys as $k=>$v)
+			$found[] = $this->dom->nodes[$k];
+
+		// return nth-element or array
+		if (is_null($idx)) return $found;
+		else if ($idx<0) $idx = count($found) + $idx;
+		return (isset($found[$idx])) ? $found[$idx] : null;
+	}
+
+	// seek for given conditions
+	// PaperG - added parameter to allow for case insensitive testing of the value of a selector.
+	protected function seek($selector, &$ret, $lowercase=false)
+	{
+		global $debug_object;
+		if (is_object($debug_object)) { $debug_object->debug_log_entry(1); }
+
+		list($tag, $key, $val, $exp, $no_key) = $selector;
+
+		// xpath index
+		if ($tag && $key && is_numeric($key))
+		{
+			$count = 0;
+			foreach ($this->children as $c)
+			{
+				if ($tag==='*' || $tag===$c->tag) {
+					if (++$count==$key) {
+						$ret[$c->_[HDOM_INFO_BEGIN]] = 1;
+						return;
+					}
+				}
+			}
+			return;
+		}
+
+		$end = (!empty($this->_[HDOM_INFO_END])) ? $this->_[HDOM_INFO_END] : 0;
+		if ($end==0) {
+			$parent = $this->parent;
+			while (!isset($parent->_[HDOM_INFO_END]) && $parent!==null) {
+				$end -= 1;
+				$parent = $parent->parent;
+			}
+			$end += $parent->_[HDOM_INFO_END];
+		}
+
+		for ($i=$this->_[HDOM_INFO_BEGIN]+1; $i<$end; ++$i) {
+			$node = $this->dom->nodes[$i];
+
+			$pass = true;
+
+			if ($tag==='*' && !$key) {
+				if (in_array($node, $this->children, true))
+					$ret[$i] = 1;
+				continue;
+			}
+
+			// compare tag
+			if ($tag && $tag!=$node->tag && $tag!=='*') {$pass=false;}
+			// compare key
+			if ($pass && $key) {
+				if ($no_key) {
+					if (isset($node->attr[$key])) $pass=false;
+				} else {
+					if (($key != "plaintext") && !isset($node->attr[$key])) $pass=false;
+				}
+			}
+			// compare value
+			if ($pass && $key && $val  && $val!=='*') {
+				// If they have told us that this is a "plaintext" search then we want the plaintext of the node - right?
+				if ($key == "plaintext") {
+					// $node->plaintext actually returns $node->text();
+					$nodeKeyValue = $node->text();
+				} else {
+					// this is a normal search, we want the value of that attribute of the tag.
+					$nodeKeyValue = $node->attr[$key];
+				}
+				if (is_object($debug_object)) {$debug_object->debug_log(2, "testing node: " . $node->tag . " for attribute: " . $key . $exp . $val . " where nodes value is: " . $nodeKeyValue);}
+
+				//PaperG - If lowercase is set, do a case insensitive test of the value of the selector.
+				if ($lowercase) {
+					$check = $this->match($exp, strtolower($val), strtolower($nodeKeyValue));
+				} else {
+					$check = $this->match($exp, $val, $nodeKeyValue);
+				}
+				if (is_object($debug_object)) {$debug_object->debug_log(2, "after match: " . ($check ? "true" : "false"));}
+
+				// handle multiple class
+				if (!$check && strcasecmp($key, 'class')===0) {
+					foreach (explode(' ',$node->attr[$key]) as $k) {
+						// Without this, there were cases where leading, trailing, or double spaces lead to our comparing blanks - bad form.
+						if (!empty($k)) {
+							if ($lowercase) {
+								$check = $this->match($exp, strtolower($val), strtolower($k));
+							} else {
+								$check = $this->match($exp, $val, $k);
+							}
+							if ($check) break;
+						}
+					}
+				}
+				if (!$check) $pass = false;
+			}
+			if ($pass) $ret[$i] = 1;
+			unset($node);
+		}
+		// It's passed by reference so this is actually what this function returns.
+		if (is_object($debug_object)) {$debug_object->debug_log(1, "EXIT - ret: ", $ret);}
+	}
+
+	protected function match($exp, $pattern, $value) {
+		global $debug_object;
+		if (is_object($debug_object)) {$debug_object->debug_log_entry(1);}
+
+		switch ($exp) {
+			case '=':
+				return ($value===$pattern);
+			case '!=':
+				return ($value!==$pattern);
+			case '^=':
+				return preg_match("/^".preg_quote($pattern,'/')."/", $value);
+			case '$=':
+				return preg_match("/".preg_quote($pattern,'/')."$/", $value);
+			case '*=':
+				if ($pattern[0]=='/') {
+					return preg_match($pattern, $value);
+				}
+				return preg_match("/".$pattern."/i", $value);
+		}
+		return false;
+	}
+
+	protected function parse_selector($selector_string) {
+		global $debug_object;
+		if (is_object($debug_object)) {$debug_object->debug_log_entry(1);}
+
+		// pattern of CSS selectors, modified from mootools
+		// Paperg: Add the colon to the attrbute, so that it properly finds <tag attr:ibute="something" > like google does.
+		// Note: if you try to look at this attribute, yo MUST use getAttribute since $dom->x:y will fail the php syntax check.
+// Notice the \[ starting the attbute?  and the @? following?  This implies that an attribute can begin with an @ sign that is not captured.
+// This implies that an html attribute specifier may start with an @ sign that is NOT captured by the expression.
+// farther study is required to determine of this should be documented or removed.
+//		$pattern = "/([\w-:\*]*)(?:\#([\w-]+)|\.([\w-]+))?(?:\[@?(!?[\w-]+)(?:([!*^$]?=)[\"']?(.*?)[\"']?)?\])?([\/, ]+)/is";
+		$pattern = "/([\w-:\*]*)(?:\#([\w-]+)|\.([\w-]+))?(?:\[@?(!?[\w-:]+)(?:([!*^$]?=)[\"']?(.*?)[\"']?)?\])?([\/, ]+)/is";
+		preg_match_all($pattern, trim($selector_string).' ', $matches, PREG_SET_ORDER);
+		if (is_object($debug_object)) {$debug_object->debug_log(2, "Matches Array: ", $matches);}
+
+		$selectors = array();
+		$result = array();
+		//print_r($matches);
+
+		foreach ($matches as $m) {
+			$m[0] = trim($m[0]);
+			if ($m[0]==='' || $m[0]==='/' || $m[0]==='//') continue;
+			// for browser generated xpath
+			if ($m[1]==='tbody') continue;
+
+			list($tag, $key, $val, $exp, $no_key) = array($m[1], null, null, '=', false);
+			if (!empty($m[2])) {$key='id'; $val=$m[2];}
+			if (!empty($m[3])) {$key='class'; $val=$m[3];}
+			if (!empty($m[4])) {$key=$m[4];}
+			if (!empty($m[5])) {$exp=$m[5];}
+			if (!empty($m[6])) {$val=$m[6];}
+
+			// convert to lowercase
+			if ($this->dom->lowercase) {$tag=strtolower($tag); $key=strtolower($key);}
+			//elements that do NOT have the specified attribute
+			if (isset($key[0]) && $key[0]==='!') {$key=substr($key, 1); $no_key=true;}
+
+			$result[] = array($tag, $key, $val, $exp, $no_key);
+			if (trim($m[7])===',') {
+				$selectors[] = $result;
+				$result = array();
+			}
+		}
+		if (count($result)>0)
+			$selectors[] = $result;
+		return $selectors;
+	}
+
+	function __get($name)
+	{
+		if (isset($this->attr[$name]))
+		{
+			return $this->convert_text($this->attr[$name]);
+		}
+		switch ($name)
+		{
+			case 'outertext': return $this->outertext();
+			case 'innertext': return $this->innertext();
+			case 'plaintext': return $this->text();
+			case 'xmltext': return $this->xmltext();
+			default: return array_key_exists($name, $this->attr);
+		}
+	}
+
+	function __set($name, $value)
+	{
+		global $debug_object;
+		if (is_object($debug_object)) {$debug_object->debug_log_entry(1);}
+
+		switch ($name)
+		{
+			case 'outertext': return $this->_[HDOM_INFO_OUTER] = $value;
+			case 'innertext':
+				if (isset($this->_[HDOM_INFO_TEXT])) return $this->_[HDOM_INFO_TEXT] = $value;
+				return $this->_[HDOM_INFO_INNER] = $value;
+		}
+		if (!isset($this->attr[$name]))
+		{
+			$this->_[HDOM_INFO_SPACE][] = array(' ', '', '');
+			$this->_[HDOM_INFO_QUOTE][] = HDOM_QUOTE_DOUBLE;
+		}
+		$this->attr[$name] = $value;
+	}
+
+	function __isset($name)
+	{
+		switch ($name)
+		{
+			case 'outertext': return true;
+			case 'innertext': return true;
+			case 'plaintext': return true;
+		}
+		//no value attr: nowrap, checked selected...
+		return (array_key_exists($name, $this->attr)) ? true : isset($this->attr[$name]);
+	}
+
+	function __unset($name) {
+		if (isset($this->attr[$name]))
+			unset($this->attr[$name]);
+	}
+
+	// PaperG - Function to convert the text from one character set to another if the two sets are not the same.
+	function convert_text($text)
+	{
+		global $debug_object;
+		if (is_object($debug_object)) {$debug_object->debug_log_entry(1);}
+
+		$converted_text = $text;
+
+		$sourceCharset = "";
+		$targetCharset = "";
+
+		if ($this->dom)
+		{
+			$sourceCharset = strtoupper($this->dom->_charset);
+			$targetCharset = strtoupper($this->dom->_target_charset);
+		}
+		if (is_object($debug_object)) {$debug_object->debug_log(3, "source charset: " . $sourceCharset . " target charaset: " . $targetCharset);}
+
+		if (!empty($sourceCharset) && !empty($targetCharset) && (strcasecmp($sourceCharset, $targetCharset) != 0))
+		{
+			// Check if the reported encoding could have been incorrect and the text is actually already UTF-8
+			if ((strcasecmp($targetCharset, 'UTF-8') == 0) && ($this->is_utf8($text)))
+			{
+				$converted_text = $text;
+			}
+			else
+			{
+				$converted_text = iconv($sourceCharset, $targetCharset, $text);
+			}
+		}
+
+		// Lets make sure that we don't have that silly BOM issue with any of the utf-8 text we output.
+		if ($targetCharset == 'UTF-8')
+		{
+			if (substr($converted_text, 0, 3) == "\xef\xbb\xbf")
+			{
+				$converted_text = substr($converted_text, 3);
+			}
+			if (substr($converted_text, -3) == "\xef\xbb\xbf")
+			{
+				$converted_text = substr($converted_text, 0, -3);
+			}
+		}
+
+		return $converted_text;
+	}
+
+	/**
+	* Returns true if $string is valid UTF-8 and false otherwise.
+	*
+	* @param mixed $str String to be tested
+	* @return boolean
+	*/
+	static function is_utf8($str)
+	{
+		$c=0; $b=0;
+		$bits=0;
+		$len=strlen($str);
+		for($i=0; $i<$len; $i++)
+		{
+			$c=ord($str[$i]);
+			if($c > 128)
+			{
+				if(($c >= 254)) return false;
+				elseif($c >= 252) $bits=6;
+				elseif($c >= 248) $bits=5;
+				elseif($c >= 240) $bits=4;
+				elseif($c >= 224) $bits=3;
+				elseif($c >= 192) $bits=2;
+				else return false;
+				if(($i+$bits) > $len) return false;
+				while($bits > 1)
+				{
+					$i++;
+					$b=ord($str[$i]);
+					if($b < 128 || $b > 191) return false;
+					$bits--;
+				}
+			}
+		}
+		return true;
+	}
+	/*
+	function is_utf8($string)
+	{
+		//this is buggy
+		return (utf8_encode(utf8_decode($string)) == $string);
+	}
+	*/
+
+	/**
+	 * Function to try a few tricks to determine the displayed size of an img on the page.
+	 * NOTE: This will ONLY work on an IMG tag. Returns FALSE on all other tag types.
+	 *
+	 * @author John Schlick
+	 * @version April 19 2012
+	 * @return array an array containing the 'height' and 'width' of the image on the page or -1 if we can't figure it out.
+	 */
+	function get_display_size()
+	{
+		global $debug_object;
+
+		$width = -1;
+		$height = -1;
+
+		if ($this->tag !== 'img')
+		{
+			return false;
+		}
+
+		// See if there is aheight or width attribute in the tag itself.
+		if (isset($this->attr['width']))
+		{
+			$width = $this->attr['width'];
+		}
+
+		if (isset($this->attr['height']))
+		{
+			$height = $this->attr['height'];
+		}
+
+		// Now look for an inline style.
+		if (isset($this->attr['style']))
+		{
+			// Thanks to user gnarf from stackoverflow for this regular expression.
+			$attributes = array();
+			preg_match_all("/([\w-]+)\s*:\s*([^;]+)\s*;?/", $this->attr['style'], $matches, PREG_SET_ORDER);
+			foreach ($matches as $match) {
+			  $attributes[$match[1]] = $match[2];
+			}
+
+			// If there is a width in the style attributes:
+			if (isset($attributes['width']) && $width == -1)
+			{
+				// check that the last two characters are px (pixels)
+				if (strtolower(substr($attributes['width'], -2)) == 'px')
+				{
+					$proposed_width = substr($attributes['width'], 0, -2);
+					// Now make sure that it's an integer and not something stupid.
+					if (filter_var($proposed_width, FILTER_VALIDATE_INT))
+					{
+						$width = $proposed_width;
+					}
+				}
+			}
+
+			// If there is a width in the style attributes:
+			if (isset($attributes['height']) && $height == -1)
+			{
+				// check that the last two characters are px (pixels)
+				if (strtolower(substr($attributes['height'], -2)) == 'px')
+				{
+					$proposed_height = substr($attributes['height'], 0, -2);
+					// Now make sure that it's an integer and not something stupid.
+					if (filter_var($proposed_height, FILTER_VALIDATE_INT))
+					{
+						$height = $proposed_height;
+					}
+				}
+			}
+
+		}
+
+		// Future enhancement:
+		// Look in the tag to see if there is a class or id specified that has a height or width attribute to it.
+
+		// Far future enhancement
+		// Look at all the parent tags of this image to see if they specify a class or id that has an img selector that specifies a height or width
+		// Note that in this case, the class or id will have the img subselector for it to apply to the image.
+
+		// ridiculously far future development
+		// If the class or id is specified in a SEPARATE css file thats not on the page, go get it and do what we were just doing for the ones on the page.
+
+		$result = array('height' => $height,
+						'width' => $width);
+		return $result;
+	}
+
+	// camel naming conventions
+	function getAllAttributes() {return $this->attr;}
+	function getAttribute($name) {return $this->__get($name);}
+	function setAttribute($name, $value) {$this->__set($name, $value);}
+	function hasAttribute($name) {return $this->__isset($name);}
+	function removeAttribute($name) {$this->__set($name, null);}
+	function getElementById($id) {return $this->find("#$id", 0);}
+	function getElementsById($id, $idx=null) {return $this->find("#$id", $idx);}
+	function getElementByTagName($name) {return $this->find($name, 0);}
+	function getElementsByTagName($name, $idx=null) {return $this->find($name, $idx);}
+	function parentNode() {return $this->parent();}
+	function childNodes($idx=-1) {return $this->children($idx);}
+	function firstChild() {return $this->first_child();}
+	function lastChild() {return $this->last_child();}
+	function nextSibling() {return $this->next_sibling();}
+	function previousSibling() {return $this->prev_sibling();}
+	function hasChildNodes() {return $this->has_child();}
+	function nodeName() {return $this->tag;}
+	function appendChild($node) {$node->parent($this); return $node;}
+
+}
+
+/**
+ * simple html dom parser
+ * Paperg - in the find routine: allow us to specify that we want case insensitive testing of the value of the selector.
+ * Paperg - change $size from protected to public so we can easily access it
+ * Paperg - added ForceTagsClosed in the constructor which tells us whether we trust the html or not.  Default is to NOT trust it.
+ *
+ * @package PlaceLocalInclude
+ */
+class simple_html_dom
+{
+	public $root = null;
+	public $nodes = array();
+	public $callback = null;
+	public $lowercase = false;
+	// Used to keep track of how large the text was when we started.
+	public $original_size;
+	public $size;
+	protected $pos;
+	protected $doc;
+	protected $char;
+	protected $cursor;
+	protected $parent;
+	protected $noise = array();
+	protected $token_blank = " \t\r\n";
+	protected $token_equal = ' =/>';
+	protected $token_slash = " />\r\n\t";
+	protected $token_attr = ' >';
+	// Note that this is referenced by a child node, and so it needs to be public for that node to see this information.
+	public $_charset = '';
+	public $_target_charset = '';
+	protected $default_br_text = "";
+	public $default_span_text = "";
+
+	// use isset instead of in_array, performance boost about 30%...
+	protected $self_closing_tags = array('img'=>1, 'br'=>1, 'input'=>1, 'meta'=>1, 'link'=>1, 'hr'=>1, 'base'=>1, 'embed'=>1, 'spacer'=>1);
+	protected $block_tags = array('root'=>1, 'body'=>1, 'form'=>1, 'div'=>1, 'span'=>1, 'table'=>1);
+	// Known sourceforge issue #2977341
+	// B tags that are not closed cause us to return everything to the end of the document.
+	protected $optional_closing_tags = array(
+		'tr'=>array('tr'=>1, 'td'=>1, 'th'=>1),
+		'th'=>array('th'=>1),
+		'td'=>array('td'=>1),
+		'li'=>array('li'=>1),
+		'dt'=>array('dt'=>1, 'dd'=>1),
+		'dd'=>array('dd'=>1, 'dt'=>1),
+		'dl'=>array('dd'=>1, 'dt'=>1),
+		'p'=>array('p'=>1),
+		'nobr'=>array('nobr'=>1),
+		'b'=>array('b'=>1),
+		'option'=>array('option'=>1),
+	);
+
+	function __construct($str=null, $lowercase=true, $forceTagsClosed=true, $target_charset=DEFAULT_TARGET_CHARSET, $stripRN=true, $defaultBRText=DEFAULT_BR_TEXT, $defaultSpanText=DEFAULT_SPAN_TEXT)
+	{
+		if ($str)
+		{
+			if (preg_match("/^http:\/\//i",$str) || is_file($str))
+			{
+				$this->load_file($str);
+			}
+			else
+			{
+				$this->load($str, $lowercase, $stripRN, $defaultBRText, $defaultSpanText);
+			}
+		}
+		// Forcing tags to be closed implies that we don't trust the html, but it can lead to parsing errors if we SHOULD trust the html.
+		if (!$forceTagsClosed) {
+			$this->optional_closing_array=array();
+		}
+		$this->_target_charset = $target_charset;
+	}
+
+	function __destruct()
+	{
+		$this->clear();
+	}
+
+	// load html from string
+	function load($str, $lowercase=true, $stripRN=true, $defaultBRText=DEFAULT_BR_TEXT, $defaultSpanText=DEFAULT_SPAN_TEXT)
+	{
+		global $debug_object;
+
+		// prepare
+		$this->prepare($str, $lowercase, $stripRN, $defaultBRText, $defaultSpanText);
+		// strip out cdata
+		$this->remove_noise("'<!\[CDATA\[(.*?)\]\]>'is", true);
+		// strip out comments
+		$this->remove_noise("'<!--(.*?)-->'is");
+		// Per sourceforge http://sourceforge.net/tracker/?func=detail&aid=2949097&group_id=218559&atid=1044037
+		// Script tags removal now preceeds style tag removal.
+		// strip out <script> tags
+		$this->remove_noise("'<\s*script[^>]*[^/]>(.*?)<\s*/\s*script\s*>'is");
+		$this->remove_noise("'<\s*script\s*>(.*?)<\s*/\s*script\s*>'is");
+		// strip out <style> tags
+		$this->remove_noise("'<\s*style[^>]*[^/]>(.*?)<\s*/\s*style\s*>'is");
+		$this->remove_noise("'<\s*style\s*>(.*?)<\s*/\s*style\s*>'is");
+		// strip out preformatted tags
+		$this->remove_noise("'<\s*(?:code)[^>]*>(.*?)<\s*/\s*(?:code)\s*>'is");
+		// strip out server side scripts
+		$this->remove_noise("'(<\?)(.*?)(\?>)'s", true);
+		// strip smarty scripts
+		$this->remove_noise("'(\{\w)(.*?)(\})'s", true);
+
+		// parsing
+		while ($this->parse());
+		// end
+		$this->root->_[HDOM_INFO_END] = $this->cursor;
+		$this->parse_charset();
+
+		// make load function chainable
+		return $this;
+
+	}
+
+	// load html from file
+	function load_file()
+	{
+		$args = func_get_args();
+		$this->load(call_user_func_array('file_get_contents', $args), true);
+		// Throw an error if we can't properly load the dom.
+		if (($error=error_get_last())!==null) {
+			$this->clear();
+			return false;
+		}
+	}
+
+	// set callback function
+	function set_callback($function_name)
+	{
+		$this->callback = $function_name;
+	}
+
+	// remove callback function
+	function remove_callback()
+	{
+		$this->callback = null;
+	}
+
+	// save dom as string
+	function save($filepath='')
+	{
+		$ret = $this->root->innertext();
+		if ($filepath!=='') file_put_contents($filepath, $ret, LOCK_EX);
+		return $ret;
+	}
+
+	// find dom node by css selector
+	// Paperg - allow us to specify that we want case insensitive testing of the value of the selector.
+	function find($selector, $idx=null, $lowercase=false)
+	{
+		return $this->root->find($selector, $idx, $lowercase);
+	}
+
+	// clean up memory due to php5 circular references memory leak...
+	function clear()
+	{
+		foreach ($this->nodes as $n) {$n->clear(); $n = null;}
+		// This add next line is documented in the sourceforge repository. 2977248 as a fix for ongoing memory leaks that occur even with the use of clear.
+		if (isset($this->children)) foreach ($this->children as $n) {$n->clear(); $n = null;}
+		if (isset($this->parent)) {$this->parent->clear(); unset($this->parent);}
+		if (isset($this->root)) {$this->root->clear(); unset($this->root);}
+		unset($this->doc);
+		unset($this->noise);
+	}
+
+	function dump($show_attr=true)
+	{
+		$this->root->dump($show_attr);
+	}
+
+	// prepare HTML data and init everything
+	protected function prepare($str, $lowercase=true, $stripRN=true, $defaultBRText=DEFAULT_BR_TEXT, $defaultSpanText=DEFAULT_SPAN_TEXT)
+	{
+		$this->clear();
+
+		// set the length of content before we do anything to it.
+		$this->size = strlen($str);
+		// Save the original size of the html that we got in.  It might be useful to someone.
+		$this->original_size = $this->size;
+
+		//before we save the string as the doc...  strip out the \r \n's if we are told to.
+		if ($stripRN) {
+			$str = str_replace("\r", " ", $str);
+			$str = str_replace("\n", " ", $str);
+
+			// set the length of content since we have changed it.
+			$this->size = strlen($str);
+		}
+
+		$this->doc = $str;
+		$this->pos = 0;
+		$this->cursor = 1;
+		$this->noise = array();
+		$this->nodes = array();
+		$this->lowercase = $lowercase;
+		$this->default_br_text = $defaultBRText;
+		$this->default_span_text = $defaultSpanText;
+		$this->root = new simple_html_dom_node($this);
+		$this->root->tag = 'root';
+		$this->root->_[HDOM_INFO_BEGIN] = -1;
+		$this->root->nodetype = HDOM_TYPE_ROOT;
+		$this->parent = $this->root;
+		if ($this->size>0) $this->char = $this->doc[0];
+	}
+
+	// parse html content
+	protected function parse()
+	{
+		if (($s = $this->copy_until_char('<'))==='')
+		{
+			return $this->read_tag();
+		}
+
+		// text
+		$node = new simple_html_dom_node($this);
+		++$this->cursor;
+		$node->_[HDOM_INFO_TEXT] = $s;
+		$this->link_nodes($node, false);
+		return true;
+	}
+
+	// PAPERG - dkchou - added this to try to identify the character set of the page we have just parsed so we know better how to spit it out later.
+	// NOTE:  IF you provide a routine called get_last_retrieve_url_contents_content_type which returns the CURLINFO_CONTENT_TYPE from the last curl_exec
+	// (or the content_type header from the last transfer), we will parse THAT, and if a charset is specified, we will use it over any other mechanism.
+	protected function parse_charset()
+	{
+		global $debug_object;
+
+		$charset = null;
+
+		if (function_exists('get_last_retrieve_url_contents_content_type'))
+		{
+			$contentTypeHeader = get_last_retrieve_url_contents_content_type();
+			$success = preg_match('/charset=(.+)/', $contentTypeHeader, $matches);
+			if ($success)
+			{
+				$charset = $matches[1];
+				if (is_object($debug_object)) {$debug_object->debug_log(2, 'header content-type found charset of: ' . $charset);}
+			}
+
+		}
+
+		if (empty($charset))
+		{
+			$el = $this->root->find('meta[http-equiv=Content-Type]',0);
+			if (!empty($el))
+			{
+				$fullvalue = $el->content;
+				if (is_object($debug_object)) {$debug_object->debug_log(2, 'meta content-type tag found' . $fullvalue);}
+
+				if (!empty($fullvalue))
+				{
+					$success = preg_match('/charset=(.+)/', $fullvalue, $matches);
+					if ($success)
+					{
+						$charset = $matches[1];
+					}
+					else
+					{
+						// If there is a meta tag, and they don't specify the character set, research says that it's typically ISO-8859-1
+						if (is_object($debug_object)) {$debug_object->debug_log(2, 'meta content-type tag couldn\'t be parsed. using iso-8859 default.');}
+						$charset = 'ISO-8859-1';
+					}
+				}
+			}
+		}
+
+		// If we couldn't find a charset above, then lets try to detect one based on the text we got...
+		if (empty($charset))
+		{
+			// Use this in case mb_detect_charset isn't installed/loaded on this machine.
+			$charset = false;
+			if (function_exists('mb_detect_encoding'))
+			{
+				// Have php try to detect the encoding from the text given to us.
+				$charset = mb_detect_encoding($this->root->plaintext . "ascii", $encoding_list = array( "UTF-8", "CP1252" ) );
+				if (is_object($debug_object)) {$debug_object->debug_log(2, 'mb_detect found: ' . $charset);}
+			}
+
+			// and if this doesn't work...  then we need to just wrongheadedly assume it's UTF-8 so that we can move on - cause this will usually give us most of what we need...
+			if ($charset === false)
+			{
+				if (is_object($debug_object)) {$debug_object->debug_log(2, 'since mb_detect failed - using default of utf-8');}
+				$charset = 'UTF-8';
+			}
+		}
+
+		// Since CP1252 is a superset, if we get one of it's subsets, we want it instead.
+		if ((strtolower($charset) == strtolower('ISO-8859-1')) || (strtolower($charset) == strtolower('Latin1')) || (strtolower($charset) == strtolower('Latin-1')))
+		{
+			if (is_object($debug_object)) {$debug_object->debug_log(2, 'replacing ' . $charset . ' with CP1252 as its a superset');}
+			$charset = 'CP1252';
+		}
+
+		if (is_object($debug_object)) {$debug_object->debug_log(1, 'EXIT - ' . $charset);}
+
+		return $this->_charset = $charset;
+	}
+
+	// read tag info
+	protected function read_tag()
+	{
+		if ($this->char!=='<')
+		{
+			$this->root->_[HDOM_INFO_END] = $this->cursor;
+			return false;
+		}
+		$begin_tag_pos = $this->pos;
+		$this->char = (++$this->pos<$this->size) ? $this->doc[$this->pos] : null; // next
+
+		// end tag
+		if ($this->char==='/')
+		{
+			$this->char = (++$this->pos<$this->size) ? $this->doc[$this->pos] : null; // next
+			// This represents the change in the simple_html_dom trunk from revision 180 to 181.
+			// $this->skip($this->token_blank_t);
+			$this->skip($this->token_blank);
+			$tag = $this->copy_until_char('>');
+
+			// skip attributes in end tag
+			if (($pos = strpos($tag, ' '))!==false)
+				$tag = substr($tag, 0, $pos);
+
+			$parent_lower = strtolower($this->parent->tag);
+			$tag_lower = strtolower($tag);
+
+			if ($parent_lower!==$tag_lower)
+			{
+				if (isset($this->optional_closing_tags[$parent_lower]) && isset($this->block_tags[$tag_lower]))
+				{
+					$this->parent->_[HDOM_INFO_END] = 0;
+					$org_parent = $this->parent;
+
+					while (($this->parent->parent) && strtolower($this->parent->tag)!==$tag_lower)
+						$this->parent = $this->parent->parent;
+
+					if (strtolower($this->parent->tag)!==$tag_lower) {
+						$this->parent = $org_parent; // restore origonal parent
+						if ($this->parent->parent) $this->parent = $this->parent->parent;
+						$this->parent->_[HDOM_INFO_END] = $this->cursor;
+						return $this->as_text_node($tag);
+					}
+				}
+				else if (($this->parent->parent) && isset($this->block_tags[$tag_lower]))
+				{
+					$this->parent->_[HDOM_INFO_END] = 0;
+					$org_parent = $this->parent;
+
+					while (($this->parent->parent) && strtolower($this->parent->tag)!==$tag_lower)
+						$this->parent = $this->parent->parent;
+
+					if (strtolower($this->parent->tag)!==$tag_lower)
+					{
+						$this->parent = $org_parent; // restore origonal parent
+						$this->parent->_[HDOM_INFO_END] = $this->cursor;
+						return $this->as_text_node($tag);
+					}
+				}
+				else if (($this->parent->parent) && strtolower($this->parent->parent->tag)===$tag_lower)
+				{
+					$this->parent->_[HDOM_INFO_END] = 0;
+					$this->parent = $this->parent->parent;
+				}
+				else
+					return $this->as_text_node($tag);
+			}
+
+			$this->parent->_[HDOM_INFO_END] = $this->cursor;
+			if ($this->parent->parent) $this->parent = $this->parent->parent;
+
+			$this->char = (++$this->pos<$this->size) ? $this->doc[$this->pos] : null; // next
+			return true;
+		}
+
+		$node = new simple_html_dom_node($this);
+		$node->_[HDOM_INFO_BEGIN] = $this->cursor;
+		++$this->cursor;
+		$tag = $this->copy_until($this->token_slash);
+		$node->tag_start = $begin_tag_pos;
+
+		// doctype, cdata & comments...
+		if (isset($tag[0]) && $tag[0]==='!') {
+			$node->_[HDOM_INFO_TEXT] = '<' . $tag . $this->copy_until_char('>');
+
+			if (isset($tag[2]) && $tag[1]==='-' && $tag[2]==='-') {
+				$node->nodetype = HDOM_TYPE_COMMENT;
+				$node->tag = 'comment';
+			} else {
+				$node->nodetype = HDOM_TYPE_UNKNOWN;
+				$node->tag = 'unknown';
+			}
+			if ($this->char==='>') $node->_[HDOM_INFO_TEXT].='>';
+			$this->link_nodes($node, true);
+			$this->char = (++$this->pos<$this->size) ? $this->doc[$this->pos] : null; // next
+			return true;
+		}
+
+		// text
+		if ($pos=strpos($tag, '<')!==false) {
+			$tag = '<' . substr($tag, 0, -1);
+			$node->_[HDOM_INFO_TEXT] = $tag;
+			$this->link_nodes($node, false);
+			$this->char = $this->doc[--$this->pos]; // prev
+			return true;
+		}
+
+		if (!preg_match("/^[\w-:]+$/", $tag)) {
+			$node->_[HDOM_INFO_TEXT] = '<' . $tag . $this->copy_until('<>');
+			if ($this->char==='<') {
+				$this->link_nodes($node, false);
+				return true;
+			}
+
+			if ($this->char==='>') $node->_[HDOM_INFO_TEXT].='>';
+			$this->link_nodes($node, false);
+			$this->char = (++$this->pos<$this->size) ? $this->doc[$this->pos] : null; // next
+			return true;
+		}
+
+		// begin tag
+		$node->nodetype = HDOM_TYPE_ELEMENT;
+		$tag_lower = strtolower($tag);
+		$node->tag = ($this->lowercase) ? $tag_lower : $tag;
+
+		// handle optional closing tags
+		if (isset($this->optional_closing_tags[$tag_lower]) )
+		{
+			while (isset($this->optional_closing_tags[$tag_lower][strtolower($this->parent->tag)]))
+			{
+				$this->parent->_[HDOM_INFO_END] = 0;
+				$this->parent = $this->parent->parent;
+			}
+			$node->parent = $this->parent;
+		}
+
+		$guard = 0; // prevent infinity loop
+		$space = array($this->copy_skip($this->token_blank), '', '');
+
+		// attributes
+		do
+		{
+			if ($this->char!==null && $space[0]==='')
+			{
+				break;
+			}
+			$name = $this->copy_until($this->token_equal);
+			if ($guard===$this->pos)
+			{
+				$this->char = (++$this->pos<$this->size) ? $this->doc[$this->pos] : null; // next
+				continue;
+			}
+			$guard = $this->pos;
+
+			// handle endless '<'
+			if ($this->pos>=$this->size-1 && $this->char!=='>') {
+				$node->nodetype = HDOM_TYPE_TEXT;
+				$node->_[HDOM_INFO_END] = 0;
+				$node->_[HDOM_INFO_TEXT] = '<'.$tag . $space[0] . $name;
+				$node->tag = 'text';
+				$this->link_nodes($node, false);
+				return true;
+			}
+
+			// handle mismatch '<'
+			if ($this->doc[$this->pos-1]=='<') {
+				$node->nodetype = HDOM_TYPE_TEXT;
+				$node->tag = 'text';
+				$node->attr = array();
+				$node->_[HDOM_INFO_END] = 0;
+				$node->_[HDOM_INFO_TEXT] = substr($this->doc, $begin_tag_pos, $this->pos-$begin_tag_pos-1);
+				$this->pos -= 2;
+				$this->char = (++$this->pos<$this->size) ? $this->doc[$this->pos] : null; // next
+				$this->link_nodes($node, false);
+				return true;
+			}
+
+			if ($name!=='/' && $name!=='') {
+				$space[1] = $this->copy_skip($this->token_blank);
+				$name = $this->restore_noise($name);
+				if ($this->lowercase) $name = strtolower($name);
+				if ($this->char==='=') {
+					$this->char = (++$this->pos<$this->size) ? $this->doc[$this->pos] : null; // next
+					$this->parse_attr($node, $name, $space);
+				}
+				else {
+					//no value attr: nowrap, checked selected...
+					$node->_[HDOM_INFO_QUOTE][] = HDOM_QUOTE_NO;
+					$node->attr[$name] = true;
+					if ($this->char!='>') $this->char = $this->doc[--$this->pos]; // prev
+				}
+				$node->_[HDOM_INFO_SPACE][] = $space;
+				$space = array($this->copy_skip($this->token_blank), '', '');
+			}
+			else
+				break;
+		} while ($this->char!=='>' && $this->char!=='/');
+
+		$this->link_nodes($node, true);
+		$node->_[HDOM_INFO_ENDSPACE] = $space[0];
+
+		// check self closing
+		if ($this->copy_until_char_escape('>')==='/')
+		{
+			$node->_[HDOM_INFO_ENDSPACE] .= '/';
+			$node->_[HDOM_INFO_END] = 0;
+		}
+		else
+		{
+			// reset parent
+			if (!isset($this->self_closing_tags[strtolower($node->tag)])) $this->parent = $node;
+		}
+		$this->char = (++$this->pos<$this->size) ? $this->doc[$this->pos] : null; // next
+
+		// If it's a BR tag, we need to set it's text to the default text.
+		// This way when we see it in plaintext, we can generate formatting that the user wants.
+		// since a br tag never has sub nodes, this works well.
+		if ($node->tag == "br")
+		{
+			$node->_[HDOM_INFO_INNER] = $this->default_br_text;
+		}
+
+		return true;
+	}
+
+	// parse attributes
+	protected function parse_attr($node, $name, &$space)
+	{
+		// Per sourceforge: http://sourceforge.net/tracker/?func=detail&aid=3061408&group_id=218559&atid=1044037
+		// If the attribute is already defined inside a tag, only pay atetntion to the first one as opposed to the last one.
+		if (isset($node->attr[$name]))
+		{
+			return;
+		}
+
+		$space[2] = $this->copy_skip($this->token_blank);
+		switch ($this->char) {
+			case '"':
+				$node->_[HDOM_INFO_QUOTE][] = HDOM_QUOTE_DOUBLE;
+				$this->char = (++$this->pos<$this->size) ? $this->doc[$this->pos] : null; // next
+				$node->attr[$name] = $this->restore_noise($this->copy_until_char_escape('"'));
+				$this->char = (++$this->pos<$this->size) ? $this->doc[$this->pos] : null; // next
+				break;
+			case '\'':
+				$node->_[HDOM_INFO_QUOTE][] = HDOM_QUOTE_SINGLE;
+				$this->char = (++$this->pos<$this->size) ? $this->doc[$this->pos] : null; // next
+				$node->attr[$name] = $this->restore_noise($this->copy_until_char_escape('\''));
+				$this->char = (++$this->pos<$this->size) ? $this->doc[$this->pos] : null; // next
+				break;
+			default:
+				$node->_[HDOM_INFO_QUOTE][] = HDOM_QUOTE_NO;
+				$node->attr[$name] = $this->restore_noise($this->copy_until($this->token_attr));
+		}
+		// PaperG: Attributes should not have \r or \n in them, that counts as html whitespace.
+		$node->attr[$name] = str_replace("\r", "", $node->attr[$name]);
+		$node->attr[$name] = str_replace("\n", "", $node->attr[$name]);
+		// PaperG: If this is a "class" selector, lets get rid of the preceeding and trailing space since some people leave it in the multi class case.
+		if ($name == "class") {
+			$node->attr[$name] = trim($node->attr[$name]);
+		}
+	}
+
+	// link node's parent
+	protected function link_nodes(&$node, $is_child)
+	{
+		$node->parent = $this->parent;
+		$this->parent->nodes[] = $node;
+		if ($is_child)
+		{
+			$this->parent->children[] = $node;
+		}
+	}
+
+	// as a text node
+	protected function as_text_node($tag)
+	{
+		$node = new simple_html_dom_node($this);
+		++$this->cursor;
+		$node->_[HDOM_INFO_TEXT] = '</' . $tag . '>';
+		$this->link_nodes($node, false);
+		$this->char = (++$this->pos<$this->size) ? $this->doc[$this->pos] : null; // next
+		return true;
+	}
+
+	protected function skip($chars)
+	{
+		$this->pos += strspn($this->doc, $chars, $this->pos);
+		$this->char = ($this->pos<$this->size) ? $this->doc[$this->pos] : null; // next
+	}
+
+	protected function copy_skip($chars)
+	{
+		$pos = $this->pos;
+		$len = strspn($this->doc, $chars, $pos);
+		$this->pos += $len;
+		$this->char = ($this->pos<$this->size) ? $this->doc[$this->pos] : null; // next
+		if ($len===0) return '';
+		return substr($this->doc, $pos, $len);
+	}
+
+	protected function copy_until($chars)
+	{
+		$pos = $this->pos;
+		$len = strcspn($this->doc, $chars, $pos);
+		$this->pos += $len;
+		$this->char = ($this->pos<$this->size) ? $this->doc[$this->pos] : null; // next
+		return substr($this->doc, $pos, $len);
+	}
+
+	protected function copy_until_char($char)
+	{
+		if ($this->char===null) return '';
+
+		if (($pos = strpos($this->doc, $char, $this->pos))===false) {
+			$ret = substr($this->doc, $this->pos, $this->size-$this->pos);
+			$this->char = null;
+			$this->pos = $this->size;
+			return $ret;
+		}
+
+		if ($pos===$this->pos) return '';
+		$pos_old = $this->pos;
+		$this->char = $this->doc[$pos];
+		$this->pos = $pos;
+		return substr($this->doc, $pos_old, $pos-$pos_old);
+	}
+
+	protected function copy_until_char_escape($char)
+	{
+		if ($this->char===null) return '';
+
+		$start = $this->pos;
+		while (1)
+		{
+			if (($pos = strpos($this->doc, $char, $start))===false)
+			{
+				$ret = substr($this->doc, $this->pos, $this->size-$this->pos);
+				$this->char = null;
+				$this->pos = $this->size;
+				return $ret;
+			}
+
+			if ($pos===$this->pos) return '';
+
+			if ($this->doc[$pos-1]==='\\') {
+				$start = $pos+1;
+				continue;
+			}
+
+			$pos_old = $this->pos;
+			$this->char = $this->doc[$pos];
+			$this->pos = $pos;
+			return substr($this->doc, $pos_old, $pos-$pos_old);
+		}
+	}
+
+	// remove noise from html content
+	// save the noise in the $this->noise array.
+	protected function remove_noise($pattern, $remove_tag=false)
+	{
+		global $debug_object;
+		if (is_object($debug_object)) { $debug_object->debug_log_entry(1); }
+
+		$count = preg_match_all($pattern, $this->doc, $matches, PREG_SET_ORDER|PREG_OFFSET_CAPTURE);
+
+		for ($i=$count-1; $i>-1; --$i)
+		{
+			$key = '___noise___'.sprintf('% 5d', count($this->noise)+1000);
+			if (is_object($debug_object)) { $debug_object->debug_log(2, 'key is: ' . $key); }
+			$idx = ($remove_tag) ? 0 : 1;
+			$this->noise[$key] = $matches[$i][$idx][0];
+			$this->doc = substr_replace($this->doc, $key, $matches[$i][$idx][1], strlen($matches[$i][$idx][0]));
+		}
+
+		// reset the length of content
+		$this->size = strlen($this->doc);
+		if ($this->size>0)
+		{
+			$this->char = $this->doc[0];
+		}
+	}
+
+	// restore noise to html content
+	function restore_noise($text)
+	{
+		global $debug_object;
+		if (is_object($debug_object)) { $debug_object->debug_log_entry(1); }
+
+		while (($pos=strpos($text, '___noise___'))!==false)
+		{
+			// Sometimes there is a broken piece of markup, and we don't GET the pos+11 etc... token which indicates a problem outside of us...
+			if (strlen($text) > $pos+15)
+			{
+				$key = '___noise___'.$text[$pos+11].$text[$pos+12].$text[$pos+13].$text[$pos+14].$text[$pos+15];
+				if (is_object($debug_object)) { $debug_object->debug_log(2, 'located key of: ' . $key); }
+
+				if (isset($this->noise[$key]))
+				{
+					$text = substr($text, 0, $pos).$this->noise[$key].substr($text, $pos+16);
+				}
+				else
+				{
+					// do this to prevent an infinite loop.
+					$text = substr($text, 0, $pos).'UNDEFINED NOISE FOR KEY: '.$key . substr($text, $pos+16);
+				}
+			}
+			else
+			{
+				// There is no valid key being given back to us... We must get rid of the ___noise___ or we will have a problem.
+				$text = substr($text, 0, $pos).'NO NUMERIC NOISE KEY' . substr($text, $pos+11);
+			}
+		}
+		return $text;
+	}
+
+	// Sometimes we NEED one of the noise elements.
+	function search_noise($text)
+	{
+		global $debug_object;
+		if (is_object($debug_object)) { $debug_object->debug_log_entry(1); }
+
+		foreach($this->noise as $noiseElement)
+		{
+			if (strpos($noiseElement, $text)!==false)
+			{
+				return $noiseElement;
+			}
+		}
+	}
+	function __toString()
+	{
+		return $this->root->innertext();
+	}
+
+	function __get($name)
+	{
+		switch ($name)
+		{
+			case 'outertext':
+				return $this->root->innertext();
+			case 'innertext':
+				return $this->root->innertext();
+			case 'plaintext':
+				return $this->root->text();
+			case 'charset':
+				return $this->_charset;
+			case 'target_charset':
+				return $this->_target_charset;
+		}
+	}
+
+	// camel naming conventions
+	function childNodes($idx=-1) {return $this->root->childNodes($idx);}
+	function firstChild() {return $this->root->first_child();}
+	function lastChild() {return $this->root->last_child();}
+	function createElement($name, $value=null) {return @str_get_html("<$name>$value</$name>")->first_child();}
+	function createTextNode($value) {return @end(str_get_html($value)->nodes);}
+	function getElementById($id) {return $this->find("#$id", 0);}
+	function getElementsById($id, $idx=null) {return $this->find("#$id", $idx);}
+	function getElementByTagName($name) {return $this->find($name, 0);}
+	function getElementsByTagName($name, $idx=-1) {return $this->find($name, $idx);}
+	function loadFile() {$args = func_get_args();$this->load_file($args);}
+}
+
+?>