]> git.immae.eu Git - github/wallabag/wallabag.git/commitdiff
Merge pull request #109 from inthepoche/dev
authorNicolas Lœuillet <nicolas.loeuillet@gmail.com>
Thu, 8 Aug 2013 16:36:10 +0000 (09:36 -0700)
committerNicolas Lœuillet <nicolas.loeuillet@gmail.com>
Thu, 8 Aug 2013 16:36:10 +0000 (09:36 -0700)
merge dev into master

101 files changed:
.gitignore [new file with mode: 0644]
.travis.yml [new file with mode: 0644]
CREDITS
INSTALL.md [new file with mode: 0644]
README.md
TODO.md [new file with mode: 0644]
composer.json [new file with mode: 0644]
composer.lock [new file with mode: 0644]
css/style-dark.css [deleted file]
css/style-light.css [deleted file]
css/style.css [deleted file]
img/dark/checkmark-off.png [deleted file]
img/dark/checkmark-on.png [deleted file]
img/dark/down.png [deleted file]
img/dark/logo.png [deleted file]
img/dark/remove.png [deleted file]
img/dark/star-off.png [deleted file]
img/dark/star-on.png [deleted file]
img/dark/up.png [deleted file]
img/logo.png [deleted file]
import.php [deleted file]
inc/3rdparty/Encoding.php [moved from inc/Encoding.php with 100% similarity]
inc/3rdparty/JSLikeHTMLElement.php [moved from inc/JSLikeHTMLElement.php with 97% similarity]
inc/3rdparty/Readability.php [moved from inc/Readability.php with 96% similarity]
inc/3rdparty/Session.class.php [moved from inc/Session.class.php with 99% similarity]
inc/3rdparty/class.messages.php [moved from inc/class.messages.php with 96% similarity, mode: 0755]
inc/3rdparty/paginator.php [new file with mode: 0644]
inc/3rdparty/simple_html_dom.php [moved from inc/simple_html_dom.php with 100% similarity]
inc/MyTool.class.php [deleted file]
inc/config.php [deleted file]
inc/functions.php [deleted file]
inc/poche/Database.class.php [new file with mode: 0644]
inc/poche/Poche.class.php [new file with mode: 0644]
inc/poche/Tools.class.php [new file with mode: 0644]
inc/poche/Url.class.php [new file with mode: 0644]
inc/poche/User.class.php [new file with mode: 0644]
inc/poche/config.inc.php [new file with mode: 0644]
inc/poche/pochePictures.php [new file with mode: 0644]
inc/rain.tpl.class.php [deleted file]
inc/store/file.class.php [deleted file]
inc/store/sqlite.class.php [deleted file]
inc/store/store.class.php [deleted file]
index.php
install/mysql.sql [new file with mode: 0644]
install/poche.sqlite [moved from db/poche.sqlite with 53% similarity]
install/postgres.sql [new file with mode: 0644]
install/update_sqlite_from_0_to_1.php [new file with mode: 0644]
js/jquery-1.9.1.min.js [deleted file]
js/jquery.masonry.min.js [deleted file]
js/poche.js [deleted file]
locale/fr_FR.UTF8/LC_MESSAGES/fr_FR.UTF8.mo [new file with mode: 0644]
locale/fr_FR.UTF8/LC_MESSAGES/fr_FR.UTF8.po [new file with mode: 0644]
phpunit.xml.dist [new file with mode: 0644]
tpl/_bookmarklet.twig [new file with mode: 0644]
tpl/_footer.twig [new file with mode: 0644]
tpl/_head.twig [new file with mode: 0644]
tpl/_menu.twig [new file with mode: 0644]
tpl/_messages.twig [new file with mode: 0644]
tpl/_top.twig [new file with mode: 0644]
tpl/config.html [deleted file]
tpl/config.twig [new file with mode: 0644]
tpl/css/knacss.css [moved from css/knacss.css with 100% similarity]
tpl/css/messages.css [new file with mode: 0755]
tpl/css/style-light.css [new file with mode: 0644]
tpl/css/style.css [new file with mode: 0644]
tpl/entries.html [deleted file]
tpl/export.html [deleted file]
tpl/export.twig [new file with mode: 0644]
tpl/footer.html [deleted file]
tpl/head.html [deleted file]
tpl/home.html [deleted file]
tpl/home.twig [new file with mode: 0644]
tpl/img/apple-touch-icon-144x144-precomposed.png [moved from img/apple-touch-icon-144x144-precomposed.png with 100% similarity]
tpl/img/apple-touch-icon-72x72-precomposed.png [moved from img/apple-touch-icon-72x72-precomposed.png with 100% similarity]
tpl/img/apple-touch-icon.png [moved from img/apple-touch-icon.png with 100% similarity]
tpl/img/favicon.ico [moved from img/favicon.ico with 100% similarity]
tpl/img/light/checkmark-off.png [moved from img/light/checkmark-off.png with 100% similarity]
tpl/img/light/checkmark-on.png [moved from img/light/checkmark-on.png with 100% similarity]
tpl/img/light/down.png [moved from img/down.png with 100% similarity]
tpl/img/light/envelop.png [new file with mode: 0755]
tpl/img/light/left.png [new file with mode: 0755]
tpl/img/light/remove.png [moved from img/light/remove.png with 100% similarity]
tpl/img/light/star-off.png [moved from img/light/star-off.png with 100% similarity]
tpl/img/light/star-on.png [moved from img/light/star-on.png with 100% similarity]
tpl/img/light/top.png [moved from img/up.png with 100% similarity, mode: 0755]
tpl/img/light/twitter.png [new file with mode: 0755]
tpl/img/logo.png [new file with mode: 0644]
tpl/img/messages/close.png [moved from img/messages/close.png with 100% similarity, mode: 0755]
tpl/img/messages/cross.png [moved from img/messages/cross.png with 100% similarity, mode: 0755]
tpl/img/messages/help.png [moved from img/messages/help.png with 100% similarity, mode: 0755]
tpl/img/messages/tick.png [moved from img/messages/tick.png with 100% similarity, mode: 0755]
tpl/img/messages/warning.png [moved from img/messages/warning.png with 100% similarity, mode: 0755]
tpl/install.html [deleted file]
tpl/install.twig [new file with mode: 0644]
tpl/js.html [deleted file]
tpl/layout.twig [new file with mode: 0644]
tpl/login.html [deleted file]
tpl/login.twig [new file with mode: 0644]
tpl/messages.html [deleted file]
tpl/view.html [deleted file]
tpl/view.twig [new file with mode: 0644]

diff --git a/.gitignore b/.gitignore
new file mode 100644 (file)
index 0000000..5e992c2
--- /dev/null
@@ -0,0 +1,5 @@
+vendor
+composer.phar
+db/poche.sqlite
+output
+phpdoc*
\ No newline at end of file
diff --git a/.travis.yml b/.travis.yml
new file mode 100644 (file)
index 0000000..9d6ba13
--- /dev/null
@@ -0,0 +1,15 @@
+language: php
+
+php:
+  - 5.4
+
+branches:
+  only:
+    - dev
+
+before_script:
+  - composer install
+
+notifications:
+  email:
+    - nicolas.loeuillet@gmail.com
\ No newline at end of file
diff --git a/CREDITS b/CREDITS
index c917a012df2b6d8e543d3fdcc2a4d2c334c69da5..a6dedce4b2e49ea8286047433003e42d564022f3 100644 (file)
--- a/CREDITS
+++ b/CREDITS
@@ -1,16 +1,14 @@
 poche is based on :
-* ReadItYourself http://www.memiks.fr/readityourself/
-* PHP Readability http://www.keyvan.net/2010/08/php-readability/
+* PHP Readability https://bitbucket.org/fivefilters/php-readability
 * Encoding https://github.com/neitanod/forceutf8
 * logo by Brightmix http://www.iconfinder.com/icondetails/43256/128/jeans_monotone_pocket_icon
 * icons http://icomoon.io
 * PHP Simple HTML DOM Parser (for Pocket import) http://simplehtmldom.sourceforge.net/
 * Session https://github.com/tontof/kriss_feed/blob/master/src/class/Session.php
+* Twig http://twig.sensiolabs.org
+* Flash messages https://github.com/plasticbrain/PHP-Flash-Messages
+* Pagination https://github.com/daveismyname/pagination
 
 poche is developed by Nicolas Lœuillet under the Do What the Fuck You Want to Public License
 
-Contributors :
-Nicolas Lœuillet aka nico_somb
-Tom.C. aka tmos
-PeaceCopathe
-Gregoire_M
\ No newline at end of file
+Contributors : https://github.com/inthepoche/poche/graphs/contributors
\ No newline at end of file
diff --git a/INSTALL.md b/INSTALL.md
new file mode 100644 (file)
index 0000000..cf02728
--- /dev/null
@@ -0,0 +1,53 @@
+# Installing poche
+
+Get the [latest dev version](https://github.com/inthepoche/poche/archive/dev.zip) of poche on github. Unzip it and upload it on your server.
+
+your datas can be stored on sqlite, postgres or mysql databases.
+
+Edit /inc/poche/config.inc.php :
+
+```php
+define ('STORAGE','sqlite'); # postgres, mysql, sqlite
+define ('STORAGE_SERVER', 'localhost'); # leave blank for sqlite
+define ('STORAGE_DB', 'poche'); # only for postgres & mysql
+define ('STORAGE_SQLITE', './db/poche.sqlite');
+define ('STORAGE_USER', 'user'); # leave blank for sqlite
+define ('STORAGE_PASSWORD', 'pass'); # leave blank for sqlite
+```
+
+poche must have write access on assets, cache and db directories.
+
+[PHP cURL](http://www.php.net/manual/en/book.curl.php) & [tidy_parse_string](http://www.php.net/manual/en/tidy.parsestring.php) are recommended.
+
+## twig
+poche now uses twig for templating. You have to install twig. 
+
+Install composer in your project : 
+```bash
+curl -s http://getcomposer.org/installer | php
+```
+Install via composer : 
+```bash
+php composer.phar install
+```
+
+If you don't want to install twig by yourself, you can download [this file](http://static.inthepoche.com/files/poche-1.0-latest-with-twig.zip).
+
+## storage in sqlite 
+You have to install [sqlite for php](http://www.php.net/manual/en/book.sqlite.php) on your server.
+
+Copy /install/poche.sqlite in /db
+
+## storage in mysql
+Execute /install/mysql.sql file in your database.
+
+## storage in postgres 
+Execute /install/postgres.sql file in your database.
+
+## upgrading from poche <= 0.3
+With poche <= 0.3, all your datas were stored in a sqlite file. The structure of this file changed. 
+
+You have to execute http://yourpoche/install/update_sqlite_from_0_to_1.php before using this new version.
+
+## installing poche
+you can go on your poche http://yourpoche. You have to fill the fields and that's all !
\ No newline at end of file
index b44e7d368a5b1e734ba1f5c943257054495d7bd0..5bea7ca4b3ed7d1801e3b8629284463b62d5bb7b 100644 (file)
--- a/README.md
+++ b/README.md
@@ -1,5 +1,5 @@
 # poche
-Abandon Pocket, Instapaper and other Readability service : adopt poche. It is the same, but it is open source.
+Abandon Pocket, Instapaper and other Readability service : adopt poche. It is the same, but it is open source. Moreover, you can migrate from Pocket & Readability.
 
 ![poche](http://inthepoche.com/img/logo.png)
 
@@ -11,24 +11,6 @@ To get news from poche, [follow us on twitter](http://twitter.com/getpoche) or [
 
 [![flattr](http://api.flattr.com/button/flattr-badge-large.png)](http://flattr.com/thing/1265480/poche-a-read-it-later-open-source-system)
 
-## Usage
-You can easily add a "poched" page with the bookmarklet.
-
-poche save the entire content of a poched links : text and pictures are stored on your server.
-
-You can :
-* read a page in a comfortable reading view
-* archive a link
-* put a link in favorite
-* delete a link
-
-## Requirements & installation
-You have to install [sqlite for php](http://www.php.net/manual/en/book.sqlite.php) on your server.
-
-Get the [latest version](https://github.com/inthepoche/poche) of poche on github. Unzip it and upload it on your server. poche must have write access on assets, cache and db directories.
-
-That's all, **poche works** !
-
 ## Security
 You **have** to protect your db/poche.sqlite file. Modify the virtual host of your website to add this condition :
 ```apache
@@ -46,12 +28,14 @@ location ~ /(db) {
 }
 ```
 
-## Import from Pocket
+## Usage
+See the documentation on our website : [inthepoche.com](http://inthepoche.com).
 
-If you want to import your Pocket datas, [export them here](https://getpocket.com/export). Put the HTML file in your poche directory, execute import.php file locally by following instructions. Be careful, the script can take a very long time.
+## Travis
+[![Build Status](https://api.travis-ci.org/inthepoche/poche.png?branch=dev)](http://travis-ci.org/#!/inthepoche/poche)
 
 ## License
-Copyright © 2010-2013 Nicolas Lœuillet <nicolas@loeuillet.org>
+Copyright © 2010-2013 Nicolas Lœuillet <nicolas.loeuillet@gmail.com>
 This work is free. You can redistribute it and/or modify it under the
 terms of the Do What The Fuck You Want To Public License, Version 2,
-as published by Sam Hocevar. See the COPYING file for more details.
+as published by Sam Hocevar. See the COPYING file for more details.
\ No newline at end of file
diff --git a/TODO.md b/TODO.md
new file mode 100644 (file)
index 0000000..ac3c0e9
--- /dev/null
+++ b/TODO.md
@@ -0,0 +1,11 @@
+# TODO
+
+pouvoir annuler la suppression
+conventions codage ? phing ? vérifier error_log qui trainent
+phpDocumentor
+minifier css 
+revoir tous les css
+barre fixe d'admin sur la page d'un billet ? 
+revoir export (export vers pocket &cie ? )
+raccourcis clavier 
+date d'ajout d'un lien
\ No newline at end of file
diff --git a/composer.json b/composer.json
new file mode 100644 (file)
index 0000000..6c69e48
--- /dev/null
@@ -0,0 +1,7 @@
+{
+    "require": {
+        "twig/twig": "1.*",
+        "twig/extensions": "1.0.*",
+        "umpirsky/twig-gettext-extractor": "1.1.*"
+    }
+}
\ No newline at end of file
diff --git a/composer.lock b/composer.lock
new file mode 100644 (file)
index 0000000..9e17fca
--- /dev/null
@@ -0,0 +1,744 @@
+{
+    "_readme": [
+        "This file locks the dependencies of your project to a known state",
+        "Read more about it at http://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file"
+    ],
+    "hash": "1c8badb14d91f4f3ef1cfae23252a2c4",
+    "packages": [
+        {
+            "name": "symfony/event-dispatcher",
+            "version": "v2.3.2",
+            "target-dir": "Symfony/Component/EventDispatcher",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/EventDispatcher.git",
+                "reference": "v2.3.2"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/EventDispatcher/zipball/v2.3.2",
+                "reference": "v2.3.2",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.3.3"
+            },
+            "require-dev": {
+                "symfony/dependency-injection": "~2.0"
+            },
+            "suggest": {
+                "symfony/dependency-injection": "",
+                "symfony/http-kernel": ""
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "2.3-dev"
+                }
+            },
+            "autoload": {
+                "psr-0": {
+                    "Symfony\\Component\\EventDispatcher\\": ""
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Fabien Potencier",
+                    "email": "fabien@symfony.com"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "http://symfony.com/contributors"
+                }
+            ],
+            "description": "Symfony EventDispatcher Component",
+            "homepage": "http://symfony.com",
+            "time": "2013-05-13 14:36:40"
+        },
+        {
+            "name": "symfony/filesystem",
+            "version": "v2.3.2",
+            "target-dir": "Symfony/Component/Filesystem",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/Filesystem.git",
+                "reference": "v2.3.2"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/Filesystem/zipball/v2.3.2",
+                "reference": "v2.3.2",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.3.3"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "2.3-dev"
+                }
+            },
+            "autoload": {
+                "psr-0": {
+                    "Symfony\\Component\\Filesystem\\": ""
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Fabien Potencier",
+                    "email": "fabien@symfony.com"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "http://symfony.com/contributors"
+                }
+            ],
+            "description": "Symfony Filesystem Component",
+            "homepage": "http://symfony.com",
+            "time": "2013-06-04 15:02:05"
+        },
+        {
+            "name": "symfony/form",
+            "version": "v2.3.2",
+            "target-dir": "Symfony/Component/Form",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/Form.git",
+                "reference": "v2.3.2"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/Form/zipball/v2.3.2",
+                "reference": "v2.3.2",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.3.3",
+                "symfony/event-dispatcher": "~2.1",
+                "symfony/intl": "~2.3",
+                "symfony/options-resolver": "~2.1",
+                "symfony/property-access": "~2.2"
+            },
+            "require-dev": {
+                "symfony/http-foundation": "~2.2",
+                "symfony/validator": "~2.2"
+            },
+            "suggest": {
+                "symfony/http-foundation": "",
+                "symfony/validator": ""
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "2.3-dev"
+                }
+            },
+            "autoload": {
+                "psr-0": {
+                    "Symfony\\Component\\Form\\": ""
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Fabien Potencier",
+                    "email": "fabien@symfony.com"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "http://symfony.com/contributors"
+                }
+            ],
+            "description": "Symfony Form Component",
+            "homepage": "http://symfony.com",
+            "time": "2013-07-01 12:24:43"
+        },
+        {
+            "name": "symfony/icu",
+            "version": "v1.0.0",
+            "target-dir": "Symfony/Component/Icu",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/Icu.git",
+                "reference": "v1.0.0"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/Icu/zipball/v1.0.0",
+                "reference": "v1.0.0",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.3.3",
+                "symfony/intl": ">=2.3,<3.0"
+            },
+            "type": "library",
+            "autoload": {
+                "psr-0": {
+                    "Symfony\\Component\\Icu\\": ""
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Symfony Community",
+                    "homepage": "http://symfony.com/contributors"
+                },
+                {
+                    "name": "Bernhard Schussek",
+                    "email": "bschussek@gmail.com"
+                }
+            ],
+            "description": "Contains an excerpt of the ICU data and classes to load it.",
+            "homepage": "http://symfony.com",
+            "keywords": [
+                "icu",
+                "intl"
+            ],
+            "time": "2013-06-03 18:32:07"
+        },
+        {
+            "name": "symfony/intl",
+            "version": "v2.3.2",
+            "target-dir": "Symfony/Component/Intl",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/Intl.git",
+                "reference": "v2.3.2"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/Intl/zipball/v2.3.2",
+                "reference": "v2.3.2",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.3.3",
+                "symfony/icu": "~1.0-RC"
+            },
+            "require-dev": {
+                "symfony/filesystem": ">=2.1"
+            },
+            "suggest": {
+                "ext-intl": "to use the component with locales other than \"en\""
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "2.3-dev"
+                }
+            },
+            "autoload": {
+                "psr-0": {
+                    "Symfony\\Component\\Intl\\": ""
+                },
+                "classmap": [
+                    "Symfony/Component/Intl/Resources/stubs"
+                ],
+                "files": [
+                    "Symfony/Component/Intl/Resources/stubs/functions.php"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Symfony Community",
+                    "homepage": "http://symfony.com/contributors"
+                },
+                {
+                    "name": "Igor Wiedler",
+                    "email": "igor@wiedler.ch",
+                    "homepage": "http://wiedler.ch/igor/"
+                },
+                {
+                    "name": "Bernhard Schussek",
+                    "email": "bschussek@gmail.com"
+                },
+                {
+                    "name": "Eriksen Costa",
+                    "email": "eriksen.costa@infranology.com.br"
+                }
+            ],
+            "description": "A PHP replacement layer for the C intl extension that includes additional data from the ICU library.",
+            "homepage": "http://symfony.com",
+            "keywords": [
+                "i18n",
+                "icu",
+                "internationalization",
+                "intl",
+                "l10n",
+                "localization"
+            ],
+            "time": "2013-07-08 13:00:35"
+        },
+        {
+            "name": "symfony/options-resolver",
+            "version": "v2.3.2",
+            "target-dir": "Symfony/Component/OptionsResolver",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/OptionsResolver.git",
+                "reference": "v2.3.2"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/OptionsResolver/zipball/v2.3.2",
+                "reference": "v2.3.2",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.3.3"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "2.3-dev"
+                }
+            },
+            "autoload": {
+                "psr-0": {
+                    "Symfony\\Component\\OptionsResolver\\": ""
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Fabien Potencier",
+                    "email": "fabien@symfony.com"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "http://symfony.com/contributors"
+                }
+            ],
+            "description": "Symfony OptionsResolver Component",
+            "homepage": "http://symfony.com",
+            "keywords": [
+                "config",
+                "configuration",
+                "options"
+            ],
+            "time": "2013-04-11 06:50:46"
+        },
+        {
+            "name": "symfony/property-access",
+            "version": "v2.3.2",
+            "target-dir": "Symfony/Component/PropertyAccess",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/PropertyAccess.git",
+                "reference": "v2.3.2"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/PropertyAccess/zipball/v2.3.2",
+                "reference": "v2.3.2",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.3.3"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "2.3-dev"
+                }
+            },
+            "autoload": {
+                "psr-0": {
+                    "Symfony\\Component\\PropertyAccess\\": ""
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Fabien Potencier",
+                    "email": "fabien@symfony.com"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "http://symfony.com/contributors"
+                }
+            ],
+            "description": "Symfony PropertyAccess Component",
+            "homepage": "http://symfony.com",
+            "keywords": [
+                "access",
+                "array",
+                "extraction",
+                "index",
+                "injection",
+                "object",
+                "property",
+                "property path",
+                "reflection"
+            ],
+            "time": "2013-07-01 12:24:43"
+        },
+        {
+            "name": "symfony/routing",
+            "version": "v2.3.2",
+            "target-dir": "Symfony/Component/Routing",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/Routing.git",
+                "reference": "v2.3.2"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/Routing/zipball/v2.3.2",
+                "reference": "v2.3.2",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.3.3"
+            },
+            "require-dev": {
+                "doctrine/common": "~2.2",
+                "psr/log": "~1.0",
+                "symfony/config": "~2.2",
+                "symfony/yaml": "~2.0"
+            },
+            "suggest": {
+                "doctrine/common": "",
+                "symfony/config": "",
+                "symfony/yaml": ""
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "2.3-dev"
+                }
+            },
+            "autoload": {
+                "psr-0": {
+                    "Symfony\\Component\\Routing\\": ""
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Fabien Potencier",
+                    "email": "fabien@symfony.com"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "http://symfony.com/contributors"
+                }
+            ],
+            "description": "Symfony Routing Component",
+            "homepage": "http://symfony.com",
+            "time": "2013-06-23 08:16:02"
+        },
+        {
+            "name": "symfony/translation",
+            "version": "v2.3.2",
+            "target-dir": "Symfony/Component/Translation",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/Translation.git",
+                "reference": "v2.3.2"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/Translation/zipball/v2.3.2",
+                "reference": "v2.3.2",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.3.3"
+            },
+            "require-dev": {
+                "symfony/config": "~2.0",
+                "symfony/yaml": "~2.2"
+            },
+            "suggest": {
+                "symfony/config": "",
+                "symfony/yaml": ""
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "2.3-dev"
+                }
+            },
+            "autoload": {
+                "psr-0": {
+                    "Symfony\\Component\\Translation\\": ""
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Fabien Potencier",
+                    "email": "fabien@symfony.com"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "http://symfony.com/contributors"
+                }
+            ],
+            "description": "Symfony Translation Component",
+            "homepage": "http://symfony.com",
+            "time": "2013-05-13 14:36:40"
+        },
+        {
+            "name": "symfony/twig-bridge",
+            "version": "v2.3.2",
+            "target-dir": "Symfony/Bridge/Twig",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/TwigBridge.git",
+                "reference": "v2.3.2"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/TwigBridge/zipball/v2.3.2",
+                "reference": "v2.3.2",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.3.3",
+                "twig/twig": "~1.11"
+            },
+            "require-dev": {
+                "symfony/form": "2.2.*",
+                "symfony/http-kernel": "~2.2",
+                "symfony/routing": "~2.2",
+                "symfony/security": "~2.0",
+                "symfony/templating": "~2.1",
+                "symfony/translation": "~2.2",
+                "symfony/yaml": "~2.0"
+            },
+            "suggest": {
+                "symfony/form": "",
+                "symfony/http-kernel": "",
+                "symfony/routing": "",
+                "symfony/security": "",
+                "symfony/templating": "",
+                "symfony/translation": "",
+                "symfony/yaml": ""
+            },
+            "type": "symfony-bridge",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "2.3-dev"
+                }
+            },
+            "autoload": {
+                "psr-0": {
+                    "Symfony\\Bridge\\Twig\\": ""
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Fabien Potencier",
+                    "email": "fabien@symfony.com"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "http://symfony.com/contributors"
+                }
+            ],
+            "description": "Symfony Twig Bridge",
+            "homepage": "http://symfony.com",
+            "time": "2013-05-16 10:19:58"
+        },
+        {
+            "name": "twig/extensions",
+            "version": "v1.0.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/fabpot/Twig-extensions.git",
+                "reference": "v1.0.0"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/fabpot/Twig-extensions/zipball/v1.0.0",
+                "reference": "v1.0.0",
+                "shasum": ""
+            },
+            "require": {
+                "twig/twig": "1.*"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.0.x-dev"
+                }
+            },
+            "autoload": {
+                "psr-0": {
+                    "Twig_Extensions_": "lib/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Fabien Potencier",
+                    "email": "fabien@symfony.com"
+                }
+            ],
+            "description": "Common additional features for Twig that do not directly belong in core",
+            "homepage": "https://github.com/fabpot/Twig-extensions",
+            "keywords": [
+                "debug",
+                "i18n",
+                "text"
+            ],
+            "time": "2013-02-28 14:21:30"
+        },
+        {
+            "name": "twig/twig",
+            "version": "v1.13.2",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/fabpot/Twig.git",
+                "reference": "v1.13.2"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/fabpot/Twig/zipball/v1.13.2",
+                "reference": "v1.13.2",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.2.4"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.13-dev"
+                }
+            },
+            "autoload": {
+                "psr-0": {
+                    "Twig_": "lib/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "BSD-3-Clause"
+            ],
+            "authors": [
+                {
+                    "name": "Fabien Potencier",
+                    "email": "fabien@symfony.com"
+                },
+                {
+                    "name": "Armin Ronacher",
+                    "email": "armin.ronacher@active-4.com"
+                }
+            ],
+            "description": "Twig, the flexible, fast, and secure template language for PHP",
+            "homepage": "http://twig.sensiolabs.org",
+            "keywords": [
+                "templating"
+            ],
+            "time": "2013-08-03 15:35:31"
+        },
+        {
+            "name": "umpirsky/twig-gettext-extractor",
+            "version": "1.1.3",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/umpirsky/Twig-Gettext-Extractor.git",
+                "reference": "1.1.3"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/umpirsky/Twig-Gettext-Extractor/zipball/1.1.3",
+                "reference": "1.1.3",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.3.3",
+                "symfony/filesystem": ">=2.0,<3.0",
+                "symfony/form": ">=2.0,<3.0",
+                "symfony/routing": ">=2.0,<3.0",
+                "symfony/translation": ">=2.0,<3.0",
+                "symfony/twig-bridge": ">=2.0,<3.0",
+                "twig/extensions": "1.0.*",
+                "twig/twig": ">=1.2.0,<2.0-dev"
+            },
+            "require-dev": {
+                "symfony/config": "2.1.*"
+            },
+            "bin": [
+                "twig-gettext-extractor"
+            ],
+            "type": "application",
+            "autoload": {
+                "psr-0": {
+                    "Twig\\Gettext": "."
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Саша Стаменковић",
+                    "email": "umpirsky@gmail.com",
+                    "homepage": "http://umpirsky.com"
+                }
+            ],
+            "description": "The Twig Gettext Extractor is Poedit friendly tool which extracts translations from twig templates.",
+            "time": "2013-02-14 16:41:48"
+        }
+    ],
+    "packages-dev": [
+
+    ],
+    "aliases": [
+
+    ],
+    "minimum-stability": "stable",
+    "stability-flags": [
+
+    ],
+    "platform": [
+
+    ],
+    "platform-dev": [
+
+    ]
+}
diff --git a/css/style-dark.css b/css/style-dark.css
deleted file mode 100644 (file)
index 813c291..0000000
+++ /dev/null
@@ -1,90 +0,0 @@
-/*** GENERAL ***/
-body {
-    color: #fff;
-    background-color: #0d0d0d;
-}
-
-a, a:hover, a:visited {
-    color: #fff;
-}
-
-#main ul#links li a.current {
-    background-color: #000;
-    color: #fff;
-}
-
-#links a:hover, .backhome a:hover{
-    background-color: #fff;
-    color: #000;
-}
-
-input[type=submit].delete {
-    background : url('../img/dark/remove.png') no-repeat center center;
-    color : transparent;
-}
-
-#main .entrie {
-    color: #fff;
-    background-color: #000;
-    border: 1px solid #fff;
-}
-
-#main .entrie h2 a:hover {
-    color: #29B1E3;
-}
-
-a.fav span {
-    background: url('../img/dark/star-on.png') no-repeat;
-}
-
-a.fav span:hover {
-    background: url('../img/dark/star-off.png') no-repeat;
-}
-
-a.fav-off span {
-    background: url('../img/dark/star-off.png') no-repeat;
-}
-
-a.fav-off span:hover {
-    background: url('../img/dark/star-on.png') no-repeat;
-}
-
-a.archive span {
-    background: url('../img/dark/checkmark-on.png') no-repeat;
-}
-
-a.archive span:hover {
-    background: url('../img/dark/checkmark-off.png') no-repeat;
-}
-
-a.archive-off span {
-    background: url('../img/dark/checkmark-off.png') no-repeat;
-}
-
-a.archive-off span:hover {
-    background: url('../img/dark/checkmark-on.png') no-repeat;
-}
-
-/*** ***/
-/*** ARTICLE PAGE ***/
-
-body.article {
-       color: #fff;
-    background-color: #0d0d0d;
-}
-
-#article header {
-    border-bottom: 1px solid #222222;
-}
-
-#article article {
-    border-bottom: 1px solid #222222;
-}
-
-.vieworiginal a {
-    color: #888888;
-}
-
-.entrie {
-    background-color: #fff;
-}
diff --git a/css/style-light.css b/css/style-light.css
deleted file mode 100644 (file)
index cd2384c..0000000
+++ /dev/null
@@ -1,100 +0,0 @@
-/*** GENERAL ***/
-body {
-    color: #222222;
-    background-color: #F1F1F1;
-}
-
-a, a:hover, a:visited {
-    color: #000;
-}
-
-.bouton {
-       background-color: #000;
-    color: #fff;
-    border: none;
-}
-.bouton:hover {
-       background-color: #222222;
-    color: #F1F1F1;
-}
-
-#main ul#links li a.current {
-    background-color: #000;
-    color: #fff;
-}
-
-#links a:hover, .backhome a:hover{
-    background-color: #040707;
-    color: #F1F1F1;
-}
-
-input[type=submit].delete {
-    background : url('../img/light/remove.png') no-repeat center center;
-    color : transparent;
-}
-
-#main .entrie {
-    color: #2e2e2e;
-    background-color: #ffffff;
-    border: 1px solid #000;
-}
-
-#main .entrie h2 a:hover {
-    color: #F5BE00;
-}
-
-a.fav span {
-    background: url('../img/light/star-on.png') no-repeat;
-}
-
-a.fav span:hover {
-    background: url('../img/light/star-off.png') no-repeat;
-}
-
-a.fav-off span {
-    background: url('../img/light/star-off.png') no-repeat;
-}
-
-a.fav-off span:hover {
-    background: url('../img/light/star-on.png') no-repeat;
-}
-
-a.archive span {
-    background: url('../img/light/checkmark-on.png') no-repeat;
-}
-
-a.archive span:hover {
-    background: url('../img/light/checkmark-off.png') no-repeat;
-}
-
-a.archive-off span {
-    background: url('../img/light/checkmark-off.png') no-repeat;
-}
-
-a.archive-off span:hover {
-    background: url('../img/light/checkmark-on.png') no-repeat;
-}
-
-/*** ***/
-/*** ARTICLE PAGE ***/
-
-body.article {
-       color: #222222;
-    background-color: #F1F1F1;
-}
-
-#article header {
-    border-bottom: 1px solid #222222;
-}
-
-#article article {
-    border-bottom: 1px solid #222222;
-}
-
-.vieworiginal a {
-    color: #888888;
-}
-
-.entrie {
-    background-color: #fff;
-}
diff --git a/css/style.css b/css/style.css
deleted file mode 100644 (file)
index 9fadfa9..0000000
+++ /dev/null
@@ -1,215 +0,0 @@
-/*** GENERAL ***/
-body {
-    font: 20px/1.3em Palatino,Georgia,serif;
-    margin: 10px;
-}
-
-header {
-    text-align: center;
-}
-
-.bouton {
-       border-radius: 2px;
-}
-
-#main ul#links {
-    padding: 0;
-    list-style-type: none;
-    text-align: center;
-}
-
-#main ul#links li {
-    display: inline;
-}
-
-#main ul#links li a.current {
-    -webkit-border-radius: 2px;
-    border-radius: 2px;
-}
-
-#main ul#sort {
-    padding: 0;
-    list-style-type: none;
-    text-align: center;
-}
-
-#main ul#sort li {
-    display: inline;
-    font-size: 0.9em;
-}
-
-#main ul#sort img:hover {
-    cursor: pointer;
-}
-
-#main, #article {
-    margin: 0 auto;
-}
-
-#links a, .backhome a{
-    text-decoration: none;
-    padding: 5px 10px;
-}
-#links a:hover, .backhome a:hover{
-    -webkit-border-radius: 2px;
-    border-radius: 2px;
-}
-
-footer {
-    text-align: right;
-}
-
-/*** ***/
-/*** LINKS DISPLAY ***/
-
-#main a.tool {
-    text-decoration: none;
-    cursor: pointer;
-}
-
-input[type=submit].delete {
-    width : 16px;
-    height :16px;
-    border : none;
-    cursor: pointer;
-    font-size : 0;
-}
-
-#main #content {
-    margin-top: 20px;
-}
-
-#main .entrie {
-    padding: 15px;
-    min-height: 8em;
-    border: 1px solid;
-}
-
-#main .entrie h2 a {
-    text-decoration: none;
-}
-
-.tools {
-    text-align: right;
-}
-
-.tools ul {
-    padding: 0; margin: 0;
-    list-style-type: none;
-}
-
-.tools ul li {
-    line-height: 20px;
-}
-
-.tools a.tool {
-    cursor: pointer;
-}
-
-#article .tools {
-    position: relative;
-    display: inline;
-    top: 0px;
-    right: 0px;
-    width: 100%;
-    text-align: left;
-}
-
-#article .tools ul li{
-    display: inline;
-}
-
-#main .entrie .tools a.tool span, #article .tools a.tool span {
-    display: inline-block;
-    width: 16px;
-    height: 16px;
-}
-
-
-/*** ***/
-/*** ARTICLE PAGE ***/
-
-body.article {
-    font: 20px/1.3em Palatino,Georgia,serif;
-}
-
-#article header {
-    text-align: left;
-}
-
-#article header a {
-    text-decoration: none;
-}
-
-.vieworiginal a {
-    text-decoration: none;
-}
-
-.backhome {
-    display: inline;
-}
-
-/*** ***/
-
-#main
-{
-    max-width: 60em; /* 960 px */
-    margin: 0 auto;
-}
-#content
-{
-    width: 103.125%; /* 990px */
-    overflow: hidden;
-    margin-left: -1.562%; /* 15px */
-    margin-bottom: -1.875em; /* 30px */
-}
-
-.entrie
-{
-    width: 30.303%; /* 300px */
-    background-color: #fff;
-    float: left;
-    margin: 0 1.515% 1.875em; /* 15px 30px */
-}
-
-@media only screen and ( max-width: 40em ) /* 640px */
-{
-    .entrie
-    {
-        width: 46.876%; /* 305px */
-        margin-bottom: 0.938em; /* 15px */
-    }
-}
-
-@media only screen and ( max-width: 20em ) /* 320px */
-{
-    #content
-    {
-        width: 100%;
-        margin-left: 0;
-    }
-
-    .entrie
-    {
-        width: 100%;
-        margin-left: 0;
-        margin-right: 0;
-    }
-}
-
-/*** ***/
-/*** MESSAGES ***/
-
-.messages { width: 100%; -moz-border-radius: 4px; border-radius: 4px; display: block; padding: 10px 0; margin: 10px auto 10px; clear: both; }
-.messages a.closeMessage { margin: -14px -8px 0 0; display:none; width: 16px; height: 16px; float: right; background: url(../img/messages/close.png) no-repeat; }
-/*.messages:hover a.closeMessage { visibility:visible; }*/
-.messages p { margin: 3px 0 3px 10px !important; padding: 0 10px 0 23px !important; font-size: 14px; line-height: 16px; }
-.messages.error { border: 1px solid #C42608; color: #c00 !important; background: #FFF0EF; }
-.messages.error p { background: url(../img/messages/cross.png ) no-repeat 0px 50%; color:#c00 !important; }
-.messages.success {background: #E0FBCC; border: 1px solid #6DC70C; }
-.messages.success p { background: url(../img/messages/tick.png) no-repeat 0px 50%; color: #2B6301 !important; }
-.messages.warning { background: #FFFCD3; border: 1px solid #EBCD41; color: #000; }
-.messages.warning p { background: url(../img/messages/warning.png ) no-repeat 0px 50%; color: #5F4E01; }
-.messages.information, .messages.info { background: #DFEBFB; border: 1px solid #82AEE7; }
-.messages.information p, .messages.info p { background: url(../img/messages/help.png ) no-repeat 0px 50%; color: #064393; }
-.messages.information a { text-decoration: underline; }
diff --git a/img/dark/checkmark-off.png b/img/dark/checkmark-off.png
deleted file mode 100644 (file)
index efc3439..0000000
Binary files a/img/dark/checkmark-off.png and /dev/null differ
diff --git a/img/dark/checkmark-on.png b/img/dark/checkmark-on.png
deleted file mode 100644 (file)
index 24391c2..0000000
Binary files a/img/dark/checkmark-on.png and /dev/null differ
diff --git a/img/dark/down.png b/img/dark/down.png
deleted file mode 100644 (file)
index 41ea960..0000000
Binary files a/img/dark/down.png and /dev/null differ
diff --git a/img/dark/logo.png b/img/dark/logo.png
deleted file mode 100644 (file)
index 9fba064..0000000
Binary files a/img/dark/logo.png and /dev/null differ
diff --git a/img/dark/remove.png b/img/dark/remove.png
deleted file mode 100644 (file)
index 41786fd..0000000
Binary files a/img/dark/remove.png and /dev/null differ
diff --git a/img/dark/star-off.png b/img/dark/star-off.png
deleted file mode 100644 (file)
index 90651b5..0000000
Binary files a/img/dark/star-off.png and /dev/null differ
diff --git a/img/dark/star-on.png b/img/dark/star-on.png
deleted file mode 100644 (file)
index 7fc1447..0000000
Binary files a/img/dark/star-on.png and /dev/null differ
diff --git a/img/dark/up.png b/img/dark/up.png
deleted file mode 100644 (file)
index 1679e18..0000000
Binary files a/img/dark/up.png and /dev/null differ
diff --git a/img/logo.png b/img/logo.png
deleted file mode 100644 (file)
index f917857..0000000
Binary files a/img/logo.png and /dev/null differ
diff --git a/import.php b/import.php
deleted file mode 100644 (file)
index 72e3eac..0000000
+++ /dev/null
@@ -1,50 +0,0 @@
-<?php
-/**
- * poche, a read it later open source system
- *
- * @category   poche
- * @author     Nicolas Lœuillet <support@inthepoche.com>
- * @copyright  2013
- * @license    http://www.wtfpl.net/ see COPYING file
- */
-
-set_time_limit(0);
-
-include dirname(__FILE__).'/inc/config.php';
-include dirname(__FILE__).'/inc/simple_html_dom.php';
-
-if (!isset($_GET['start'])) {
-    echo 'Please execute the import script locally, it can take a very long time. <br /><a href="import.php?start">Bye bye Pocket, let\'s go !</a>';
-}
-else {
-    $html = new simple_html_dom();
-    $html->load_file('ril_export.html');
-
-    $read = 0;
-    $errors = array();
-    foreach($html->find('ul') as $ul)
-    {
-        foreach($ul->find('li') as $li)
-        {
-            $a = $li->find('a');
-            $url = $a[0]->href;
-
-
-            action_to_do('add', $url);
-            if ($read == '1') {
-                $last_id = $db->getHandle()->lastInsertId();
-                $sql_update     = "UPDATE entries SET is_read=~is_read WHERE id=?";
-                $params_update  = array($last_id);
-                $query_update   = $db->getHandle()->prepare($sql_update);
-                $query_update->execute($params_update);
-            }
-        }
-        # Pocket génère un fichier HTML avec deux <ul>
-        # Le premier concerne les éléments non lus
-        # Le second concerne les éléments archivés
-        $read = 1;
-    }
-
-    echo 'Import from Pocket completed. <a href="index.php">Welcome to #poche !</a>';
-    logm('import from pocket completed');
-}
\ No newline at end of file
similarity index 100%
rename from inc/Encoding.php
rename to inc/3rdparty/Encoding.php
similarity index 97%
rename from inc/JSLikeHTMLElement.php
rename to inc/3rdparty/JSLikeHTMLElement.php
index dfcc1be5e7f98227b2469f4fc422eb4c23b1de1b..238ba8a8d335f889d7a49e099471e767526b3aa1 100644 (file)
@@ -4,7 +4,7 @@
 *
 * This class extends PHP's DOMElement to allow
 * users to get and set the innerHTML property of
-* HTML elements in the same way it's done in
+* HTML elements in the same way it's done in 
 * JavaScript.
 *
 * Example usage:
 * $doc->registerNodeClass('DOMElement', 'JSLikeHTMLElement');
 * $doc->loadHTML('<div><p>Para 1</p><p>Para 2</p></div>');
 * $elem = $doc->getElementsByTagName('div')->item(0);
-*
+* 
 * // print innerHTML
 * echo $elem->innerHTML; // prints '<p>Para 1</p><p>Para 2</p>'
 * echo "\n\n";
-*
+* 
 * // set innerHTML
 * $elem->innerHTML = '<a href="http://fivefilters.org">FiveFilters.org</a>';
 * echo $elem->innerHTML; // prints '<a href="http://fivefilters.org">FiveFilters.org</a>'
 * echo "\n\n";
-*
+* 
 * // print document (with our changes)
 * echo $doc->saveXML();
 * @endcode
@@ -59,7 +59,7 @@ class JSLikeHTMLElement extends DOMElement
                                        $value = mb_convert_encoding($value, 'HTML-ENTITIES', 'UTF-8');
                                        // Using <htmlfragment> will generate a warning, but so will bad HTML
                                        // (and by this point, bad HTML is what we've got).
-                                       // We use it (and suppress the warning) because an HTML fragment will
+                                       // We use it (and suppress the warning) because an HTML fragment will 
                                        // be wrapped around <html><body> tags which we don't really want to keep.
                                        // Note: despite the warning, if loadHTML succeeds it will return true.
                                        $result = @$f->loadHTML('<htmlfragment>'.$value.'</htmlfragment>');
@@ -86,7 +86,7 @@ class JSLikeHTMLElement extends DOMElement
        * @code
        * $string = $div->innerHTML;
        * @endcode
-       */
+       */      
        public function __get($name)
        {
                if ($name == 'innerHTML') {
similarity index 96%
rename from inc/Readability.php
rename to inc/3rdparty/Readability.php
index d28d28f9f0b8f0e12ac064e775d282c666dd1c4d..e1e8738b8e149e8f3bd26c6830fb36630a098aa5 100644 (file)
@@ -1,5 +1,5 @@
 <?php
-/**
+/** 
 * Arc90's Readability ported to PHP for FiveFilters.org
 * Based on readability.js version 1.7.1 (without multi-page support)
 * Updated to allow HTML5 parsing with html5lib
 * License: Apache License, Version 2.0
 * Requires: PHP5
 * Date: 2012-09-19
-*
+* 
 * Differences between the PHP port and the original
 * ------------------------------------------------------
-* Arc90's Readability is designed to run in the browser. It works on the DOM
-* tree (the parsed HTML) after the page's CSS styles have been applied and
-* Javascript code executed. This PHP port does not run inside a browser.
-* We use PHP's ability to parse HTML to build our DOM tree, but we cannot
-* rely on CSS or Javascript support. As such, the results will not always
-* match Arc90's Readability. (For example, if a web page contains CSS style
-* rules or Javascript code which hide certain HTML elements from display,
-* Arc90's Readability will dismiss those from consideration but our PHP port,
+* Arc90's Readability is designed to run in the browser. It works on the DOM 
+* tree (the parsed HTML) after the page's CSS styles have been applied and 
+* Javascript code executed. This PHP port does not run inside a browser. 
+* We use PHP's ability to parse HTML to build our DOM tree, but we cannot 
+* rely on CSS or Javascript support. As such, the results will not always 
+* match Arc90's Readability. (For example, if a web page contains CSS style 
+* rules or Javascript code which hide certain HTML elements from display, 
+* Arc90's Readability will dismiss those from consideration but our PHP port, 
 * unable to understand CSS or Javascript, will not know any better.)
-*
-* Another significant difference is that the aim of Arc90's Readability is
-* to re-present the main content block of a given web page so users can
-* read it more easily in their browsers. Correct identification, clean up,
-* and separation of the content block is only a part of this process.
-* This PHP port is only concerned with this part, it does not include code
-* that relates to presentation in the browser - Arc90 already do
-* that extremely well, and for PDF output there's FiveFilters.org's
+* 
+* Another significant difference is that the aim of Arc90's Readability is 
+* to re-present the main content block of a given web page so users can 
+* read it more easily in their browsers. Correct identification, clean up, 
+* and separation of the content block is only a part of this process. 
+* This PHP port is only concerned with this part, it does not include code 
+* that relates to presentation in the browser - Arc90 already do 
+* that extremely well, and for PDF output there's FiveFilters.org's 
 * PDF Newspaper: http://fivefilters.org/pdf-newspaper/.
-*
-* Finally, this class contains methods that might be useful for developers
-* working on HTML document fragments. So without deviating too much from
-* the original code (which I don't want to do because it makes debugging
-* and updating more difficult), I've tried to make it a little more
-* developer friendly. You should be able to use the methods here on
-* existing DOMElement objects without passing an entire HTML document to
+* 
+* Finally, this class contains methods that might be useful for developers 
+* working on HTML document fragments. So without deviating too much from 
+* the original code (which I don't want to do because it makes debugging 
+* and updating more difficult), I've tried to make it a little more 
+* developer friendly. You should be able to use the methods here on 
+* existing DOMElement objects without passing an entire HTML document to 
 * be parsed.
 */
 
@@ -48,7 +48,7 @@
 require_once(dirname(__FILE__).'/JSLikeHTMLElement.php');
 
 // Alternative usage (for testing only!)
-// uncomment the lines below and call Readability.php in your browser
+// uncomment the lines below and call Readability.php in your browser 
 // passing it the URL of the page you'd like content from, e.g.:
 // Readability.php?url=http://medialens.org/alerts/09/090615_the_guardian_climate.php
 
@@ -75,11 +75,11 @@ class Readability
     public $url = null; // optional - URL where HTML was retrieved
     public $debug = false;
     public $lightClean = true; // preserves more content (experimental) added 2012-09-19
-    protected $body = null; //
+    protected $body = null; // 
     protected $bodyCache = null; // Cache the body HTML in case we need to re-use it later
     protected $flags = 7; // 1 | 2 | 4;   // Start with all flags set.
     protected $success = false; // indicates whether we were able to extract or not
-
+    
     /**
     * All of the regular expressions in use within readability.
     * Defined up here so we don't instantiate them repeatedly in loops.
@@ -97,19 +97,19 @@ class Readability
         'killBreaks' => '/(<br\s*\/?>(\s|&nbsp;?)*){1,}/',
         'video' => '!//(player\.|www\.)?(youtube|vimeo|viddler)\.com!i',
         'skipFootnoteLink' => '/^\s*(\[?[a-z0-9]{1,2}\]?|^|edit|citation needed)\s*$/i'
-    );
-
+    );  
+    
     /* constants */
     const FLAG_STRIP_UNLIKELYS = 1;
     const FLAG_WEIGHT_CLASSES = 2;
     const FLAG_CLEAN_CONDITIONALLY = 4;
-
+    
     /**
     * Create instance of Readability
     * @param string UTF-8 encoded string
     * @param string (optional) URL associated with HTML (used for footnotes)
     * @param string which parser to use for turning raw HTML into a DOMDocument (either 'libxml' or 'html5lib')
-    */
+    */  
     function __construct($html, $url=null, $parser='libxml')
     {
         $this->url = $url;
@@ -135,18 +135,18 @@ class Readability
     public function getTitle() {
         return $this->articleTitle;
     }
-
+    
     /**
     * Get article content element
     * @return DOMElement
     */
     public function getContent() {
         return $this->articleContent;
-    }
-
+    }   
+    
     /**
     * Runs readability.
-    *
+    * 
     * Workflow:
     *  1. Prep the document by removing script tags, css, etc.
     *  2. Build readability's DOM tree.
@@ -161,7 +161,7 @@ class Readability
         if (!isset($this->dom->documentElement)) return false;
         $this->removeScripts($this->dom);
         //die($this->getInnerHTML($this->dom->documentElement));
-
+        
         // Assume successful outcome
         $this->success = true;
 
@@ -176,7 +176,7 @@ class Readability
         }
 
         $this->prepDocument();
-
+        
         //die($this->dom->documentElement->parentNode->nodeType);
         //$this->setInnerHTML($this->dom->documentElement, $this->getInnerHTML($this->dom->documentElement));
         //die($this->getInnerHTML($this->dom->documentElement));
@@ -191,9 +191,9 @@ class Readability
             $this->success = false;
             $articleContent = $this->dom->createElement('div');
             $articleContent->setAttribute('id', 'readability-content');
-            $articleContent->innerHTML = '<p>Sorry, Readability was unable to parse this page for content.</p>';
+            $articleContent->innerHTML = '<p>Sorry, Readability was unable to parse this page for content.</p>';        
         }
-
+        
         $overlay->setAttribute('id', 'readOverlay');
         $innerDiv->setAttribute('id', 'readInner');
 
@@ -201,7 +201,7 @@ class Readability
         $innerDiv->appendChild($articleTitle);
         $innerDiv->appendChild($articleContent);
         $overlay->appendChild($innerDiv);
-
+        
         /* Clear the old HTML, insert the new content. */
         $this->body->innerHTML = '';
         $this->body->appendChild($overlay);
@@ -209,21 +209,21 @@ class Readability
         $this->body->removeAttribute('style');
 
         $this->postProcessContent($articleContent);
-
+        
         // Set title and content instance variables
         $this->articleTitle = $articleTitle;
         $this->articleContent = $articleContent;
-
+        
         return $this->success;
     }
-
+    
     /**
     * Debug
     */
     protected function dbg($msg) {
         if ($this->debug) echo '* ',$msg, "\n";
     }
-
+    
     /**
     * Run any post-process modifications to article content as necessary.
     *
@@ -231,11 +231,11 @@ class Readability
     * @return void
     */
     public function postProcessContent($articleContent) {
-        if ($this->convertLinksToFootnotes && !preg_match('/wikipedia\.org/', @$this->url)) {
+        if ($this->convertLinksToFootnotes && !preg_match('/wikipedia\.org/', @$this->url)) { 
             $this->addFootnotes($articleContent);
         }
     }
-
+    
     /**
     * Get the article title as an H1.
     *
@@ -248,11 +248,11 @@ class Readability
         try {
             $curTitle = $origTitle = $this->getInnerText($this->dom->getElementsByTagName('title')->item(0));
         } catch(Exception $e) {}
-
+        
         if (preg_match('/ [\|\-] /', $curTitle))
         {
             $curTitle = preg_replace('/(.*)[\|\-] .*/i', '$1', $origTitle);
-
+            
             if (count(explode(' ', $curTitle)) < 3) {
                 $curTitle = preg_replace('/[^\|\-]*[\|\-](.*)/i', '$1', $origTitle);
             }
@@ -279,17 +279,17 @@ class Readability
         if (count(explode(' ', $curTitle)) <= 4) {
             $curTitle = $origTitle;
         }
-
+        
         $articleTitle = $this->dom->createElement('h1');
         $articleTitle->innerHTML = $curTitle;
-
+        
         return $articleTitle;
     }
-
+    
     /**
     * Prepare the HTML document for readability to scrape it.
     * This includes things like stripping javascript, CSS, and handling terrible markup.
-    *
+    * 
     * @return void
     **/
     protected function prepDocument() {
@@ -328,13 +328,13 @@ class Readability
         $footnotesWrapper = $this->dom->createElement('div');
         $footnotesWrapper->setAttribute('id', 'readability-footnotes');
         $footnotesWrapper->innerHTML = '<h3>References</h3>';
-
+        
         $articleFootnotes = $this->dom->createElement('ol');
         $articleFootnotes->setAttribute('id', 'readability-footnotes-list');
         $footnotesWrapper->appendChild($articleFootnotes);
-
+        
         $articleLinks = $articleContent->getElementsByTagName('a');
-
+        
         $linkCount = 0;
         for ($i = 0; $i < $articleLinks->length; $i++)
         {
@@ -346,11 +346,11 @@ class Readability
             if (!$linkDomain && isset($this->url)) $linkDomain = @parse_url($this->url, PHP_URL_HOST);
             //linkDomain   = footnoteLink.host ? footnoteLink.host : document.location.host,
             $linkText     = $this->getInnerText($articleLink);
-
+            
             if ((strpos($articleLink->getAttribute('class'), 'readability-DoNotFootnote') !== false) || preg_match($this->regexps['skipFootnoteLink'], $linkText)) {
                 continue;
             }
-
+            
             $linkCount++;
 
             /** Add a superscript reference after the article link */
@@ -358,7 +358,7 @@ class Readability
             $refLink->innerHTML = '<small><sup>[' . $linkCount . ']</sup></small>';
             $refLink->setAttribute('class', 'readability-DoNotFootnote');
             $refLink->setAttribute('style', 'color: inherit;');
-
+            
             //TODO: does this work or should we use DOMNode.isSameNode()?
             if ($articleLink->parentNode->lastChild == $articleLink) {
                 $articleLink->parentNode->appendChild($refLink);
@@ -373,15 +373,15 @@ class Readability
 
             $footnoteLink->innerHTML = ($footnoteLink->getAttribute('title') != '' ? $footnoteLink->getAttribute('title') : $linkText);
             $footnoteLink->setAttribute('name', 'readabilityFootnoteLink-' . $linkCount);
-
+            
             $footnote->appendChild($footnoteLink);
             if ($linkDomain) $footnote->innerHTML = $footnote->innerHTML . '<small> (' . $linkDomain . ')</small>';
-
+            
             $articleFootnotes->appendChild($footnote);
         }
 
         if ($linkCount > 0) {
-            $articleContent->appendChild($footnotesWrapper);
+            $articleContent->appendChild($footnotesWrapper);           
         }
     }
 
@@ -404,7 +404,7 @@ class Readability
             //}
         }
     }
-
+    
     /**
     * Prepare the article node for display. Clean out any inline styles,
     * iframes, forms, strip extraneous <p> tags, etc.
@@ -429,7 +429,7 @@ class Readability
         * as a header and not a subheader, so remove it since we already have a header.
         ***/
         if (!$this->lightClean && ($articleContent->getElementsByTagName('h2')->length == 1)) {
-            $this->clean($articleContent, 'h2');
+            $this->clean($articleContent, 'h2'); 
         }
         $this->clean($articleContent, 'iframe');
 
@@ -448,7 +448,7 @@ class Readability
             $embedCount  = $articleParagraphs->item($i)->getElementsByTagName('embed')->length;
             $objectCount = $articleParagraphs->item($i)->getElementsByTagName('object')->length;
             $iframeCount = $articleParagraphs->item($i)->getElementsByTagName('iframe')->length;
-
+            
             if ($imgCount === 0 && $embedCount === 0 && $objectCount === 0 && $iframeCount === 0 && $this->getInnerText($articleParagraphs->item($i), false) == '')
             {
                 $articleParagraphs->item($i)->parentNode->removeChild($articleParagraphs->item($i));
@@ -457,13 +457,13 @@ class Readability
 
         try {
             $articleContent->innerHTML = preg_replace('/<br[^>]*>\s*<p/i', '<p', $articleContent->innerHTML);
-            //articleContent.innerHTML = articleContent.innerHTML.replace(/<br[^>]*>\s*<p/gi, '<p');
+            //articleContent.innerHTML = articleContent.innerHTML.replace(/<br[^>]*>\s*<p/gi, '<p');      
         }
         catch (Exception $e) {
             $this->dbg("Cleaning innerHTML of breaks failed. This is an IE strict-block-elements bug. Ignoring.: " . $e);
         }
     }
-
+    
     /**
     * Initialize a node with the readability object. Also checks the
     * className/id for special names to add to its score.
@@ -474,7 +474,7 @@ class Readability
     protected function initializeNode($node) {
         $readability = $this->dom->createAttribute('readability');
         $readability->value = 0; // this is our contentScore
-        $node->setAttributeNode($readability);
+        $node->setAttributeNode($readability);               
 
         switch (strtoupper($node->tagName)) { // unsure if strtoupper is needed, but using it just in case
             case 'DIV':
@@ -486,7 +486,7 @@ class Readability
             case 'BLOCKQUOTE':
                 $readability->value += 3;
                 break;
-
+                
             case 'ADDRESS':
             case 'OL':
             case 'UL':
@@ -510,7 +510,7 @@ class Readability
         }
         $readability->value += $this->getClassWeight($node);
     }
-
+    
     /***
     * grabArticle - Using a variety of metrics (content score, classname, element types), find the content that is
     *               most likely to be the stuff a user wants to read. Then return it wrapped up in a div.
@@ -548,7 +548,7 @@ class Readability
                     $node->parentNode->removeChild($node);
                     $nodeIndex--;
                     continue;
-                }
+                }               
             }
 
             if ($tagName == 'P' || $tagName == 'TD' || $tagName == 'PRE') {
@@ -589,7 +589,7 @@ class Readability
                 }
             }
         }
-
+        
         /**
         * Loop through all paragraphs, and assign a score to them based on how content-y they look.
         * Then add their score to their parent node.
@@ -613,7 +613,7 @@ class Readability
             }
 
             /* Initialize readability data for the parent. */
-            if (!$parentNode->hasAttribute('readability'))
+            if (!$parentNode->hasAttribute('readability')) 
             {
                 $this->initializeNode($parentNode);
                 $candidates[] = $parentNode;
@@ -633,15 +633,15 @@ class Readability
 
             /* Add points for any commas within this paragraph */
             $contentScore += count(explode(',', $innerText));
-
+            
             /* For every 100 characters in this paragraph, add another point. Up to 3 points. */
             $contentScore += min(floor(strlen($innerText) / 100), 3);
-
+            
             /* Add the score to the parent. The grandparent gets half. */
             $parentNode->getAttributeNode('readability')->value += $contentScore;
 
             if ($grandParentNode) {
-                $grandParentNode->getAttributeNode('readability')->value += $contentScore/2;
+                $grandParentNode->getAttributeNode('readability')->value += $contentScore/2;             
             }
         }
 
@@ -727,12 +727,12 @@ class Readability
             {
                 $append = true;
             }
-
+            
             if (strtoupper($siblingNode->nodeName) == 'P') {
                 $linkDensity = $this->getLinkDensity($siblingNode);
                 $nodeContent = $this->getInnerText($siblingNode);
                 $nodeLength  = strlen($nodeContent);
-
+                
                 if ($nodeLength > 80 && $linkDensity < 0.25)
                 {
                     $append = true;
@@ -751,7 +751,7 @@ class Readability
                 $sibNodeName = strtoupper($siblingNode->nodeName);
                 if ($sibNodeName != 'DIV' && $sibNodeName != 'P') {
                     /* We have a node that isn't a common block level element, like a form or td tag. Turn it into a div so it doesn't get filtered out later by accident. */
-
+                    
                     $this->dbg('Altering siblingNode of ' . $sibNodeName . ' to div.');
                     $nodeToAppend = $this->dom->createElement('div');
                     try {
@@ -770,7 +770,7 @@ class Readability
                     $s--;
                     $sl--;
                 }
-
+                
                 /* To ensure a node does not interfere with readability styles, remove its classnames */
                 $nodeToAppend->removeAttribute('class');
 
@@ -796,14 +796,14 @@ class Readability
             // in the meantime, we check and create an empty element if it's not there.
             if (!isset($this->body->childNodes)) $this->body = $this->dom->createElement('body');
             $this->body->innerHTML = $this->bodyCache;
-
+            
             if ($this->flagIsActive(self::FLAG_STRIP_UNLIKELYS)) {
                 $this->removeFlag(self::FLAG_STRIP_UNLIKELYS);
                 return $this->grabArticle($this->body);
             }
             else if ($this->flagIsActive(self::FLAG_WEIGHT_CLASSES)) {
                 $this->removeFlag(self::FLAG_WEIGHT_CLASSES);
-                return $this->grabArticle($this->body);
+                return $this->grabArticle($this->body);              
             }
             else if ($this->flagIsActive(self::FLAG_CLEAN_CONDITIONALLY)) {
                 $this->removeFlag(self::FLAG_CLEAN_CONDITIONALLY);
@@ -815,7 +815,7 @@ class Readability
         }
         return $articleContent;
     }
-
+    
     /**
     * Remove script tags from document
     *
@@ -829,7 +829,7 @@ class Readability
             $scripts->item($i)->parentNode->removeChild($scripts->item($i));
         }
     }
-
+    
     /**
     * Get the inner text of a node.
     * This also strips out any excess whitespace to be found.
@@ -878,11 +878,11 @@ class Readability
             $elem->removeAttribute('style');
         }
     }
-
+    
     /**
     * Get the density of links as a percentage of the content
     * This is the amount of text that is inside a link divided by the total text in the node.
-    *
+    * 
     * @param DOMElement $e
     * @return number (float)
     */
@@ -900,9 +900,9 @@ class Readability
             return 0;
         }
     }
-
+    
     /**
-    * Get an elements class/id weight. Uses regular expressions to tell if this
+    * Get an elements class/id weight. Uses regular expressions to tell if this 
     * element looks good or bad.
     *
     * @param DOMElement $e
@@ -964,7 +964,7 @@ class Readability
     public function clean($e, $tag) {
         $targetList = $e->getElementsByTagName($tag);
         $isEmbed = ($tag == 'iframe' || $tag == 'object' || $tag == 'embed');
-
+        
         for ($y=$targetList->length-1; $y >= 0; $y--) {
             /* Allow youtube and vimeo videos through as people usually want to see those. */
             if ($isEmbed) {
@@ -972,7 +972,7 @@ class Readability
                 for ($i=0, $il=$targetList->item($y)->attributes->length; $i < $il; $i++) {
                     $attributeValues .= $targetList->item($y)->attributes->item($i)->value . '|'; // DOMAttr? (TODO: test)
                 }
-
+                
                 /* First, check the elements attributes to see if any of them contain youtube or vimeo */
                 if (preg_match($this->regexps['video'], $attributeValues)) {
                     continue;
@@ -986,10 +986,10 @@ class Readability
             $targetList->item($y)->parentNode->removeChild($targetList->item($y));
         }
     }
-
+    
     /**
     * Clean an element of all tags of type "tag" if they look fishy.
-    * "Fishy" is an algorithm based on content length, classnames,
+    * "Fishy" is an algorithm based on content length, classnames, 
     * link density, number of images & embeds, etc.
     *
     * @param DOMElement $e
@@ -1013,7 +1013,7 @@ class Readability
         for ($i=$curTagsLength-1; $i >= 0; $i--) {
             $weight = $this->getClassWeight($tagsList->item($i));
             $contentScore = ($tagsList->item($i)->hasAttribute('readability')) ? (int)$tagsList->item($i)->getAttribute('readability') : 0;
-
+            
             $this->dbg('Cleaning Conditionally ' . $tagsList->item($i)->tagName . ' (' . $tagsList->item($i)->getAttribute('class') . ':' . $tagsList->item($i)->getAttribute('id') . ')' . (($tagsList->item($i)->hasAttribute('readability')) ? (' with score ' . $tagsList->item($i)->getAttribute('readability')) : ''));
 
             if ($weight + $contentScore < 0) {
@@ -1034,13 +1034,13 @@ class Readability
                 $embeds = $tagsList->item($i)->getElementsByTagName('embed');
                 for ($ei=0, $il=$embeds->length; $ei < $il; $ei++) {
                     if (preg_match($this->regexps['video'], $embeds->item($ei)->getAttribute('src'))) {
-                        $embedCount++;
+                        $embedCount++; 
                     }
                 }
                 $embeds = $tagsList->item($i)->getElementsByTagName('iframe');
                 for ($ei=0, $il=$embeds->length; $ei < $il; $ei++) {
                     if (preg_match($this->regexps['video'], $embeds->item($ei)->getAttribute('src'))) {
-                        $embedCount++;
+                        $embedCount++; 
                     }
                 }
 
@@ -1058,7 +1058,7 @@ class Readability
                         $toRemove = true;
                     } else if ( $input > floor($p/3) ) {
                         $this->dbg(' too many <input> elements');
-                        $toRemove = true;
+                        $toRemove = true; 
                     } else if ($contentLength < 25 && ($embedCount === 0 && ($img === 0 || $img > 2))) {
                         $this->dbg(' content length less than 25 chars, 0 embeds and either 0 images or more than 2 images');
                         $toRemove = true;
@@ -1082,7 +1082,7 @@ class Readability
                         $toRemove = true;
                     } else if ( $input > floor($p/3) ) {
                         $this->dbg(' too many <input> elements');
-                        $toRemove = true;
+                        $toRemove = true; 
                     } else if ($contentLength < 25 && ($img === 0 || $img > 2) ) {
                         $this->dbg(' content length less than 25 chars and 0 images, or more than 2 images');
                         $toRemove = true;
@@ -1126,11 +1126,11 @@ class Readability
     public function flagIsActive($flag) {
         return ($this->flags & $flag) > 0;
     }
-
+    
     public function addFlag($flag) {
         $this->flags = $this->flags | $flag;
     }
-
+    
     public function removeFlag($flag) {
         $this->flags = $this->flags & ~$flag;
     }
similarity index 99%
rename from inc/Session.class.php
rename to inc/3rdparty/Session.class.php
index eff924ccdb06dddf8c68bcfab1a15fc967bfc000..3162f5074d82700f5970e0611fed2161613ac0f2 100644 (file)
@@ -93,7 +93,7 @@ class Session
     // Force logout
     public static function logout()
     {
-        unset($_SESSION['uid'],$_SESSION['info'],$_SESSION['expires_on'],$_SESSION['tokens'], $_SESSION['login'], $_SESSION['pass']);
+        unset($_SESSION['uid'],$_SESSION['info'],$_SESSION['expires_on'],$_SESSION['tokens'], $_SESSION['login'], $_SESSION['pass'], $_SESSION['poche_user']);
     }
 
     // Make sure user is logged in.
old mode 100644 (file)
new mode 100755 (executable)
similarity index 96%
rename from inc/class.messages.php
rename to inc/3rdparty/class.messages.php
index 6d515bf..e60bd3a
-<?php
-//--------------------------------------------------------------------------------------------------
-// Session-Based Flash Messages v1.0
-// Copyright 2012 Mike Everhart (http://mikeeverhart.net)
-//
-//   Licensed under the Apache License, Version 2.0 (the "License");
-//   you may not use this file except in compliance with the License.
-//   You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-//   Unless required by applicable law or agreed to in writing, software
-//   distributed under the License is distributed on an "AS IS" BASIS,
-//   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-//   See the License for the specific language governing permissions and
-//      limitations under the License.
-//      
-//------------------------------------------------------------------------------
-// Description:
-//------------------------------------------------------------------------------
-//
-//     Stores messages in Session data to be easily retrieved later on.
-//     This class includes four different types of messages:
-//  - Success
-//  - Error
-//  - Warning
-//  - Information
-// 
-//  See README for basic usage instructions, or see samples/index.php for more advanced samples
-//
-//--------------------------------------------------------------------------------------------------
-// Changelog
-//--------------------------------------------------------------------------------------------------
-// 
-//     2011-05-15 - v1.0 - Initial Version
-//
-//--------------------------------------------------------------------------------------------------
-
-class Messages {
-       
-       //-----------------------------------------------------------------------------------------------
-       // Class Variables
-       //-----------------------------------------------------------------------------------------------       
-       var $msgId;
-       var $msgTypes = array( 'help', 'info', 'warning', 'success', 'error' );
-       var $msgClass = 'messages';
-       var $msgWrapper = "<div class='%s %s'><a href='#' class='closeMessage'></a>\n%s</div>\n";
-       var $msgBefore = '<p>';
-       var $msgAfter = "</p>\n";
-
-       
-       /**
-        * Constructor
-        * @author Mike Everhart
-        */
-       public function __construct() {
-       
-               // Generate a unique ID for this user and session
-               $this->msgId = md5(uniqid());
-               
-               // Create the session array if it doesnt already exist
-               if( !array_key_exists('flash_messages', $_SESSION) ) $_SESSION['flash_messages'] = array();
-               
-       }
-       
-       /**
-        * Add a message to the queue
-        * 
-        * @author Mike Everhart
-        * 
-        * @param  string   $type               The type of message to add
-        * @param  string   $message            The message
-        * @param  string   $redirect_to        (optional) If set, the user will be redirected to this URL
-        * @return  bool 
-        * 
-        */
-       public function add($type, $message, $redirect_to=null) {
-               
-               if( !isset($_SESSION['flash_messages']) ) return false;
-               
-               if( !isset($type) || !isset($message[0]) ) return false;
-
-               // Replace any shorthand codes with their full version
-               if( strlen(trim($type)) == 1 ) {
-                       $type = str_replace( array('h', 'i', 'w', 'e', 's'), array('help', 'info', 'warning', 'error', 'success'), $type );
-               
-               // Backwards compatibility...
-               } elseif( $type == 'information' ) {
-                       $type = 'info'; 
-               }
-               
-               // Make sure it's a valid message type
-               if( !in_array($type, $this->msgTypes) ) die('"' . strip_tags($type) . '" is not a valid message type!' );
-               
-               // If the session array doesn't exist, create it
-               if( !array_key_exists( $type, $_SESSION['flash_messages'] ) ) $_SESSION['flash_messages'][$type] = array();
-               
-               $_SESSION['flash_messages'][$type][] = $message;
-
-               if( !is_null($redirect_to) ) {
-                       header("Location: $redirect_to");
-                       exit();
-               }
-               
-               return true;
-               
-       }
-       
-       //-----------------------------------------------------------------------------------------------
-       // display()
-       // print queued messages to the screen
-       //-----------------------------------------------------------------------------------------------
-       /**
-        * Display the queued messages
-        * 
-        * @author Mike Everhart
-        * 
-        * @param  string   $type     Which messages to display
-        * @param  bool         $print    True  = print the messages on the screen
-        * @return mixed              
-        * 
-        */
-       public function display($type='all', $print=true) {
-               $messages = '';
-               $data = '';
-               
-               if( !isset($_SESSION['flash_messages']) ) return false;
-               
-               if( $type == 'g' || $type == 'growl' ) {
-                       $this->displayGrowlMessages();
-                       return true;
-               }
-               
-               // Print a certain type of message?
-               if( in_array($type, $this->msgTypes) ) {
-                       foreach( $_SESSION['flash_messages'][$type] as $msg ) {
-                               $messages .= $this->msgBefore . $msg . $this->msgAfter;
-                       }
-
-                       $data .= sprintf($this->msgWrapper, $this->msgClass, $type, $messages);
-                       
-                       // Clear the viewed messages
-                       $this->clear($type);
-               
-               // Print ALL queued messages
-               } elseif( $type == 'all' ) {
-                       foreach( $_SESSION['flash_messages'] as $type => $msgArray ) {
-                               $messages = '';
-                               foreach( $msgArray as $msg ) {
-                                       $messages .= $this->msgBefore . $msg . $this->msgAfter; 
-                               }
-                               $data .= sprintf($this->msgWrapper, $this->msgClass, $type, $messages);
-                       }
-                       
-                       // Clear ALL of the messages
-                       $this->clear();
-               
-               // Invalid Message Type?
-               } else { 
-                       return false;
-               }
-               
-               // Print everything to the screen or return the data
-               if( $print ) { 
-                       echo $data; 
-               } else { 
-                       return $data; 
-               }
-       }
-       
-       
-       /**
-        * Check to  see if there are any queued error messages
-        * 
-        * @author Mike Everhart
-        * 
-        * @return bool  true  = There ARE error messages
-        *               false = There are NOT any error messages
-        * 
-        */
-       public function hasErrors() { 
-               return empty($_SESSION['flash_messages']['error']) ? false : true;      
-       }
-       
-       /**
-        * Check to see if there are any ($type) messages queued
-        * 
-        * @author Mike Everhart
-        * 
-        * @param  string   $type     The type of messages to check for
-        * @return bool                   
-        * 
-        */
-       public function hasMessages($type=null) {
-               if( !is_null($type) ) {
-                       if( !empty($_SESSION['flash_messages'][$type]) ) return $_SESSION['flash_messages'][$type];     
-               } else {
-                       foreach( $this->msgTypes as $type ) {
-                               if( !empty($_SESSION['flash_messages']) ) return true;  
-                       }
-               }
-               return false;
-       }
-       
-       /**
-        * Clear messages from the session data
-        * 
-        * @author Mike Everhart
-        * 
-        * @param  string   $type     The type of messages to clear
-        * @return bool 
-        * 
-        */
-       public function clear($type='all') { 
-               if( $type == 'all' ) {
-                       unset($_SESSION['flash_messages']); 
-               } else {
-                       unset($_SESSION['flash_messages'][$type]);
-               }
-               return true;
-       }
-       
-       public function __toString() { return $this->hasMessages();     }
-
-       public function __destruct() {
-               //$this->clear();
-       }
-
-
-} // end class
+<?php\r
+//--------------------------------------------------------------------------------------------------\r
+// Session-Based Flash Messages v1.0\r
+// Copyright 2012 Mike Everhart (http://mikeeverhart.net)\r
+//\r
+//   Licensed under the Apache License, Version 2.0 (the "License");\r
+//   you may not use this file except in compliance with the License.\r
+//   You may obtain a copy of the License at\r
+//\r
+//     http://www.apache.org/licenses/LICENSE-2.0\r
+//\r
+//   Unless required by applicable law or agreed to in writing, software\r
+//   distributed under the License is distributed on an "AS IS" BASIS,\r
+//   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+//   See the License for the specific language governing permissions and\r
+//      limitations under the License.\r
+//      \r
+//------------------------------------------------------------------------------\r
+// Description:\r
+//------------------------------------------------------------------------------\r
+//\r
+//     Stores messages in Session data to be easily retrieved later on.\r
+//     This class includes four different types of messages:\r
+//  - Success\r
+//  - Error\r
+//  - Warning\r
+//  - Information\r
+// \r
+//  See README for basic usage instructions, or see samples/index.php for more advanced samples\r
+//\r
+//--------------------------------------------------------------------------------------------------\r
+// Changelog\r
+//--------------------------------------------------------------------------------------------------\r
+// \r
+//     2011-05-15 - v1.0 - Initial Version\r
+//\r
+//--------------------------------------------------------------------------------------------------\r
+\r
+class Messages {\r
+       \r
+       //-----------------------------------------------------------------------------------------------\r
+       // Class Variables\r
+       //-----------------------------------------------------------------------------------------------       \r
+       var $msgId;\r
+       var $msgTypes = array( 'help', 'info', 'warning', 'success', 'error' );\r
+       var $msgClass = 'messages';\r
+       var $msgWrapper = "<div class='%s %s'><a href='#' class='closeMessage'>X</a>\n%s</div>\n";\r
+       var $msgBefore = '<p>';\r
+       var $msgAfter = "</p>\n";\r
+\r
+       \r
+       /**\r
+        * Constructor\r
+        * @author Mike Everhart\r
+        */\r
+       public function __construct() {\r
+       \r
+               // Generate a unique ID for this user and session\r
+               $this->msgId = md5(uniqid());\r
+               \r
+               // Create the session array if it doesnt already exist\r
+               if( !array_key_exists('flash_messages', $_SESSION) ) $_SESSION['flash_messages'] = array();\r
+               \r
+       }\r
+       \r
+       /**\r
+        * Add a message to the queue\r
+        * \r
+        * @author Mike Everhart\r
+        * \r
+        * @param  string   $type               The type of message to add\r
+        * @param  string   $message            The message\r
+        * @param  string   $redirect_to        (optional) If set, the user will be redirected to this URL\r
+        * @return  bool \r
+        * \r
+        */\r
+       public function add($type, $message, $redirect_to=null) {\r
+               \r
+               if( !isset($_SESSION['flash_messages']) ) return false;\r
+               \r
+               if( !isset($type) || !isset($message[0]) ) return false;\r
+\r
+               // Replace any shorthand codes with their full version\r
+               if( strlen(trim($type)) == 1 ) {\r
+                       $type = str_replace( array('h', 'i', 'w', 'e', 's'), array('help', 'info', 'warning', 'error', 'success'), $type );\r
+               \r
+               // Backwards compatibility...\r
+               } elseif( $type == 'information' ) {\r
+                       $type = 'info'; \r
+               }\r
+               \r
+               // Make sure it's a valid message type\r
+               if( !in_array($type, $this->msgTypes) ) die('"' . strip_tags($type) . '" is not a valid message type!' );\r
+               \r
+               // If the session array doesn't exist, create it\r
+               if( !array_key_exists( $type, $_SESSION['flash_messages'] ) ) $_SESSION['flash_messages'][$type] = array();\r
+               \r
+               $_SESSION['flash_messages'][$type][] = $message;\r
+\r
+               if( !is_null($redirect_to) ) {\r
+                       header("Location: $redirect_to");\r
+                       exit();\r
+               }\r
+               \r
+               return true;\r
+               \r
+       }\r
+       \r
+       //-----------------------------------------------------------------------------------------------\r
+       // display()\r
+       // print queued messages to the screen\r
+       //-----------------------------------------------------------------------------------------------\r
+       /**\r
+        * Display the queued messages\r
+        * \r
+        * @author Mike Everhart\r
+        * \r
+        * @param  string   $type     Which messages to display\r
+        * @param  bool         $print    True  = print the messages on the screen\r
+        * @return mixed              \r
+        * \r
+        */\r
+       public function display($type='all', $print=true) {\r
+               $messages = '';\r
+               $data = '';\r
+               \r
+               if( !isset($_SESSION['flash_messages']) ) return false;\r
+               \r
+               if( $type == 'g' || $type == 'growl' ) {\r
+                       $this->displayGrowlMessages();\r
+                       return true;\r
+               }\r
+               \r
+               // Print a certain type of message?\r
+               if( in_array($type, $this->msgTypes) ) {\r
+                       foreach( $_SESSION['flash_messages'][$type] as $msg ) {\r
+                               $messages .= $this->msgBefore . $msg . $this->msgAfter;\r
+                       }\r
+\r
+                       $data .= sprintf($this->msgWrapper, $this->msgClass, $type, $messages);\r
+                       \r
+                       // Clear the viewed messages\r
+                       $this->clear($type);\r
+               \r
+               // Print ALL queued messages\r
+               } elseif( $type == 'all' ) {\r
+                       foreach( $_SESSION['flash_messages'] as $type => $msgArray ) {\r
+                               $messages = '';\r
+                               foreach( $msgArray as $msg ) {\r
+                                       $messages .= $this->msgBefore . $msg . $this->msgAfter; \r
+                               }\r
+                               $data .= sprintf($this->msgWrapper, $this->msgClass, $type, $messages);\r
+                       }\r
+                       \r
+                       // Clear ALL of the messages\r
+                       $this->clear();\r
+               \r
+               // Invalid Message Type?\r
+               } else { \r
+                       return false;\r
+               }\r
+               \r
+               // Print everything to the screen or return the data\r
+               if( $print ) { \r
+                       echo $data; \r
+               } else { \r
+                       return $data; \r
+               }\r
+       }\r
+       \r
+       \r
+       /**\r
+        * Check to  see if there are any queued error messages\r
+        * \r
+        * @author Mike Everhart\r
+        * \r
+        * @return bool  true  = There ARE error messages\r
+        *               false = There are NOT any error messages\r
+        * \r
+        */\r
+       public function hasErrors() { \r
+               return empty($_SESSION['flash_messages']['error']) ? false : true;      \r
+       }\r
+       \r
+       /**\r
+        * Check to see if there are any ($type) messages queued\r
+        * \r
+        * @author Mike Everhart\r
+        * \r
+        * @param  string   $type     The type of messages to check for\r
+        * @return bool                   \r
+        * \r
+        */\r
+       public function hasMessages($type=null) {\r
+               if( !is_null($type) ) {\r
+                       if( !empty($_SESSION['flash_messages'][$type]) ) return $_SESSION['flash_messages'][$type];     \r
+               } else {\r
+                       foreach( $this->msgTypes as $type ) {\r
+                               if( !empty($_SESSION['flash_messages']) ) return true;  \r
+                       }\r
+               }\r
+               return false;\r
+       }\r
+       \r
+       /**\r
+        * Clear messages from the session data\r
+        * \r
+        * @author Mike Everhart\r
+        * \r
+        * @param  string   $type     The type of messages to clear\r
+        * @return bool \r
+        * \r
+        */\r
+       public function clear($type='all') { \r
+               if( $type == 'all' ) {\r
+                       unset($_SESSION['flash_messages']); \r
+               } else {\r
+                       unset($_SESSION['flash_messages'][$type]);\r
+               }\r
+               return true;\r
+       }\r
+       \r
+       public function __toString() { return $this->hasMessages();     }\r
+\r
+       public function __destruct() {\r
+               //$this->clear();\r
+       }\r
+\r
+\r
+} // end class\r
 ?>
\ No newline at end of file
diff --git a/inc/3rdparty/paginator.php b/inc/3rdparty/paginator.php
new file mode 100644 (file)
index 0000000..306756c
--- /dev/null
@@ -0,0 +1,202 @@
+<?php
+/*
+ * PHP Pagination Class
+ *
+ * @author David Carr - dave@daveismyname.com - http://www.daveismyname.com
+ * @version 1.0
+ * @date October 20, 2013
+ */
+class Paginator{
+
+        /**
+     * set the number of items per page.
+     *
+     * @var numeric
+    */
+    private $_perPage;
+
+    /**
+     * set get parameter for fetching the page number
+     *
+     * @var string
+    */
+    private $_instance;
+
+    /**
+     * sets the page number.
+     *
+     * @var numeric
+    */
+    private $_page;
+
+    /**
+     * set the limit for the data source
+     *
+     * @var string
+    */
+    private $_limit;
+
+    /**
+     * set the total number of records/items.
+     *
+     * @var numeric
+    */
+    private $_totalRows = 0;
+
+
+
+    /**
+     *  __construct
+     *  
+     *  pass values when class is istantiated 
+     *  
+     * @param numeric  $_perPage  sets the number of iteems per page
+     * @param numeric  $_instance sets the instance for the GET parameter
+     */
+    public function __construct($perPage,$instance){
+        $this->_instance = $instance;        
+        $this->_perPage = $perPage;
+        $this->set_instance();        
+    }
+
+    /**
+     * get_start
+     *
+     * creates the starting point for limiting the dataset
+     * @return numeric
+    */
+    private function get_start(){
+        return ($this->_page * $this->_perPage) - $this->_perPage;
+    }
+
+    /**
+     * set_instance
+     * 
+     * sets the instance parameter, if numeric value is 0 then set to 1
+     *
+     * @var numeric
+    */
+    private function set_instance(){
+        $this->_page = (int) (!isset($_GET[$this->_instance]) ? 1 : $_GET[$this->_instance]); 
+        $this->_page = ($this->_page == 0 ? 1 : $this->_page);
+    }
+
+    /**
+     * set_total
+     *
+     * collect a numberic value and assigns it to the totalRows
+     *
+     * @var numeric
+    */
+    public function set_total($_totalRows){
+        $this->_totalRows = $_totalRows;
+    }
+
+    /**
+     * get_limit
+     *
+     * returns the limit for the data source, calling the get_start method and passing in the number of items perp page
+     * 
+     * @return string
+    */
+    public function get_limit(){
+        if (STORAGE == 'postgres') {
+            return "LIMIT ".$this->_perPage." OFFSET ".$this->get_start();
+        } else {
+            return "LIMIT ".$this->get_start().",".$this->_perPage;
+        }
+    }
+
+        /**
+         * page_links
+         *
+         * create the html links for navigating through the dataset
+         * 
+         * @var sting $path optionally set the path for the link
+         * @var sting $ext optionally pass in extra parameters to the GET
+         * @return string returns the html menu
+        */
+    public function page_links($path='?',$ext=null)
+    {
+        $adjacents = "2";
+        $prev = $this->_page - 1;
+        $next = $this->_page + 1;
+        $lastpage = ceil($this->_totalRows/$this->_perPage);
+        $lpm1 = $lastpage - 1;
+
+        $pagination = "";
+        if($lastpage > 1)
+        {   
+            $pagination .= "<div class='pagination'>";
+        if ($this->_page > 1)
+            $pagination.= "<a href='".$path."$this->_instance=$prev"."$ext'>« previous</a>";
+        else
+            $pagination.= "<span class='disabled'>« previous</span>";   
+
+        if ($lastpage < 7 + ($adjacents * 2))
+        {   
+        for ($counter = 1; $counter <= $lastpage; $counter++)
+        {
+        if ($counter == $this->_page)
+            $pagination.= "<span class='current'>$counter</span>";
+        else
+            $pagination.= "<a href='".$path."$this->_instance=$counter"."$ext'>$counter</a>";                   
+        }
+        }
+        elseif($lastpage > 5 + ($adjacents * 2))
+        {
+        if($this->_page < 1 + ($adjacents * 2))       
+        {
+        for ($counter = 1; $counter < 4 + ($adjacents * 2); $counter++)
+        {
+        if ($counter == $this->_page)
+            $pagination.= "<span class='current'>$counter</span>";
+        else
+            $pagination.= "<a href='".$path."$this->_instance=$counter"."$ext'>$counter</a>";                   
+        }
+            $pagination.= "...";
+            $pagination.= "<a href='".$path."$this->_instance=$lpm1"."$ext'>$lpm1</a>";
+            $pagination.= "<a href='".$path."$this->_instance=$lastpage"."$ext'>$lastpage</a>";       
+        }
+        elseif($lastpage - ($adjacents * 2) > $this->_page && $this->_page > ($adjacents * 2))
+        {
+            $pagination.= "<a href='".$path."$this->_instance=1"."$ext'>1</a>";
+            $pagination.= "<a href='".$path."$this->_instance=2"."$ext'>2</a>";
+            $pagination.= "...";
+        for ($counter = $this->_page - $adjacents; $counter <= $this->_page + $adjacents; $counter++)
+        {
+        if ($counter == $this->_page)
+            $pagination.= "<span class='current'>$counter</span>";
+        else
+            $pagination.= "<a href='".$path."$this->_instance=$counter"."$ext'>$counter</a>";                   
+        }
+            $pagination.= "..";
+            $pagination.= "<a href='".$path."$this->_instance=$lpm1"."$ext'>$lpm1</a>";
+            $pagination.= "<a href='".$path."$this->_instance=$lastpage"."$ext'>$lastpage</a>";       
+        }
+        else
+        {
+            $pagination.= "<a href='".$path."$this->_instance=1"."$ext'>1</a>";
+            $pagination.= "<a href='".$path."$this->_instance=2"."$ext'>2</a>";
+            $pagination.= "..";
+        for ($counter = $lastpage - (2 + ($adjacents * 2)); $counter <= $lastpage; $counter++)
+        {
+        if ($counter == $this->_page)
+            $pagination.= "<span class='current'>$counter</span>";
+        else
+            $pagination.= "<a href='".$path."$this->_instance=$counter"."$ext'>$counter</a>";                   
+        }
+        }
+        }
+
+        if ($this->_page < $counter - 1)
+            $pagination.= "<a href='".$path."$this->_instance=$next"."$ext'>next »</a>";
+        else
+            $pagination.= "<span class='disabled'>next »</span>";
+            $pagination.= "</div>\n";       
+        }
+
+
+    return $pagination;
+    }
+}
diff --git a/inc/MyTool.class.php b/inc/MyTool.class.php
deleted file mode 100644 (file)
index 1f5051a..0000000
+++ /dev/null
@@ -1,265 +0,0 @@
-<?php
-/**
- * poche, a read it later open source system
- *
- * @category   poche
- * @author     Nicolas Lœuillet <support@inthepoche.com>
- * @copyright  2013
- * @license    http://www.wtfpl.net/ see COPYING file
- */
-class MyTool
-{
-    public static function initPhp()
-    {
-        define('START_TIME', microtime(true));
-
-        if (phpversion() < 5) {
-            die("Argh you don't have PHP 5 !");
-        }
-
-        error_reporting(E_ALL);
-
-        function stripslashesDeep($value) {
-            return is_array($value)
-                ? array_map('stripslashesDeep', $value)
-                : stripslashes($value);
-        }
-
-        if (get_magic_quotes_gpc()) {
-            $_POST = array_map('stripslashesDeep', $_POST);
-            $_GET = array_map('stripslashesDeep', $_GET);
-            $_COOKIE = array_map('stripslashesDeep', $_COOKIE);
-        }
-
-        ob_start();
-        register_shutdown_function('ob_end_flush');
-    }
-
-    public static function isUrl($url)
-    {
-        // http://neo22s.com/check-if-url-exists-and-is-online-php/
-        $pattern='|^http(s)?://[a-z0-9-]+(.[a-z0-9-]+)*(:[0-9]+)?(/.*)?$|i';
-
-        return preg_match($pattern, $url);
-    }
-
-    public static function isEmail($email)
-    {
-        $pattern = "/^[A-Z0-9._%-]+@[A-Z0-9.-]+\.[A-Z]{2, 4}$/i";
-
-        return (preg_match($pattern, $email));
-    }
-
-    public static function formatBBCode($text)
-    {
-        $replace = array(
-            '/\[m\](.+?)\[\/m\]/is'
-            => '/* moderate */',
-            '/\[b\](.+?)\[\/b\]/is'
-            => '<strong>$1</strong>',
-            '/\[i\](.+?)\[\/i\]/is'
-            => '<em>$1</em>',
-            '/\[s\](.+?)\[\/s\]/is'
-            => '<del>$1</del>',
-            '/\[u\](.+?)\[\/u\]/is'
-            => '<span style="text-decoration: underline;">$1</span>',
-            '/\[url\](.+?)\[\/url]/is'
-            => '<a href="$1">$1</a>',
-            '/\[url=(\w+:\/\/[^\]]+)\](.+?)\[\/url]/is'
-            => '<a href="$1">$2</a>',
-            '/\[quote\](.+?)\[\/quote\]/is'
-            => '<blockquote>$1</blockquote>',
-            '/\[code\](.+?)\[\/code\]/is'
-            => '<code>$1</code>',
-            '/\[([^[]+)\|([^[]+)\]/is'
-            => '<a href="$2">$1</a>'
-            );
-        $text = preg_replace(
-            array_keys($replace),
-            array_values($replace),
-            $text
-        );
-
-        return $text;
-    }
-
-    public static function formatText($text)
-    {
-        $text = preg_replace_callback(
-            '/<code_html>(.*?)<\/code_html>/is',
-            create_function(
-                '$matches',
-                'return htmlspecialchars($matches[1]);'
-            ),
-            $text
-        );
-        $text = preg_replace_callback(
-            '/<code_php>(.*?)<\/code_php>/is',
-            create_function(
-                '$matches',
-                'return highlight_string("<?php $matches[1] ?>", true);'
-            ),
-            $text
-        );
-        $text = preg_replace('/<br \/>/is', '', $text);
-
-        $text = preg_replace(
-            '#(^|\s)([a-z]+://([^\s\w/]?[\w/])*)(\s|$)#im',
-            '\\1<a href="\\2">\\2</a>\\4',
-            $text
-        );
-        $text = preg_replace(
-            '#(^|\s)wp:?([a-z]{2}|):([\w]+)#im',
-            '\\1<a href="http://\\2.wikipedia.org/wiki/\\3">\\3</a>',
-            $text
-        );
-        $text = str_replace(
-            'http://.wikipedia.org/wiki/',
-            'http://www.wikipedia.org/wiki/',
-            $text
-        );
-        $text = str_replace('\wp:', 'wp:', $text);
-        $text = str_replace('\http:', 'http:', $text);
-        $text = MyTool::formatBBCode($text);
-        $text = nl2br($text);
-
-        return $text;
-    }
-
-    public static function getUrl()
-    {
-        $https = (!empty($_SERVER['HTTPS'])
-                  && (strtolower($_SERVER['HTTPS']) == 'on'))
-            || (isset($_SERVER["SERVER_PORT"])
-                && $_SERVER["SERVER_PORT"] == '443'); // HTTPS detection.
-        $serverport = (!isset($_SERVER["SERVER_PORT"])
-                       || $_SERVER["SERVER_PORT"] == '80'
-                       || ($https && $_SERVER["SERVER_PORT"] == '443')
-                       ? ''
-                       : ':' . $_SERVER["SERVER_PORT"]);
-
-        $scriptname = str_replace('/index.php', '/', $_SERVER["SCRIPT_NAME"]);
-
-        if (!isset($_SERVER["SERVER_NAME"])) {
-            return $scriptname;
-        }
-
-        return 'http' . ($https ? 's' : '') . '://'
-            . $_SERVER["SERVER_NAME"] . $serverport . $scriptname;
-    }
-
-    public static function rrmdir($dir)
-    {
-        if (is_dir($dir) && ($d = @opendir($dir))) {
-            while (($file = @readdir($d)) !== false) {
-                if ( $file == '.' || $file == '..' ) {
-                    continue;
-                } else {
-                    unlink($dir . '/' . $file);
-                }
-            }
-        }
-    }
-
-    public static function humanBytes($bytes)
-    {
-        $siPrefix = array( 'bytes', 'KB', 'MB', 'GB', 'TB', 'EB', 'ZB', 'YB' );
-        $base = 1024;
-        $class = min((int) log($bytes, $base), count($siPrefix) - 1);
-        $val = sprintf('%1.2f', $bytes / pow($base, $class));
-
-        return $val . ' ' . $siPrefix[$class];
-    }
-
-    public static function returnBytes($val)
-    {
-        $val = trim($val);
-        $last = strtolower($val[strlen($val)-1]);
-        switch($last)
-        {
-        case 'g': $val *= 1024;
-        case 'm': $val *= 1024;
-        case 'k': $val *= 1024;
-        }
-
-        return $val;
-    }
-
-    public static function getMaxFileSize()
-    {
-        $sizePostMax = MyTool::returnBytes(ini_get('post_max_size'));
-        $sizeUploadMax = MyTool::returnBytes(ini_get('upload_max_filesize'));
-
-        // Return the smaller of two:
-        return min($sizePostMax, $sizeUploadMax);
-    }
-
-    public static function smallHash($text)
-    {
-        $t = rtrim(base64_encode(hash('crc32', $text, true)), '=');
-        // Get rid of characters which need encoding in URLs.
-        $t = str_replace('+', '-', $t);
-        $t = str_replace('/', '_', $t);
-        $t = str_replace('=', '@', $t);
-
-        return $t;
-    }
-
-    public static function renderJson($data)
-    {
-        header('Cache-Control: no-cache, must-revalidate');
-        header('Expires: Sat, 26 Jul 1997 05:00:00 GMT');
-        header('Content-type: application/json; charset=UTF-8');
-
-        echo json_encode($data);
-        exit();
-    }
-
-    public static function grabToLocal($url, $file, $force = false)
-    {
-        if ((!file_exists($file) || $force) && in_array('curl', get_loaded_extensions())){
-            $ch = curl_init ($url);
-            curl_setopt($ch, CURLOPT_HEADER, false);
-            curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
-            curl_setopt($ch, CURLOPT_BINARYTRANSFER, true);
-            $raw = curl_exec($ch);
-            if (curl_getinfo($ch, CURLINFO_HTTP_CODE) == 200) {
-                $fp = fopen($file, 'x');
-                fwrite($fp, $raw);
-                fclose($fp);
-            }
-            curl_close ($ch);
-        }
-    }
-
-    public static function redirect($rurl = '')
-    {
-        if ($rurl === '') {
-            // if (!empty($_SERVER['HTTP_REFERER']) && strcmp(parse_url($_SERVER['HTTP_REFERER'],PHP_URL_HOST),$_SERVER['SERVER_NAME'])==0)
-            $rurl = (empty($_SERVER['HTTP_REFERER'])?'?':$_SERVER['HTTP_REFERER']);
-            if (isset($_POST['returnurl'])) {
-                $rurl = $_POST['returnurl'];
-            }
-        }
-
-        // prevent loop
-        if (empty($rurl) || parse_url($rurl, PHP_URL_QUERY) === $_SERVER['QUERY_STRING']) {
-            $rurl = MyTool::getUrl();
-        }
-
-        if (substr($rurl, 0, 1) !== '?') {
-            $ref = MyTool::getUrl();
-            if (substr($rurl, 0, strlen($ref)) !== $ref) {
-                $rurl = $ref;
-            }
-        }
-        header('Location: '.$rurl);
-        exit();
-    }
-
-    public static function silence_errors($num, $str)
-    {
-// No-op
-    }
-}
\ No newline at end of file
diff --git a/inc/config.php b/inc/config.php
deleted file mode 100644 (file)
index bd9287f..0000000
+++ /dev/null
@@ -1,66 +0,0 @@
-<?php
-/**
- * poche, a read it later open source system
- *
- * @category   poche
- * @author     Nicolas Lœuillet <nicolas@loeuillet.org>
- * @copyright  2013
- * @license    http://www.wtfpl.net/ see COPYING file
- */
-
-define ('POCHE_VERSION', '0.2.1');
-
-if (!is_dir('db/')) {
-    @mkdir('db/',0705);
-}
-
-define ('MODE_DEMO', FALSE);
-define ('ABS_PATH', 'assets/');
-define ('CONVERT_LINKS_FOOTNOTES', TRUE);
-define ('REVERT_FORCED_PARAGRAPH_ELEMENTS',FALSE);
-define ('DOWNLOAD_PICTURES', TRUE);
-define ('SALT', '464v54gLLw928uz4zUBqkRJeiPY68zCX');
-$storage_type = 'sqlite'; # sqlite or file
-
-include 'functions.php';
-require_once 'Readability.php';
-require_once 'Encoding.php';
-require_once 'rain.tpl.class.php';
-require_once 'MyTool.class.php';
-require_once 'Session.class.php';
-require_once 'store/store.class.php';
-require_once 'store/sqlite.class.php';
-require_once 'store/file.class.php';
-require_once 'class.messages.php';
-
-Session::init();
-
-$store     = new $storage_type();
-# initialisation de RainTPL
-raintpl::$tpl_dir   = './tpl/';
-raintpl::$cache_dir = './cache/';
-raintpl::$base_url  = get_poche_url();
-raintpl::configure('path_replace', false);
-raintpl::configure('debug', false);
-$tpl = new raintpl();
-
-if(!$store->isInstalled())
-{
-    logm('poche still not installed');
-    $tpl->draw('install');
-    if (isset($_GET['install'])) {
-        if (($_POST['password'] == $_POST['password_repeat']) 
-            && $_POST['password'] != "" && $_POST['login'] != "") {
-            $store->install($_POST['login'], encode_string($_POST['password'] . $_POST['login']));
-            Session::logout();
-            MyTool::redirect();
-        }
-    }
-    exit();
-}
-
-$_SESSION['login'] = (isset ($_SESSION['login'])) ? $_SESSION['login'] : $store->getLogin();
-$_SESSION['pass']  = (isset ($_SESSION['pass'])) ? $_SESSION['pass'] : $store->getPassword();
-
-$msg = new Messages();
-$tpl->assign('msg', $msg);
\ No newline at end of file
diff --git a/inc/functions.php b/inc/functions.php
deleted file mode 100644 (file)
index 73e591c..0000000
+++ /dev/null
@@ -1,398 +0,0 @@
-<?php
-/**
- * poche, a read it later open source system
- *
- * @category   poche
- * @author     Nicolas Lœuillet <support@inthepoche.com>
- * @copyright  2013
- * @license    http://www.wtfpl.net/ see COPYING file
- */
-
-/**
- * Permet de générer l'URL de poche pour le bookmarklet
- */
-function get_poche_url()
-{
-    $protocol = "http";
-    if(isset($_SERVER['HTTPS'])) {
-        if($_SERVER['HTTPS'] != "off" && $_SERVER['HTTPS'] != "") {
-            $protocol = "https";
-        }
-    }
-
-    return $protocol . "://" . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'];
-}
-
-function encode_string($string) 
-{
-    return sha1($string . SALT);
-}
-
-// function define to retrieve url content
-function get_external_file($url)
-{
-    $timeout = 15;
-    // spoofing FireFox 18.0
-    $useragent="Mozilla/5.0 (Windows NT 5.1; rv:18.0) Gecko/20100101 Firefox/18.0";
-
-    if  (in_array  ('curl', get_loaded_extensions())) {
-        // Fetch feed from URL
-        $curl = curl_init();
-        curl_setopt($curl, CURLOPT_URL, $url);
-        curl_setopt($curl, CURLOPT_TIMEOUT, $timeout);
-        curl_setopt($curl, CURLOPT_FOLLOWLOCATION, true);
-        curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
-        curl_setopt($curl, CURLOPT_HEADER, false);
-
-        // FOR SSL do not verified certificate
-        curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, FALSE);
-        curl_setopt($curl, CURLOPT_AUTOREFERER, TRUE );
-
-        // FeedBurner requires a proper USER-AGENT...
-        curl_setopt($curl, CURL_HTTP_VERSION_1_1, true);
-        curl_setopt($curl, CURLOPT_ENCODING, "gzip, deflate");
-        curl_setopt($curl, CURLOPT_USERAGENT, $useragent);
-
-        $data = curl_exec($curl);
-
-        $httpcode = curl_getinfo($curl, CURLINFO_HTTP_CODE);
-
-        $httpcodeOK = isset($httpcode) and ($httpcode == 200 or $httpcode == 301);
-
-        curl_close($curl);
-    } else {
-
-        // create http context and add timeout and user-agent
-        $context = stream_context_create(array(
-                                'http'=>array('timeout' => $timeout,
-                                    'header'=> "User-Agent: ".$useragent,      /*spoot Mozilla Firefox*/
-                                    'follow_location' => true),
-                                // FOR SSL do not verified certificate
-                                'ssl' => array('verify_peer' => false,
-                                        'allow_self_signed' => true)
-                                )
-                            );
-
-        // only download page lesser than 4MB
-        $data = @file_get_contents($url, false, $context, -1, 4000000); // We download at most 4 MB from source.
-
-        if(isset($http_response_header) and isset($http_response_header[0])) {
-            $httpcodeOK = isset($http_response_header) and isset($http_response_header[0]) and ((strpos($http_response_header[0], '200 OK') !== FALSE) or (strpos($http_response_header[0], '301 Moved Permanently') !== FALSE));
-        }
-    }
-
-    // if response is not empty and response is OK
-    if (isset($data) and isset($httpcodeOK) and $httpcodeOK ) {
-
-        // take charset of page and get it
-        preg_match('#<meta .*charset=.*>#Usi', $data, $meta);
-
-        // if meta tag is found
-        if (!empty($meta[0])) {
-            // retrieve encoding in $enc
-            preg_match('#charset="?(.*)"#si', $meta[0], $enc);
-
-            // if charset is found set it otherwise, set it to utf-8
-            $html_charset = (!empty($enc[1])) ? strtolower($enc[1]) : 'utf-8';
-
-        } else {
-            $html_charset = 'utf-8';
-            $enc[1] = '';
-        }
-
-        // replace charset of url to charset of page
-        $data = str_replace('charset='.$enc[1], 'charset='.$html_charset, $data);
-
-        return $data;
-    }
-    else {
-        return FALSE;
-    }
-}
-
-/**
- * Préparation de l'URL avec récupération du contenu avant insertion en base
- */
-function prepare_url($url)
-{
-    $parametres = array();
-    $url        = html_entity_decode(trim($url));
-
-    // We remove the annoying parameters added by FeedBurner and GoogleFeedProxy (?utm_source=...)
-    // from shaarli, by sebsauvage
-    $i=strpos($url,'&utm_source='); if ($i!==false) $url=substr($url,0,$i);
-    $i=strpos($url,'?utm_source='); if ($i!==false) $url=substr($url,0,$i);
-    $i=strpos($url,'#xtor=RSS-'); if ($i!==false) $url=substr($url,0,$i);
-
-    $title = $url;
-    $html = Encoding::toUTF8(get_external_file($url,15));
-    // If get_external_file if not able to retrieve HTTPS content try the same URL with HTTP protocol
-    if (!preg_match('!^https?://!i', $url) && (!isset($html) || strlen($html) <= 0)) {
-        $url = 'http://' . $url;
-        $html = Encoding::toUTF8(get_external_file($url,15));
-    }
-
-    if (function_exists('tidy_parse_string')) {
-        $tidy = tidy_parse_string($html, array(), 'UTF8');
-        $tidy->cleanRepair();
-        $html = $tidy->value;
-    }
-
-    if (isset($html) and strlen($html) > 0)
-    {
-        $r = new Readability($html, $url);
-
-        $r->convertLinksToFootnotes = CONVERT_LINKS_FOOTNOTES;
-        $r->revertForcedParagraphElements = REVERT_FORCED_PARAGRAPH_ELEMENTS;
-
-        if($r->init())
-        {
-            $content = $r->articleContent->innerHTML;
-            $parametres['title'] = $r->articleTitle->innerHTML;
-            $parametres['content'] = $content;
-            return $parametres;
-        }
-    }
-
-    return FALSE;
-}
-
-/**
- * On modifie les URLS des images dans le corps de l'article
- */
-function filtre_picture($content, $url, $id)
-{
-    $matches = array();
-    preg_match_all('#<\s*(img)[^>]+src="([^"]*)"[^>]*>#Si', $content, $matches, PREG_SET_ORDER);
-    foreach($matches as $i => $link)
-    {
-        $link[1] = trim($link[1]);
-        if (!preg_match('#^(([a-z]+://)|(\#))#', $link[1]) )
-        {
-            $absolute_path = get_absolute_link($link[2],$url);
-            $filename = basename(parse_url($absolute_path, PHP_URL_PATH));
-            $directory = create_assets_directory($id);
-            $fullpath = $directory . '/' . $filename;
-            download_pictures($absolute_path, $fullpath);
-            $content = str_replace($matches[$i][2], $fullpath, $content);
-        }
-
-    }
-
-    return $content;
-}
-
-/**
- * Retourne le lien absolu
- */
-function get_absolute_link($relative_link, $url)
-{
-    /* return if already absolute URL */
-    if (parse_url($relative_link, PHP_URL_SCHEME) != '') return $relative_link;
-
-    /* queries and anchors */
-    if ($relative_link[0]=='#' || $relative_link[0]=='?') return $url . $relative_link;
-
-    /* parse base URL and convert to local variables:
-       $scheme, $host, $path */
-    extract(parse_url($url));
-
-    /* remove non-directory element from path */
-    $path = preg_replace('#/[^/]*$#', '', $path);
-
-    /* destroy path if relative url points to root */
-    if ($relative_link[0] == '/') $path = '';
-
-    /* dirty absolute URL */
-    $abs = $host . $path . '/' . $relative_link;
-
-    /* replace '//' or '/./' or '/foo/../' with '/' */
-    $re = array('#(/\.?/)#', '#/(?!\.\.)[^/]+/\.\./#');
-    for($n=1; $n>0; $abs=preg_replace($re, '/', $abs, -1, $n)) {}
-
-    /* absolute URL is ready! */
-    return $scheme.'://'.$abs;
-}
-
-/**
- * Téléchargement des images
- */
-
-function download_pictures($absolute_path, $fullpath)
-{
-    $rawdata = get_external_file($absolute_path);
-
-    if(file_exists($fullpath)) {
-        unlink($fullpath);
-    }
-    $fp = fopen($fullpath, 'x');
-    fwrite($fp, $rawdata);
-    fclose($fp);
-}
-
-/**
- * Crée un répertoire de médias pour l'article
- */
-function create_assets_directory($id)
-{
-    $assets_path = ABS_PATH;
-    if(!is_dir($assets_path)) {
-        mkdir($assets_path, 0705);
-    }
-
-    $article_directory = $assets_path . $id;
-    if(!is_dir($article_directory)) {
-        mkdir($article_directory, 0705);
-    }
-
-    return $article_directory;
-}
-
-/**
- * Suppression du répertoire d'images
- */
-function remove_directory($directory)
-{
-    if(is_dir($directory)) {
-        $files = array_diff(scandir($directory), array('.','..'));
-        foreach ($files as $file) {
-            (is_dir("$directory/$file")) ? remove_directory("$directory/$file") : unlink("$directory/$file");
-        }
-        return rmdir($directory);
-    }
-}
-
-function display_view($view, $id = 0, $full_head = 'yes')
-{
-    global $tpl, $store, $msg;
-
-    switch ($view)
-    {
-        case 'export':
-            $entries = $store->retrieveAll();
-            $tpl->assign('export', myTool::renderJson($entries));
-            $tpl->draw('export');
-            logm('export view');
-            break;
-        case 'config':
-            $tpl->assign('load_all_js', 0);
-            $tpl->draw('head');
-            $tpl->draw('home');
-            $tpl->draw('config');
-            $tpl->draw('js');
-            $tpl->draw('footer');
-            logm('config view');
-            break;
-        case 'view':
-            $entry = $store->retrieveOneById($id);
-
-            if ($entry != NULL) {
-                $tpl->assign('id', $entry['id']);
-                $tpl->assign('url', $entry['url']);
-                $tpl->assign('title', $entry['title']);
-                $content = $entry['content'];
-                if (function_exists('tidy_parse_string')) {
-                    $tidy = tidy_parse_string($content, array('indent'=>true, 'show-body-only' => true), 'UTF8');
-                    $tidy->cleanRepair();
-                    $content = $tidy->value;
-                }
-                $tpl->assign('content', $content);
-                $tpl->assign('is_fav', $entry['is_fav']);
-                $tpl->assign('is_read', $entry['is_read']);
-                $tpl->assign('load_all_js', 0);
-                $tpl->draw('view');
-            }
-            else {
-                logm('error in view call : entry is NULL');
-            }
-
-            logm('view link #' . $id);
-            break;
-        default: # home view
-            $entries = $store->getEntriesByView($view);
-
-            $tpl->assign('entries', $entries);
-
-            if ($full_head == 'yes') {
-                $tpl->assign('load_all_js', 1);
-                $tpl->draw('head');
-                $tpl->draw('home');
-            }
-
-            $tpl->draw('entries');
-
-            if ($full_head == 'yes') {
-                $tpl->draw('js');
-                $tpl->draw('footer');
-            }
-            break;
-    }
-}
-
-/**
- * Appel d'une action (mark as fav, archive, delete)
- */
-function action_to_do($action, $url, $id = 0)
-{
-    global $store, $msg;
-
-    switch ($action)
-    {
-        case 'add':
-            if ($url == '')
-                continue;
-
-            if (MyTool::isUrl($url)) {
-                if($parametres_url = prepare_url($url)) {
-                    if ($store->add($url, $parametres_url['title'], $parametres_url['content'])) {
-                        $last_id = $store->getLastId();
-                        if (DOWNLOAD_PICTURES) {
-                            $content = filtre_picture($parametres_url['content'], $url, $last_id);
-                        }
-                        $msg->add('s', 'the link has been added successfully');
-                    }
-                    else {
-                        $msg->add('e', 'error during insertion : the link wasn\'t added');
-                    }
-                }
-                else {
-                    $msg->add('e', 'error during url preparation : the link wasn\'t added');
-                    logm('error during url preparation');
-                }
-            }
-            else {
-                $msg->add('e', 'error during url preparation : the link is not valid');
-                logm($url . ' is not a valid url');
-            }
-
-            logm('add link ' . $url);
-            break;
-        case 'delete':
-            if ($store->deleteById($id)) {
-                remove_directory(ABS_PATH . $id);
-                $msg->add('s', 'the link has been deleted successfully');
-                logm('delete link #' . $id);
-            }
-            else {
-                $msg->add('e', 'the link wasn\'t deleted');
-                logm('error : can\'t delete link #' . $id);
-            }
-            break;
-        case 'toggle_fav' :
-            $store->favoriteById($id);
-            logm('mark as favorite link #' . $id);
-            break;
-        case 'toggle_archive' :
-            $store->archiveById($id);
-            logm('archive link #' . $id);
-            break;
-        default:
-            break;
-    }
-}
-
-function logm($message)
-{
-    $t = strval(date('Y/m/d_H:i:s')).' - '.$_SERVER["REMOTE_ADDR"].' - '.strval($message)."\n";
-    file_put_contents('./log.txt',$t,FILE_APPEND);
-}
diff --git a/inc/poche/Database.class.php b/inc/poche/Database.class.php
new file mode 100644 (file)
index 0000000..cd5a9a3
--- /dev/null
@@ -0,0 +1,216 @@
+<?php
+/**
+ * poche, a read it later open source system
+ *
+ * @category   poche
+ * @author     Nicolas Lœuillet <support@inthepoche.com>
+ * @copyright  2013
+ * @license    http://www.wtfpl.net/ see COPYING file
+ */
+
+class Database {
+    var $handle;
+
+    function __construct()
+    {
+        switch (STORAGE) {
+            case 'sqlite':
+                $db_path = 'sqlite:' . STORAGE_SQLITE;
+                $this->handle = new PDO($db_path);
+                break;
+            case 'mysql':
+                $db_path = 'mysql:host=' . STORAGE_SERVER . ';dbname=' . STORAGE_DB;
+                $this->handle = new PDO($db_path, STORAGE_USER, STORAGE_PASSWORD); 
+                break;
+            case 'postgres':
+                $db_path = 'pgsql:host=' . STORAGE_SERVER . ';dbname=' . STORAGE_DB;
+                $this->handle = new PDO($db_path, STORAGE_USER, STORAGE_PASSWORD); 
+                break;
+        }
+
+        $this->handle->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
+        Tools::logm('storage type ' . STORAGE);
+    }
+
+    private function getHandle() {
+        return $this->handle;
+    }
+
+    public function isInstalled() {
+        $sql = "SELECT username FROM users";
+        $query = $this->executeQuery($sql, array());
+        $hasAdmin = count($query->fetchAll());
+
+        if ($hasAdmin == 0) 
+            return FALSE;
+
+        return TRUE;
+    }
+
+    public function install($login, $password) {
+        $sql = 'INSERT INTO users ( username, password, name, email) VALUES (?, ?, ?, ?)';
+        $params = array($login, $password, $login, ' ');
+        $query = $this->executeQuery($sql, $params);
+
+        $sequence = '';
+        if (STORAGE == 'postgres') {
+            $sequence = 'users_id_seq';
+        }
+
+        $id_user = intval($this->getLastId($sequence));
+
+        $sql = 'INSERT INTO users_config ( user_id, name, value ) VALUES (?, ?, ?)';
+        $params = array($id_user, 'pager', '10');
+        $query = $this->executeQuery($sql, $params);
+
+        $sql = 'INSERT INTO users_config ( user_id, name, value ) VALUES (?, ?, ?)';
+        $params = array($id_user, 'language', 'en_EN.UTF8');
+        $query = $this->executeQuery($sql, $params);
+
+        return TRUE;
+    }
+
+    private function getConfigUser($id) {
+        $sql = "SELECT * FROM users_config WHERE user_id = ?";
+        $query = $this->executeQuery($sql, array($id));
+        $result = $query->fetchAll();
+        $user_config = array();
+        
+        foreach ($result as $key => $value) {
+            $user_config[$value['name']] = $value['value'];
+        }
+
+        return $user_config;
+    }
+
+    public function login($username, $password) {
+        $sql = "SELECT * FROM users WHERE username=? AND password=?";
+        $query = $this->executeQuery($sql, array($username, $password));
+        $login = $query->fetchAll();
+
+        $user = array();
+        if (isset($login[0])) {
+            $user['id'] = $login[0]['id'];
+            $user['username'] = $login[0]['username'];
+            $user['password'] = $login[0]['password'];
+            $user['name'] = $login[0]['name'];
+            $user['email'] = $login[0]['email'];
+            $user['config'] = $this->getConfigUser($login[0]['id']);
+        }
+
+        return $user;
+    }
+
+    public function updatePassword($id, $password)
+    {
+        $sql_update = "UPDATE users SET password=? WHERE id=?";
+        $params_update = array($password, $id);
+        $query = $this->executeQuery($sql_update, $params_update);
+    }
+
+    private function executeQuery($sql, $params) {
+        try
+        {
+            $query = $this->getHandle()->prepare($sql);
+            $query->execute($params);
+            return $query;
+        }
+        catch (Exception $e)
+        {
+            Tools::logm('execute query error : '.$e->getMessage());
+            return FALSE;
+        }
+    }
+
+    public function retrieveAll($user_id) {
+        $sql        = "SELECT * FROM entries WHERE user_id=? ORDER BY id";
+        $query      = $this->executeQuery($sql, array($user_id));
+        $entries    = $query->fetchAll();
+
+        return $entries;
+    }
+
+    public function retrieveOneById($id, $user_id) {
+        $entry  = NULL;
+        $sql    = "SELECT * FROM entries WHERE id=? AND user_id=?";
+        $params = array(intval($id), $user_id);
+        $query  = $this->executeQuery($sql, $params);
+        $entry  = $query->fetchAll();
+
+        return $entry[0];
+    }
+
+    public function getEntriesByView($view, $user_id, $limit = '') {
+        switch ($_SESSION['sort'])
+        {
+            case 'ia':
+                $order = 'ORDER BY id';
+                break;
+            case 'id':
+                $order = 'ORDER BY id DESC';
+                break;
+            case 'ta':
+                $order = 'ORDER BY lower(title)';
+                break;
+            case 'td':
+                $order = 'ORDER BY lower(title) DESC';
+                break;
+            default:
+                $order = 'ORDER BY id';
+                break;
+        }
+
+        switch ($view)
+        {
+            case 'archive':
+                $sql    = "SELECT * FROM entries WHERE user_id=? AND is_read=? " . $order;
+                $params = array($user_id, 1);
+                break;
+            case 'fav' :
+                $sql    = "SELECT * FROM entries WHERE user_id=? AND is_fav=? " . $order;
+                $params = array($user_id, 1);
+                break;
+            default:
+                $sql    = "SELECT * FROM entries WHERE user_id=? AND is_read=? " . $order;
+                $params = array($user_id, 0);
+                break;
+        }
+
+        $sql .= ' ' . $limit;
+
+        $query = $this->executeQuery($sql, $params);
+        $entries = $query->fetchAll();
+
+        return $entries;
+    }
+
+    public function add($url, $title, $content, $user_id) {
+        $sql_action = 'INSERT INTO entries ( url, title, content, user_id ) VALUES (?, ?, ?, ?)';
+        $params_action = array($url, $title, $content, $user_id);
+        $query = $this->executeQuery($sql_action, $params_action);
+        return $query;
+    }
+
+    public function deleteById($id, $user_id) {
+        $sql_action     = "DELETE FROM entries WHERE id=? AND user_id=?";
+        $params_action  = array($id, $user_id);
+        $query          = $this->executeQuery($sql_action, $params_action);
+        return $query;
+    }
+
+    public function favoriteById($id, $user_id) {
+        $sql_action     = "UPDATE entries SET is_fav=NOT is_fav WHERE id=? AND user_id=?";
+        $params_action  = array($id, $user_id);
+        $query          = $this->executeQuery($sql_action, $params_action);
+    }
+
+    public function archiveById($id, $user_id) {
+        $sql_action     = "UPDATE entries SET is_read=NOT is_read WHERE id=? AND user_id=?";
+        $params_action  = array($id, $user_id);
+        $query          = $this->executeQuery($sql_action, $params_action);
+    }
+
+    public function getLastId($column = '') {
+        return $this->getHandle()->lastInsertId($column);
+    }
+}
diff --git a/inc/poche/Poche.class.php b/inc/poche/Poche.class.php
new file mode 100644 (file)
index 0000000..56910bc
--- /dev/null
@@ -0,0 +1,485 @@
+<?php
+/**
+ * poche, a read it later open source system
+ *
+ * @category   poche
+ * @author     Nicolas Lœuillet <support@inthepoche.com>
+ * @copyright  2013
+ * @license    http://www.wtfpl.net/ see COPYING file
+ */
+
+class Poche
+{
+    public $user;
+    public $store;
+    public $tpl;
+    public $messages;
+    public $pagination;
+
+    function __construct()
+    {
+        $this->store = new Database();
+        $this->init();
+        $this->messages = new Messages();
+
+        # installation
+        if(!$this->store->isInstalled())
+        {
+            $this->install();
+        }
+    }
+
+    private function init() 
+    {
+        if (file_exists('./install') && !DEBUG_POCHE) {
+            Tools::logm('folder /install exists');
+            die('the folder /install exists, you have to delete it before using poche.');
+        }
+
+        Tools::initPhp();
+        Session::init();
+
+        if (isset($_SESSION['poche_user']) && $_SESSION['poche_user'] != array()) {
+            $this->user = $_SESSION['poche_user'];
+        }
+        else {
+            # fake user, just for install & login screens
+            $this->user = new User();
+            $this->user->setConfig($this->getDefaultConfig());
+        }
+
+        # l10n
+        $language = $this->user->getConfigValue('language');
+        putenv('LC_ALL=' . $language);
+        setlocale(LC_ALL, $language);
+        bindtextdomain($language, LOCALE); 
+        textdomain($language); 
+
+        # template engine
+        $loader = new Twig_Loader_Filesystem(TPL);
+        if (DEBUG_POCHE) {
+            $twig_params = array();
+        }
+        else {
+            $twig_params = array('cache' => CACHE);
+        }
+        $this->tpl = new Twig_Environment($loader, $twig_params);
+        $this->tpl->addExtension(new Twig_Extensions_Extension_I18n());
+        # filter to display domain name of an url
+        $filter = new Twig_SimpleFilter('getDomain', 'Tools::getDomain');
+        $this->tpl->addFilter($filter);
+
+        # Pagination
+        $this->pagination = new Paginator($this->user->getConfigValue('pager'), 'p');
+    }
+
+    private function install() 
+    {
+        Tools::logm('poche still not installed');
+        echo $this->tpl->render('install.twig', array(
+            'token' => Session::getToken()
+        ));
+        if (isset($_GET['install'])) {
+            if (($_POST['password'] == $_POST['password_repeat']) 
+                && $_POST['password'] != "" && $_POST['login'] != "") {
+                # let's rock, install poche baby !
+                $this->store->install($_POST['login'], Tools::encodeString($_POST['password'] . $_POST['login']));
+                Session::logout();
+                Tools::logm('poche is now installed');
+                Tools::redirect();
+            }
+            else {
+                Tools::logm('error during installation');
+                Tools::redirect();
+            }
+        }
+        exit();
+    }
+
+    public function getDefaultConfig()
+    {
+        return array(
+            'pager' => PAGINATION,
+            'language' => LANG,
+            );
+    }
+
+    /**
+     * Call action (mark as fav, archive, delete, etc.)
+     */
+    public function action($action, Url $url, $id = 0, $import = FALSE)
+    {
+        switch ($action)
+        {
+            case 'add':
+                if($parametres_url = $url->fetchContent()) {
+                    if ($this->store->add($url->getUrl(), $parametres_url['title'], $parametres_url['content'], $this->user->getId())) {
+                        Tools::logm('add link ' . $url->getUrl());
+                        $sequence = '';
+                        if (STORAGE == 'postgres') {
+                            $sequence = 'entries_id_seq';
+                        }
+                        $last_id = $this->store->getLastId($sequence);
+                        if (DOWNLOAD_PICTURES) {
+                            $content = filtre_picture($parametres_url['content'], $url->getUrl(), $last_id);
+                        }
+                        if (!$import) {
+                            $this->messages->add('s', _('the link has been added successfully'));
+                        }
+                    }
+                    else {
+                        if (!$import) {
+                            $this->messages->add('e', _('error during insertion : the link wasn\'t added'));
+                            Tools::logm('error during insertion : the link wasn\'t added ' . $url->getUrl());
+                        }
+                    }
+                }
+                else {
+                    if (!$import) {
+                        $this->messages->add('e', _('error during fetching content : the link wasn\'t added'));
+                        Tools::logm('error during content fetch ' . $url->getUrl());
+                    }
+                }
+                if (!$import) {
+                    Tools::redirect();
+                }
+                break;
+            case 'delete':
+                $msg = 'delete link #' . $id;
+                if ($this->store->deleteById($id, $this->user->getId())) {
+                    if (DOWNLOAD_PICTURES) {
+                        remove_directory(ABS_PATH . $id);
+                    }
+                    $this->messages->add('s', _('the link has been deleted successfully'));
+                }
+                else {
+                    $this->messages->add('e', _('the link wasn\'t deleted'));
+                    $msg = 'error : can\'t delete link #' . $id;
+                }
+                Tools::logm($msg);
+                Tools::redirect('?');
+                break;
+            case 'toggle_fav' :
+                $this->store->favoriteById($id, $this->user->getId());
+                Tools::logm('mark as favorite link #' . $id);
+                if (!$import) {
+                    Tools::redirect();
+                }
+                break;
+            case 'toggle_archive' :
+                $this->store->archiveById($id, $this->user->getId());
+                Tools::logm('archive link #' . $id);
+                if (!$import) {
+                    Tools::redirect();
+                }
+                break;
+            default:
+                break;
+        }
+    }
+
+    function displayView($view, $id = 0)
+    {
+        $tpl_vars = array();
+
+        switch ($view)
+        {
+            case 'config':
+                $dev = $this->getPocheVersion('dev');
+                $prod = $this->getPocheVersion('prod');
+                $compare_dev = version_compare(POCHE_VERSION, $dev);
+                $compare_prod = version_compare(POCHE_VERSION, $prod);
+                $tpl_vars = array(
+                    'dev' => $dev,
+                    'prod' => $prod,
+                    'compare_dev' => $compare_dev,
+                    'compare_prod' => $compare_prod,
+                );
+                Tools::logm('config view');
+                break;
+            case 'view':
+                $entry = $this->store->retrieveOneById($id, $this->user->getId());
+                if ($entry != NULL) {
+                    Tools::logm('view link #' . $id);
+                    $content = $entry['content'];
+                    if (function_exists('tidy_parse_string')) {
+                        $tidy = tidy_parse_string($content, array('indent'=>true, 'show-body-only' => true), 'UTF8');
+                        $tidy->cleanRepair();
+                        $content = $tidy->value;
+                    }
+                    $tpl_vars = array(
+                        'entry' => $entry,
+                        'content' => $content,
+                    );
+                }
+                else {
+                    Tools::logm('error in view call : entry is NULL');
+                }
+                break;
+            default: # home view
+                $entries = $this->store->getEntriesByView($view, $this->user->getId());
+                $this->pagination->set_total(count($entries));
+                $page_links = $this->pagination->page_links('?view=' . $view . '&sort=' . $_SESSION['sort'] . '&');
+                $datas = $this->store->getEntriesByView($view, $this->user->getId(), $this->pagination->get_limit());
+                $tpl_vars = array(
+                    'entries' => $datas,
+                    'page_links' => $page_links,
+                );
+                Tools::logm('display ' . $view . ' view');
+                break;
+        }
+
+        return $tpl_vars;
+    }
+
+    /**
+     * update the password of the current user. 
+     * if MODE_DEMO is TRUE, the password can't be updated. 
+     * @todo add the return value
+     * @todo set the new password in function header like this updatePassword($newPassword)
+     * @return boolean
+     */
+    public function updatePassword()
+    {
+        if (MODE_DEMO) {
+            $this->messages->add('i', _('in demo mode, you can\'t update your password'));
+            Tools::logm('in demo mode, you can\'t do this');
+            Tools::redirect('?view=config');
+        }
+        else {
+            if (isset($_POST['password']) && isset($_POST['password_repeat'])) {
+                if ($_POST['password'] == $_POST['password_repeat'] && $_POST['password'] != "") {
+                    $this->messages->add('s', _('your password has been updated'));
+                    $this->store->updatePassword($this->user->getId(), Tools::encodeString($_POST['password'] . $this->user->getUsername()));
+                    Session::logout();
+                    Tools::logm('password updated');
+                    Tools::redirect();
+                }
+                else {
+                    $this->messages->add('e', _('the two fields have to be filled & the password must be the same in the two fields'));
+                    Tools::redirect('?view=config');
+                }
+            }
+        }
+    }
+
+    /**
+     * checks if login & password are correct and save the user in session.
+     * it redirects the user to the $referer link
+     * @param  string $referer the url to redirect after login
+     * @todo add the return value
+     * @return boolean
+     */
+    public function login($referer)
+    {
+        if (!empty($_POST['login']) && !empty($_POST['password'])) {
+            $user = $this->store->login($_POST['login'], Tools::encodeString($_POST['password'] . $_POST['login']));
+            if ($user != array()) {
+                # Save login into Session
+                Session::login($user['username'], $user['password'], $_POST['login'], Tools::encodeString($_POST['password'] . $_POST['login']), array('poche_user' => new User($user)));
+
+                $this->messages->add('s', _('welcome to your poche'));
+                if (!empty($_POST['longlastingsession'])) {
+                    $_SESSION['longlastingsession'] = 31536000;
+                    $_SESSION['expires_on'] = time() + $_SESSION['longlastingsession'];
+                    session_set_cookie_params($_SESSION['longlastingsession']);
+                } else {
+                    session_set_cookie_params(0);
+                }
+                session_regenerate_id(true);
+                Tools::logm('login successful');
+                Tools::redirect($referer);
+            }
+            $this->messages->add('e', _('login failed: bad login or password'));
+            Tools::logm('login failed');
+            Tools::redirect();
+        } else {
+            $this->messages->add('e', _('login failed: you have to fill all fields'));
+            Tools::logm('login failed');
+            Tools::redirect();
+        }
+    }
+
+    /**
+     * log out the poche user. It cleans the session.
+     * @todo add the return value
+     * @return boolean 
+     */
+    public function logout()
+    {
+        $this->user = array();
+        Session::logout();
+        $this->messages->add('s', _('see you soon!'));
+        Tools::logm('logout');
+        Tools::redirect();
+    }
+
+    /**
+     * import from Instapaper. poche needs a ./instapaper-export.html file
+     * @todo add the return value
+     * @return boolean
+     */
+    private function importFromInstapaper()
+    {
+        # TODO gestion des articles favs
+        $html = new simple_html_dom();
+        $html->load_file('./instapaper-export.html');
+        Tools::logm('starting import from instapaper');
+
+        $read = 0;
+        $errors = array();
+        foreach($html->find('ol') as $ul)
+        {
+            foreach($ul->find('li') as $li)
+            {
+                $a = $li->find('a');
+                $url = new Url(base64_encode($a[0]->href));
+                $this->action('add', $url, 0, TRUE);
+                if ($read == '1') {
+                    $sequence = '';
+                    if (STORAGE == 'postgres') {
+                        $sequence = 'entries_id_seq';
+                    }
+                    $last_id = $this->store->getLastId($sequence);
+                    $this->action('toggle_archive', $url, $last_id, TRUE);
+                }
+            }
+
+            # the second <ol> is for read links
+            $read = 1;
+        }
+        $this->messages->add('s', _('import from instapaper completed'));
+        Tools::logm('import from instapaper completed');
+        Tools::redirect();
+    }
+
+    /**
+     * import from Pocket. poche needs a ./ril_export.html file
+     * @todo add the return value
+     * @return boolean 
+     */
+    private function importFromPocket()
+    {
+        # TODO gestion des articles favs
+        $html = new simple_html_dom();
+        $html->load_file('./ril_export.html');
+        Tools::logm('starting import from pocket');
+
+        $read = 0;
+        $errors = array();
+        foreach($html->find('ul') as $ul)
+        {
+            foreach($ul->find('li') as $li)
+            {
+                $a = $li->find('a');
+                $url = new Url(base64_encode($a[0]->href));
+                $this->action('add', $url, 0, TRUE);
+                if ($read == '1') {
+                        $sequence = '';
+                        if (STORAGE == 'postgres') {
+                            $sequence = 'entries_id_seq';
+                        }
+                    $last_id = $this->store->getLastId($sequence);
+                    $this->action('toggle_archive', $url, $last_id, TRUE);
+                }
+            }
+            
+            # the second <ul> is for read links
+            $read = 1;
+        }
+        $this->messages->add('s', _('import from pocket completed'));
+        Tools::logm('import from pocket completed');
+        Tools::redirect();
+    }
+
+    /**
+     * import from Readability. poche needs a ./readability file
+     * @todo add the return value
+     * @return boolean 
+     */
+    private function importFromReadability()
+    {
+        # TODO gestion des articles lus / favs
+        $str_data = file_get_contents("./readability");
+        $data = json_decode($str_data,true);
+        Tools::logm('starting import from Readability');
+
+        foreach ($data as $key => $value) {
+            $url = '';
+            foreach ($value as $attr => $attr_value) {
+                if ($attr == 'article__url') {
+                    $url = new Url(base64_encode($attr_value));
+                }
+                $sequence = '';
+                if (STORAGE == 'postgres') {
+                    $sequence = 'entries_id_seq';
+                }
+                // if ($attr_value == 'favorite' && $attr_value == 'true') {
+                //     $last_id = $this->store->getLastId($sequence);
+                //     $this->store->favoriteById($last_id);
+                //     $this->action('toogle_fav', $url, $last_id, TRUE);
+                // }
+                if ($attr_value == 'archive' && $attr_value == 'true') {
+                    $last_id = $this->store->getLastId($sequence);
+                    $this->action('toggle_archive', $url, $last_id, TRUE);
+                }
+            }
+            if ($url->isCorrect())
+                $this->action('add', $url, 0, TRUE);
+        }
+        $this->messages->add('s', _('import from Readability completed'));
+        Tools::logm('import from Readability completed');
+        Tools::redirect();
+    }
+
+    /**
+     * import datas into your poche
+     * @param  string $from name of the service to import : pocket, instapaper or readability
+     * @todo add the return value
+     * @return boolean       
+     */
+    public function import($from)
+    {
+        if ($from == 'pocket') {
+            return $this->importFromPocket();
+        }
+        else if ($from == 'readability') {
+            return $this->importFromReadability();
+        }
+        else if ($from == 'instapaper') {
+            return $this->importFromInstapaper();
+        }
+    }
+
+    /**
+     * export poche entries in json
+     * @return json all poche entries
+     */
+    public function export()
+    {
+        $entries = $this->store->retrieveAll($this->user->getId());
+        echo $this->tpl->render('export.twig', array(
+            'export' => Tools::renderJson($entries),
+        ));
+        Tools::logm('export view');
+    }
+
+    /**
+     * Checks online the latest version of poche and cache it
+     * @param  string $which 'prod' or 'dev'
+     * @return string        latest $which version
+     */
+    private function getPocheVersion($which = 'prod')
+    {
+        $cache_file = CACHE . '/' . $which;
+
+        # checks if the cached version file exists
+        if (file_exists($cache_file) && (filemtime($cache_file) > (time() - 86400 ))) {
+           $version = file_get_contents($cache_file);
+        } else {
+           $version = file_get_contents('http://static.inthepoche.com/versions/' . $which);
+           file_put_contents($cache_file, $version, LOCK_EX);
+        }
+        return $version;
+    }
+}
\ No newline at end of file
diff --git a/inc/poche/Tools.class.php b/inc/poche/Tools.class.php
new file mode 100644 (file)
index 0000000..d0e4316
--- /dev/null
@@ -0,0 +1,226 @@
+<?php
+/**
+ * poche, a read it later open source system
+ *
+ * @category   poche
+ * @author     Nicolas Lœuillet <support@inthepoche.com>
+ * @copyright  2013
+ * @license    http://www.wtfpl.net/ see COPYING file
+ */
+class Tools
+{
+    public static function initPhp()
+    {
+        define('START_TIME', microtime(true));
+
+        if (phpversion() < 5) {
+            die(_('Oops, it seems you don\'t have PHP 5.'));
+        }
+
+        error_reporting(E_ALL);
+
+        function stripslashesDeep($value) {
+            return is_array($value)
+                ? array_map('stripslashesDeep', $value)
+                : stripslashes($value);
+        }
+
+        if (get_magic_quotes_gpc()) {
+            $_POST = array_map('stripslashesDeep', $_POST);
+            $_GET = array_map('stripslashesDeep', $_GET);
+            $_COOKIE = array_map('stripslashesDeep', $_COOKIE);
+        }
+
+        ob_start();
+        register_shutdown_function('ob_end_flush');
+    }
+
+    public static function getPocheUrl()
+    {
+        $https = (!empty($_SERVER['HTTPS'])
+                    && (strtolower($_SERVER['HTTPS']) == 'on'))
+            || (isset($_SERVER["SERVER_PORT"])
+                    && $_SERVER["SERVER_PORT"] == '443'); // HTTPS detection.
+        $serverport = (!isset($_SERVER["SERVER_PORT"])
+            || $_SERVER["SERVER_PORT"] == '80'
+            || ($https && $_SERVER["SERVER_PORT"] == '443')
+            ? '' : ':' . $_SERVER["SERVER_PORT"]);
+
+        $scriptname = str_replace('/index.php', '/', $_SERVER["SCRIPT_NAME"]);
+
+        if (!isset($_SERVER["SERVER_NAME"])) {
+            return $scriptname;
+        }
+
+        return 'http' . ($https ? 's' : '') . '://'
+            . $_SERVER["SERVER_NAME"] . $serverport . $scriptname;
+    }
+
+    public static function redirect($url = '')
+    {
+        if ($url === '') {
+            $url = (empty($_SERVER['HTTP_REFERER'])?'?':$_SERVER['HTTP_REFERER']);
+            if (isset($_POST['returnurl'])) {
+                $url = $_POST['returnurl'];
+            }
+        }
+
+        # prevent loop
+        if (empty($url) || parse_url($url, PHP_URL_QUERY) === $_SERVER['QUERY_STRING']) {
+            $url = Tools::getPocheUrl();
+        }
+
+        if (substr($url, 0, 1) !== '?') {
+            $ref = Tools::getPocheUrl();
+            if (substr($url, 0, strlen($ref)) !== $ref) {
+                $url = $ref;
+            }
+        }
+        self::logm('redirect to ' . $url);
+        header('Location: '.$url);
+        exit();
+    }
+
+    public static function getTplFile($view)
+    {
+        $tpl_file = 'home.twig';
+        switch ($view)
+        {
+            case 'install':
+                $tpl_file = 'install.twig';
+                break;
+            case 'import';
+                $tpl_file = 'import.twig';
+                break;
+            case 'export':
+                $tpl_file = 'export.twig';
+                break;
+            case 'config':
+                $tpl_file = 'config.twig';
+                break;
+            case 'view':
+                $tpl_file = 'view.twig';
+                break;
+            default:
+            break;
+        }
+        return $tpl_file;
+    }
+
+    public static function getFile($url)
+    {
+        $timeout = 15;
+        $useragent = "Mozilla/5.0 (Windows NT 5.1; rv:18.0) Gecko/20100101 Firefox/18.0";
+
+        if (in_array ('curl', get_loaded_extensions())) {
+            # Fetch feed from URL
+            $curl = curl_init();
+            curl_setopt($curl, CURLOPT_URL, $url);
+            curl_setopt($curl, CURLOPT_TIMEOUT, $timeout);
+            curl_setopt($curl, CURLOPT_FOLLOWLOCATION, true);
+            curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
+            curl_setopt($curl, CURLOPT_HEADER, false);
+
+            # for ssl, do not verified certificate
+            curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, FALSE);
+            curl_setopt($curl, CURLOPT_AUTOREFERER, TRUE );
+
+            # FeedBurner requires a proper USER-AGENT...
+            curl_setopt($curl, CURL_HTTP_VERSION_1_1, true);
+            curl_setopt($curl, CURLOPT_ENCODING, "gzip, deflate");
+            curl_setopt($curl, CURLOPT_USERAGENT, $useragent);
+
+            $data = curl_exec($curl);
+            $httpcode = curl_getinfo($curl, CURLINFO_HTTP_CODE);
+            $httpcodeOK = isset($httpcode) and ($httpcode == 200 or $httpcode == 301);
+            curl_close($curl);
+        } else {
+            # create http context and add timeout and user-agent
+            $context = stream_context_create(
+                array(
+                    'http' => array(
+                        'timeout' => $timeout,
+                        'header' => "User-Agent: " . $useragent,
+                        'follow_location' => true
+                    ),
+                    'ssl' => array(
+                        'verify_peer' => false,
+                        'allow_self_signed' => true
+                    )
+                )
+            );
+
+            # only download page lesser than 4MB
+            $data = @file_get_contents($url, false, $context, -1, 4000000); 
+
+            if (isset($http_response_header) and isset($http_response_header[0])) {
+                $httpcodeOK = isset($http_response_header) and isset($http_response_header[0]) and ((strpos($http_response_header[0], '200 OK') !== FALSE) or (strpos($http_response_header[0], '301 Moved Permanently') !== FALSE));
+            }
+        }
+
+        # if response is not empty and response is OK
+        if (isset($data) and isset($httpcodeOK) and $httpcodeOK) {
+
+            # take charset of page and get it
+            preg_match('#<meta .*charset=.*>#Usi', $data, $meta);
+
+            # if meta tag is found
+            if (!empty($meta[0])) {
+                preg_match('#charset="?(.*)"#si', $meta[0], $encoding);
+                # if charset is found set it otherwise, set it to utf-8
+                $html_charset = (!empty($encoding[1])) ? strtolower($encoding[1]) : 'utf-8';
+            } else {
+                $html_charset = 'utf-8';
+                $encoding[1] = '';
+            }
+
+            # replace charset of url to charset of page
+            $data = str_replace('charset=' . $encoding[1], 'charset=' . $html_charset, $data);
+
+            return $data;
+        }
+        else {
+            return FALSE;
+        }
+    }
+
+    public static function renderJson($data)
+    {
+        header('Cache-Control: no-cache, must-revalidate');
+        header('Expires: Sat, 26 Jul 1997 05:00:00 GMT');
+        header('Content-type: application/json; charset=UTF-8');
+        echo json_encode($data);
+        exit();
+    }
+
+    public static function logm($message)
+    {
+        if (DEBUG_POCHE) {
+            $t = strval(date('Y/m/d_H:i:s')) . ' - ' . $_SERVER["REMOTE_ADDR"] . ' - ' . strval($message) . "\n";
+            file_put_contents(CACHE . '/log.txt', $t, FILE_APPEND);
+            error_log('DEBUG POCHE : ' . $message);
+        }
+    }
+
+    public static function encodeString($string) 
+    {
+        return sha1($string . SALT);
+    }
+
+    public static function checkVar($var, $default = '')
+    {
+        return ((isset ($_REQUEST["$var"])) ? htmlentities($_REQUEST["$var"]) : $default);
+    }
+
+    public static function getDomain($url)
+    {
+      $pieces = parse_url($url);
+      $domain = isset($pieces['host']) ? $pieces['host'] : '';
+      if (preg_match('/(?P<domain>[a-z0-9][a-z0-9\-]{1,63}\.[a-z\.]{2,6})$/i', $domain, $regs)) {
+        return $regs['domain'];
+      }
+      
+      return FALSE;
+    }
+}
\ No newline at end of file
diff --git a/inc/poche/Url.class.php b/inc/poche/Url.class.php
new file mode 100644 (file)
index 0000000..f4a8f99
--- /dev/null
@@ -0,0 +1,94 @@
+<?php
+/**
+ * poche, a read it later open source system
+ *
+ * @category   poche
+ * @author     Nicolas Lœuillet <support@inthepoche.com>
+ * @copyright  2013
+ * @license    http://www.wtfpl.net/ see COPYING file
+ */
+
+class Url
+{
+    public $url;
+
+    function __construct($url)
+    {
+        $this->url = base64_decode($url);
+    }
+
+    public function getUrl() {
+        return $this->url;
+    }
+
+    public function setUrl($url) {
+        $this->url = $url;
+    }
+
+    public function isCorrect()
+    {
+        $pattern = '|^http(s)?://[a-z0-9-]+(.[a-z0-9-]+)*(:[0-9]+)?(/.*)?$|i';
+
+        return preg_match($pattern, $this->url);
+    }
+
+    public function clean()
+    {
+        $url = html_entity_decode(trim($this->url));
+
+        $stuff = strpos($url,'&utm_source=');
+        if ($stuff !== FALSE)
+            $url = substr($url, 0, $stuff);
+        $stuff = strpos($url,'?utm_source=');
+        if ($stuff !== FALSE)
+            $url = substr($url, 0, $stuff);
+        $stuff = strpos($url,'#xtor=RSS-');
+        if ($stuff !== FALSE)
+            $url = substr($url, 0, $stuff);
+
+        $this->url = $url;
+    }
+
+    public function fetchContent()
+    {
+        if ($this->isCorrect()) {
+            $this->clean();
+            $html = Encoding::toUTF8(Tools::getFile($this->getUrl()));
+
+            # if Tools::getFile() if not able to retrieve HTTPS content, try the same URL with HTTP protocol
+            if (!preg_match('!^https?://!i', $this->getUrl()) && (!isset($html) || strlen($html) <= 0)) {
+                $this->setUrl('http://' . $this->getUrl());
+                $html = Encoding::toUTF8(Tools::getFile($this->getUrl()));
+            }
+
+            if (function_exists('tidy_parse_string')) {
+                $tidy = tidy_parse_string($html, array(), 'UTF8');
+                $tidy->cleanRepair();
+                $html = $tidy->value;
+            }
+
+            $parameters = array();
+            if (isset($html) and strlen($html) > 0)
+            {
+                $readability = new Readability($html, $this->getUrl());
+                $readability->convertLinksToFootnotes = CONVERT_LINKS_FOOTNOTES;
+                $readability->revertForcedParagraphElements = REVERT_FORCED_PARAGRAPH_ELEMENTS;
+
+                if($readability->init())
+                {
+                    $content = $readability->articleContent->innerHTML;
+                    $parameters['title'] = $readability->articleTitle->innerHTML;
+                    $parameters['content'] = $content;
+
+                    return $parameters;
+                }
+            }
+        }
+        else {
+            #$msg->add('e', _('error during url preparation : the link is not valid'));
+            Tools::logm($this->getUrl() . ' is not a valid url');
+        }
+
+        return FALSE;
+    }
+}
\ No newline at end of file
diff --git a/inc/poche/User.class.php b/inc/poche/User.class.php
new file mode 100644 (file)
index 0000000..6dac783
--- /dev/null
@@ -0,0 +1,50 @@
+<?php
+/**
+ * poche, a read it later open source system
+ *
+ * @category   poche
+ * @author     Nicolas Lœuillet <support@inthepoche.com>
+ * @copyright  2013
+ * @license    http://www.wtfpl.net/ see COPYING file
+ */
+
+class User
+{
+    public $id;
+    public $username;
+    public $name;
+    public $password;
+    public $email;
+    public $config;
+
+    function __construct($user = array())
+    {
+        if ($user != array()) {
+            $this->id = $user['id'];
+            $this->username = $user['username'];
+            $this->name = $user['name'];
+            $this->password = $user['password'];
+            $this->email = $user['email'];
+            $this->config = $user['config'];
+        }
+    }
+
+    public function getId() 
+    {
+        return $this->id;
+    }
+
+    public function getUsername() 
+    {
+        return $this->username;
+    }
+
+    public function setConfig($config)
+    {
+        $this->config = $config;
+    }
+
+    public function getConfigValue($name) {
+        return (isset($this->config[$name])) ? $this->config[$name] : FALSE;
+    }
+}
\ No newline at end of file
diff --git a/inc/poche/config.inc.php b/inc/poche/config.inc.php
new file mode 100644 (file)
index 0000000..0958600
--- /dev/null
@@ -0,0 +1,61 @@
+<?php
+/**
+ * poche, a read it later open source system
+ *
+ * @category   poche
+ * @author     Nicolas Lœuillet <nicolas@loeuillet.org>
+ * @copyright  2013
+ * @license    http://www.wtfpl.net/ see COPYING file
+ */
+
+# storage
+define ('STORAGE','sqlite'); # postgres, mysql, sqlite
+define ('STORAGE_SERVER', 'localhost'); # leave blank for sqlite
+define ('STORAGE_DB', 'poche'); # only for postgres & mysql
+define ('STORAGE_SQLITE', './db/poche.sqlite');
+define ('STORAGE_USER', 'postgres'); # leave blank for sqlite
+define ('STORAGE_PASSWORD', 'postgres'); # leave blank for sqlite
+
+define ('POCHE_VERSION', '1.0-beta1');
+define ('MODE_DEMO', FALSE);
+define ('DEBUG_POCHE', TRUE);
+define ('CONVERT_LINKS_FOOTNOTES', FALSE);
+define ('REVERT_FORCED_PARAGRAPH_ELEMENTS', FALSE);
+define ('DOWNLOAD_PICTURES', FALSE);
+define ('SHARE_TWITTER', TRUE);
+define ('SHARE_MAIL', TRUE);
+define ('SALT', '464v54gLLw928uz4zUBqkRJeiPY68zCX');
+define ('ABS_PATH', 'assets/');
+define ('TPL', './tpl');
+define ('LOCALE', './locale');
+define ('CACHE', './cache');
+define ('LANG', 'en_EN.UTF8');
+define ('PAGINATION', '10');
+define ('THEME', 'light');
+
+# /!\ Be careful if you change the lines below /!\
+require_once './inc/poche/User.class.php';
+require_once './inc/poche/Tools.class.php';
+require_once './inc/poche/Url.class.php';
+require_once './inc/3rdparty/class.messages.php';
+require_once './inc/poche/Poche.class.php';
+require_once './inc/3rdparty/Readability.php';
+require_once './inc/3rdparty/Encoding.php';
+require_once './inc/poche/Database.class.php';
+require_once './vendor/autoload.php';
+require_once './inc/3rdparty/simple_html_dom.php';
+require_once './inc/3rdparty/paginator.php';
+require_once './inc/3rdparty/Session.class.php';
+
+if (DOWNLOAD_PICTURES) {
+    require_once './inc/poche/pochePictures.php';
+}
+
+$poche = new Poche();
+#XSRF protection with token
+// if (!empty($_POST)) {
+//     if (!Session::isToken($_POST['token'])) {
+//         die(_('Wrong token'));
+//     }
+//     unset($_SESSION['tokens']);
+// }
\ No newline at end of file
diff --git a/inc/poche/pochePictures.php b/inc/poche/pochePictures.php
new file mode 100644 (file)
index 0000000..4e4a0b0
--- /dev/null
@@ -0,0 +1,110 @@
+<?php
+/**
+ * poche, a read it later open source system
+ *
+ * @category   poche
+ * @author     Nicolas Lœuillet <support@inthepoche.com>
+ * @copyright  2013
+ * @license    http://www.wtfpl.net/ see COPYING file
+ */
+
+/**
+ * On modifie les URLS des images dans le corps de l'article
+ */
+function filtre_picture($content, $url, $id)
+{
+    $matches = array();
+    preg_match_all('#<\s*(img)[^>]+src="([^"]*)"[^>]*>#Si', $content, $matches, PREG_SET_ORDER);
+    foreach($matches as $i => $link) {
+        $link[1] = trim($link[1]);
+        if (!preg_match('#^(([a-z]+://)|(\#))#', $link[1])) {
+            $absolute_path = get_absolute_link($link[2],$url);
+            $filename = basename(parse_url($absolute_path, PHP_URL_PATH));
+            $directory = create_assets_directory($id);
+            $fullpath = $directory . '/' . $filename;
+            download_pictures($absolute_path, $fullpath);
+            $content = str_replace($matches[$i][2], $fullpath, $content);
+        }
+
+    }
+
+    return $content;
+}
+
+/**
+ * Retourne le lien absolu
+ */
+function get_absolute_link($relative_link, $url) {
+    /* return if already absolute URL */
+    if (parse_url($relative_link, PHP_URL_SCHEME) != '') return $relative_link;
+
+    /* queries and anchors */
+    if ($relative_link[0]=='#' || $relative_link[0]=='?') return $url . $relative_link;
+
+    /* parse base URL and convert to local variables:
+       $scheme, $host, $path */
+    extract(parse_url($url));
+
+    /* remove non-directory element from path */
+    $path = preg_replace('#/[^/]*$#', '', $path);
+
+    /* destroy path if relative url points to root */
+    if ($relative_link[0] == '/') $path = '';
+
+    /* dirty absolute URL */
+    $abs = $host . $path . '/' . $relative_link;
+
+    /* replace '//' or '/./' or '/foo/../' with '/' */
+    $re = array('#(/\.?/)#', '#/(?!\.\.)[^/]+/\.\./#');
+    for($n=1; $n>0; $abs=preg_replace($re, '/', $abs, -1, $n)) {}
+
+    /* absolute URL is ready! */
+    return $scheme.'://'.$abs;
+}
+
+/**
+ * Téléchargement des images
+ */
+function download_pictures($absolute_path, $fullpath)
+{
+    $rawdata = Tools::getFile($absolute_path);
+
+    if(file_exists($fullpath)) {
+        unlink($fullpath);
+    }
+    $fp = fopen($fullpath, 'x');
+    fwrite($fp, $rawdata);
+    fclose($fp);
+}
+
+/**
+ * Crée un répertoire de médias pour l'article
+ */
+function create_assets_directory($id)
+{
+    $assets_path = ABS_PATH;
+    if(!is_dir($assets_path)) {
+        mkdir($assets_path, 0705);
+    }
+
+    $article_directory = $assets_path . $id;
+    if(!is_dir($article_directory)) {
+        mkdir($article_directory, 0705);
+    }
+
+    return $article_directory;
+}
+
+/**
+ * Suppression du répertoire d'images
+ */
+function remove_directory($directory)
+{
+    if(is_dir($directory)) {
+        $files = array_diff(scandir($directory), array('.','..'));
+        foreach ($files as $file) {
+            (is_dir("$directory/$file")) ? remove_directory("$directory/$file") : unlink("$directory/$file");
+        }
+        return rmdir($directory);
+    }
+}
\ No newline at end of file
diff --git a/inc/rain.tpl.class.php b/inc/rain.tpl.class.php
deleted file mode 100644 (file)
index 6522c79..0000000
+++ /dev/null
@@ -1,1043 +0,0 @@
-<?php
-
-/**
- *  RainTPL
- *  -------
- *  Realized by Federico Ulfo & maintained by the Rain Team
- *  Distributed under GNU/LGPL 3 License
- *
- *  @version 2.7.2
- */
-
-
-class RainTPL{
-
-       // -------------------------
-       //      CONFIGURATION
-       // -------------------------
-
-               /**
-                * Template directory
-                *
-                * @var string
-                */
-               static $tpl_dir = "tpl/";
-
-
-               /**
-                * Cache directory. Is the directory where RainTPL will compile the template and save the cache
-                *
-                * @var string
-                */
-               static $cache_dir = "tmp/";
-
-
-               /**
-                * Template base URL. RainTPL will add this URL to the relative paths of element selected in $path_replace_list.
-                *
-                * @var string
-                */
-               static $base_url = null;
-
-
-               /**
-                * Template extension.
-                *
-                * @var string
-                */
-               static $tpl_ext = "html";
-
-
-               /**
-                * Path replace is a cool features that replace all relative paths of images (<img src="...">), stylesheet (<link href="...">), script (<script src="...">) and link (<a href="...">)
-                * Set true to enable the path replace.
-                *
-                * @var unknown_type
-                */
-               static $path_replace = true;
-
-
-               /**
-                * You can set what the path_replace method will replace.
-                * Avaible options: a, img, link, script, input
-                *
-                * @var array
-                */
-               static $path_replace_list = array( 'a', 'img', 'link', 'script', 'input' );
-
-
-               /**
-                * You can define in the black list what string are disabled into the template tags
-                *
-                * @var unknown_type
-                */
-               static $black_list = array( '\$this', 'raintpl::', 'self::', '_SESSION', '_SERVER', '_ENV',  'eval', 'exec', 'unlink', 'rmdir' );
-
-
-               /**
-                * Check template.
-                * true: checks template update time, if changed it compile them
-                * false: loads the compiled template. Set false if server doesn't have write permission for cache_directory.
-                *
-                */
-               static $check_template_update = true;
-
-
-               /**
-                * PHP tags <? ?>
-                * True: php tags are enabled into the template
-                * False: php tags are disabled into the template and rendered as html
-                *
-                * @var bool
-                */
-               static $php_enabled = true;
-
-
-               /**
-                * Debug mode flag.
-                * True: debug mode is used, syntax errors are displayed directly in template. Execution of script is not terminated.
-                * False: exception is thrown on found error.
-                *
-                * @var bool
-                */
-               static $debug = false;
-
-       // -------------------------
-
-
-       // -------------------------
-       //      RAINTPL VARIABLES
-       // -------------------------
-
-               /**
-                * Is the array where RainTPL keep the variables assigned
-                *
-                * @var array
-                */
-               public $var = array();
-
-               protected $tpl = array(),               // variables to keep the template directories and info
-                                 $cache = false,               // static cache enabled / disabled
-                  $cache_id = null;       // identify only one cache
-
-                protected static $config_name_sum = array();   // takes all the config to create the md5 of the file
-
-       // -------------------------
-
-
-
-       const CACHE_EXPIRE_TIME = 3600; // default cache expire time = hour
-
-
-
-       /**
-        * Assign variable
-        * eg.  $t->assign('name','mickey');
-        *
-        * @param mixed $variable_name Name of template variable or associative array name/value
-        * @param mixed $value value assigned to this variable. Not set if variable_name is an associative array
-        */
-
-       function assign( $variable, $value = null ){
-               if( is_array( $variable ) )
-                       $this->var += $variable;
-               else
-                       $this->var[ $variable ] = $value;
-       }
-
-
-
-       /**
-        * Draw the template
-        * eg.  $html = $tpl->draw( 'demo', TRUE ); // return template in string
-        * or   $tpl->draw( $tpl_name ); // echo the template
-        *
-        * @param string $tpl_name  template to load
-        * @param boolean $return_string  true=return a string, false=echo the template
-        * @return string
-        */
-
-       function draw( $tpl_name, $return_string = false ){
-
-               try {
-                       // compile the template if necessary and set the template filepath
-                       $this->check_template( $tpl_name );
-               } catch (RainTpl_Exception $e) {
-                       $output = $this->printDebug($e);
-                       die($output);
-               }
-
-               // Cache is off and, return_string is false
-        // Rain just echo the template
-
-        if( !$this->cache && !$return_string ){
-            extract( $this->var );
-            include $this->tpl['compiled_filename'];
-            unset( $this->tpl );
-        }
-
-
-               // cache or return_string are enabled
-        // rain get the output buffer to save the output in the cache or to return it as string
-
-        else{
-
-            //----------------------
-            // get the output buffer
-            //----------------------
-                ob_start();
-                extract( $this->var );
-                include $this->tpl['compiled_filename'];
-                $raintpl_contents = ob_get_clean();
-            //----------------------
-
-
-            // save the output in the cache
-            if( $this->cache )
-                file_put_contents( $this->tpl['cache_filename'], "<?php if(!class_exists('raintpl')){exit;}?>" . $raintpl_contents );
-
-            // free memory
-            unset( $this->tpl );
-
-            // return or print the template
-            if( $return_string ) return $raintpl_contents; else echo $raintpl_contents;
-
-        }
-
-       }
-
-
-
-       /**
-        * If exists a valid cache for this template it returns the cache
-        *
-        * @param string $tpl_name Name of template (set the same of draw)
-        * @param int $expiration_time Set after how many seconds the cache expire and must be regenerated
-        * @return string it return the HTML or null if the cache must be recreated
-        */
-
-       function cache( $tpl_name, $expire_time = self::CACHE_EXPIRE_TIME, $cache_id = null ){
-
-        // set the cache_id
-        $this->cache_id = $cache_id;
-
-               if( !$this->check_template( $tpl_name ) && file_exists( $this->tpl['cache_filename'] ) && ( time() - filemtime( $this->tpl['cache_filename'] ) < $expire_time ) )
-                       return substr( file_get_contents( $this->tpl['cache_filename'] ), 43 );
-               else{
-                       //delete the cache of the selected template
-            if (file_exists($this->tpl['cache_filename']))
-            unlink($this->tpl['cache_filename'] );
-                       $this->cache = true;
-               }
-       }
-
-
-
-       /**
-        * Configure the settings of RainTPL
-        *
-        */
-       static function configure( $setting, $value = null ){
-               if( is_array( $setting ) )
-                       foreach( $setting as $key => $value )
-                               self::configure( $key, $value );
-               else if( property_exists( __CLASS__, $setting ) ){
-                       self::$$setting = $value;
-            self::$config_name_sum[ $setting ] = $value; // take trace of all config
-        }
-       }
-
-
-
-       // check if has to compile the template
-       // return true if the template has changed
-       protected function check_template( $tpl_name ){
-
-               if( !isset($this->tpl['checked']) ){
-
-                       $tpl_basename                       = basename( $tpl_name );                                                                                                            // template basename
-                       $tpl_basedir                        = strpos($tpl_name,"/") ? dirname($tpl_name) . '/' : null;                                          // template basedirectory
-                       $tpl_dir                            = self::$tpl_dir . $tpl_basedir;                                                            // template directory
-                       $this->tpl['tpl_filename']          = $tpl_dir . $tpl_basename . '.' . self::$tpl_ext;  // template filename
-                       $temp_compiled_filename             = self::$cache_dir . $tpl_basename . "." . md5( $tpl_dir . serialize(self::$config_name_sum));
-                       $this->tpl['compiled_filename']     = $temp_compiled_filename . '.rtpl.php';    // cache filename
-                       $this->tpl['cache_filename']        = $temp_compiled_filename . '.s_' . $this->cache_id . '.rtpl.php';  // static cache filename
-
-                       // if the template doesn't exsist throw an error
-                       if( self::$check_template_update && !file_exists( $this->tpl['tpl_filename'] ) ){
-                               $e = new RainTpl_NotFoundException( 'Template '. $tpl_basename .' not found!' );
-                               throw $e->setTemplateFile($this->tpl['tpl_filename']);
-                       }
-
-                       // file doesn't exsist, or the template was updated, Rain will compile the template
-                       if( !file_exists( $this->tpl['compiled_filename'] ) || ( self::$check_template_update && filemtime($this->tpl['compiled_filename']) < filemtime( $this->tpl['tpl_filename'] ) ) ){
-                               $this->compileFile( $tpl_basename, $tpl_basedir, $this->tpl['tpl_filename'], self::$cache_dir, $this->tpl['compiled_filename'] );
-                               return true;
-                       }
-                       $this->tpl['checked'] = true;
-               }
-       }
-
-
-       /**
-       * execute stripslaches() on the xml block. Invoqued by preg_replace_callback function below
-       * @access protected
-       */
-       protected function xml_reSubstitution($capture) {
-               return "<?php echo '<?xml ".stripslashes($capture[1])." ?>'; ?>";
-       }
-
-       /**
-        * Compile and write the compiled template file
-        * @access protected
-        */
-       protected function compileFile( $tpl_basename, $tpl_basedir, $tpl_filename, $cache_dir, $compiled_filename ){
-
-               //read template file
-               $this->tpl['source'] = $template_code = file_get_contents( $tpl_filename );
-
-               //xml substitution
-               $template_code = preg_replace( "/<\?xml(.*?)\?>/s", "##XML\\1XML##", $template_code );
-
-               //disable php tag
-               if( !self::$php_enabled )
-                       $template_code = str_replace( array("<?","?>"), array("&lt;?","?&gt;"), $template_code );
-
-               //xml re-substitution
-               $template_code = preg_replace_callback ( "/##XML(.*?)XML##/s", array($this, 'xml_reSubstitution'), $template_code );
-
-               //compile template
-               $template_compiled = "<?php if(!class_exists('raintpl')){exit;}?>" . $this->compileTemplate( $template_code, $tpl_basedir );
-
-
-               // fix the php-eating-newline-after-closing-tag-problem
-               $template_compiled = str_replace( "?>\n", "?>\n\n", $template_compiled );
-
-               // create directories
-               if( !is_dir( $cache_dir ) )
-                       mkdir( $cache_dir, 0755, true );
-
-               if( !is_writable( $cache_dir ) )
-                       throw new RainTpl_Exception ('Cache directory ' . $cache_dir . 'doesn\'t have write permission. Set write permission or set RAINTPL_CHECK_TEMPLATE_UPDATE to false. More details on http://www.raintpl.com/Documentation/Documentation-for-PHP-developers/Configuration/');
-
-               //write compiled file
-               file_put_contents( $compiled_filename, $template_compiled );
-       }
-
-
-
-       /**
-        * Compile template
-        * @access protected
-        */
-       protected function compileTemplate( $template_code, $tpl_basedir ){
-
-               //tag list
-               $tag_regexp = array( 'loop'         => '(\{loop(?: name){0,1}="\${0,1}[^"]*"\})',
-                             'loop_close'   => '(\{\/loop\})',
-                             'if'           => '(\{if(?: condition){0,1}="[^"]*"\})',
-                             'elseif'       => '(\{elseif(?: condition){0,1}="[^"]*"\})',
-                             'else'         => '(\{else\})',
-                             'if_close'     => '(\{\/if\})',
-                             'function'     => '(\{function="[^"]*"\})',
-                             'noparse'      => '(\{noparse\})',
-                             'noparse_close'=> '(\{\/noparse\})',
-                             'ignore'       => '(\{ignore\}|\{\*)',
-                             'ignore_close'    => '(\{\/ignore\}|\*\})',
-                             'include'      => '(\{include="[^"]*"(?: cache="[^"]*")?\})',
-                             'template_info'=> '(\{\$template_info\})',
-                             'function'                => '(\{function="(\w*?)(?:.*?)"\})'
-                                                       );
-
-               $tag_regexp = "/" . join( "|", $tag_regexp ) . "/";
-
-               //split the code with the tags regexp
-               $template_code = preg_split ( $tag_regexp, $template_code, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY );
-
-               //path replace (src of img, background and href of link)
-               $template_code = $this->path_replace( $template_code, $tpl_basedir );
-
-               //compile the code
-               $compiled_code = $this->compileCode( $template_code );
-
-               //return the compiled code
-               return $compiled_code;
-
-       }
-
-
-
-       /**
-        * Compile the code
-        * @access protected
-        */
-       protected function compileCode( $parsed_code ){
-
-               //variables initialization
-               $compiled_code = $open_if = $comment_is_open = $ignore_is_open = null;
-        $loop_level = 0;
-
-               //read all parsed code
-               while( $html = array_shift( $parsed_code ) ){
-
-                       //close ignore tag
-                       if( !$comment_is_open && ( strpos( $html, '{/ignore}' ) !== FALSE || strpos( $html, '*}' ) !== FALSE ) )
-                               $ignore_is_open = false;
-
-                       //code between tag ignore id deleted
-                       elseif( $ignore_is_open ){
-                               //ignore the code
-                       }
-
-                       //close no parse tag
-                       elseif( strpos( $html, '{/noparse}' ) !== FALSE )
-                               $comment_is_open = false;
-
-                       //code between tag noparse is not compiled
-                       elseif( $comment_is_open )
-                               $compiled_code .= $html;
-
-                       //ignore
-                       elseif( strpos( $html, '{ignore}' ) !== FALSE || strpos( $html, '{*' ) !== FALSE )
-                               $ignore_is_open = true;
-
-                       //noparse
-                       elseif( strpos( $html, '{noparse}' ) !== FALSE )
-                               $comment_is_open = true;
-
-                       //include tag
-                       elseif( preg_match( '/\{include="([^"]*)"(?: cache="([^"]*)"){0,1}\}/', $html, $code ) ){
-
-                               //variables substitution
-                               $include_var = $this->var_replace( $code[ 1 ], $left_delimiter = null, $right_delimiter = null, $php_left_delimiter = '".' , $php_right_delimiter = '."', $loop_level );
-
-                               // if the cache is active
-                               if( isset($code[ 2 ]) ){
-
-                                       //dynamic include
-                                       $compiled_code .= '<?php $tpl = new '.get_class($this).';' .
-                                                                'if( $cache = $tpl->cache( $template = basename("'.$include_var.'") ) )' .
-                                                                '      echo $cache;' .
-                                                                'else{' .
-                                                                '      $tpl_dir_temp = self::$tpl_dir;' .
-                                                                '      $tpl->assign( $this->var );' .
-                                                                       ( !$loop_level ? null : '$tpl->assign( "key", $key'.$loop_level.' ); $tpl->assign( "value", $value'.$loop_level.' );' ).
-                                                                '      $tpl->draw( dirname("'.$include_var.'") . ( substr("'.$include_var.'",-1,1) != "/" ? "/" : "" ) . basename("'.$include_var.'") );'.
-                                                                '} ?>';
-                               }
-                               else{
-
-                                       //dynamic include
-                                       $compiled_code .= '<?php $tpl = new '.get_class($this).';' .
-                                                                         '$tpl_dir_temp = self::$tpl_dir;' .
-                                                                         '$tpl->assign( $this->var );' .
-                                                                         ( !$loop_level ? null : '$tpl->assign( "key", $key'.$loop_level.' ); $tpl->assign( "value", $value'.$loop_level.' );' ).
-                                                                         '$tpl->draw( dirname("'.$include_var.'") . ( substr("'.$include_var.'",-1,1) != "/" ? "/" : "" ) . basename("'.$include_var.'") );'.
-                                                                         '?>';
-
-
-                               }
-
-                       }
-
-                       //loop
-                       elseif( preg_match( '/\{loop(?: name){0,1}="\${0,1}([^"]*)"\}/', $html, $code ) ){
-
-                               //increase the loop counter
-                               $loop_level++;
-
-                               //replace the variable in the loop
-                               $var = $this->var_replace( '$' . $code[ 1 ], $tag_left_delimiter=null, $tag_right_delimiter=null, $php_left_delimiter=null, $php_right_delimiter=null, $loop_level-1 );
-
-                               //loop variables
-                               $counter = "\$counter$loop_level";       // count iteration
-                               $key = "\$key$loop_level";               // key
-                               $value = "\$value$loop_level";           // value
-
-                               //loop code
-                               $compiled_code .=  "<?php $counter=-1; if( isset($var) && is_array($var) && sizeof($var) ) foreach( $var as $key => $value ){ $counter++; ?>";
-
-                       }
-
-                       //close loop tag
-                       elseif( strpos( $html, '{/loop}' ) !== FALSE ) {
-
-                               //iterator
-                               $counter = "\$counter$loop_level";
-
-                               //decrease the loop counter
-                               $loop_level--;
-
-                               //close loop code
-                               $compiled_code .=  "<?php } ?>";
-
-                       }
-
-                       //if
-                       elseif( preg_match( '/\{if(?: condition){0,1}="([^"]*)"\}/', $html, $code ) ){
-
-                               //increase open if counter (for intendation)
-                               $open_if++;
-
-                               //tag
-                               $tag = $code[ 0 ];
-
-                               //condition attribute
-                               $condition = $code[ 1 ];
-
-                               // check if there's any function disabled by black_list
-                               $this->function_check( $tag );
-
-                               //variable substitution into condition (no delimiter into the condition)
-                               $parsed_condition = $this->var_replace( $condition, $tag_left_delimiter = null, $tag_right_delimiter = null, $php_left_delimiter = null, $php_right_delimiter = null, $loop_level );
-
-                               //if code
-                               $compiled_code .=   "<?php if( $parsed_condition ){ ?>";
-
-                       }
-
-                       //elseif
-                       elseif( preg_match( '/\{elseif(?: condition){0,1}="([^"]*)"\}/', $html, $code ) ){
-
-                               //tag
-                               $tag = $code[ 0 ];
-
-                               //condition attribute
-                               $condition = $code[ 1 ];
-
-                               //variable substitution into condition (no delimiter into the condition)
-                               $parsed_condition = $this->var_replace( $condition, $tag_left_delimiter = null, $tag_right_delimiter = null, $php_left_delimiter = null, $php_right_delimiter = null, $loop_level );
-
-                               //elseif code
-                               $compiled_code .=   "<?php }elseif( $parsed_condition ){ ?>";
-                       }
-
-                       //else
-                       elseif( strpos( $html, '{else}' ) !== FALSE ) {
-
-                               //else code
-                               $compiled_code .=   '<?php }else{ ?>';
-
-                       }
-
-                       //close if tag
-                       elseif( strpos( $html, '{/if}' ) !== FALSE ) {
-
-                               //decrease if counter
-                               $open_if--;
-
-                               // close if code
-                               $compiled_code .=   '<?php } ?>';
-
-                       }
-
-                       //function
-                       elseif( preg_match( '/\{function="(\w*)(.*?)"\}/', $html, $code ) ){
-
-                               //tag
-                               $tag = $code[ 0 ];
-
-                               //function
-                               $function = $code[ 1 ];
-
-                               // check if there's any function disabled by black_list
-                               $this->function_check( $tag );
-
-                               if( empty( $code[ 2 ] ) )
-                                       $parsed_function = $function . "()";
-                               else
-                                       // parse the function
-                                       $parsed_function = $function . $this->var_replace( $code[ 2 ], $tag_left_delimiter = null, $tag_right_delimiter = null, $php_left_delimiter = null, $php_right_delimiter = null, $loop_level );
-
-                               //if code
-                               $compiled_code .=   "<?php echo $parsed_function; ?>";
-                       }
-
-                       // show all vars
-                       elseif ( strpos( $html, '{$template_info}' ) !== FALSE ) {
-
-                               //tag
-                               $tag  = '{$template_info}';
-
-                               //if code
-                               $compiled_code .=   '<?php echo "<pre>"; print_r( $this->var ); echo "</pre>"; ?>';
-                       }
-
-
-                       //all html code
-                       else{
-
-                               //variables substitution (es. {$title})
-                               $html = $this->var_replace( $html, $left_delimiter = '\{', $right_delimiter = '\}', $php_left_delimiter = '<?php ', $php_right_delimiter = ';?>', $loop_level, $echo = true );
-                               //const substitution (es. {#CONST#})
-                               $html = $this->const_replace( $html, $left_delimiter = '\{', $right_delimiter = '\}', $php_left_delimiter = '<?php ', $php_right_delimiter = ';?>', $loop_level, $echo = true );
-                               //functions substitution (es. {"string"|functions})
-                               $compiled_code .= $this->func_replace( $html, $left_delimiter = '\{', $right_delimiter = '\}', $php_left_delimiter = '<?php ', $php_right_delimiter = ';?>', $loop_level, $echo = true );
-                       }
-               }
-
-               if( $open_if > 0 ) {
-                       $e = new RainTpl_SyntaxException('Error! You need to close an {if} tag in ' . $this->tpl['tpl_filename'] . ' template');
-                       throw $e->setTemplateFile($this->tpl['tpl_filename']);
-               }
-               return $compiled_code;
-       }
-
-
-       /**
-        * Reduce a path, eg. www/library/../filepath//file => www/filepath/file
-        * @param type $path
-        * @return type
-        */
-       protected function reduce_path( $path ){
-               $path = str_replace( "://", "@not_replace@", $path );
-               $path = str_replace( "//", "/", $path );
-               $path = str_replace( "@not_replace@", "://", $path );
-               return preg_replace('/\w+\/\.\.\//', '', $path );
-       }
-
-
-
-       /**
-        * replace the path of image src, link href and a href.
-        * url => template_dir/url
-        * url# => url
-        * http://url => http://url
-        *
-        * @param string $html
-        * @return string html sostituito
-        */
-       protected function path_replace( $html, $tpl_basedir ){
-
-               if( self::$path_replace ){
-
-                       $tpl_dir = self::$base_url . self::$tpl_dir . $tpl_basedir;
-
-                       // reduce the path
-                       $path = $this->reduce_path($tpl_dir);
-
-                       $exp = $sub = array();
-
-                       if( in_array( "img", self::$path_replace_list ) ){
-                               $exp = array( '/<img(.*?)src=(?:")(http|https)\:\/\/([^"]+?)(?:")/i', '/<img(.*?)src=(?:")([^"]+?)#(?:")/i', '/<img(.*?)src="(.*?)"/', '/<img(.*?)src=(?:\@)([^"]+?)(?:\@)/i' );
-                               $sub = array( '<img$1src=@$2://$3@', '<img$1src=@$2@', '<img$1src="' . $path . '$2"', '<img$1src="$2"' );
-                       }
-
-                       if( in_array( "script", self::$path_replace_list ) ){
-                               $exp = array_merge( $exp , array( '/<script(.*?)src=(?:")(http|https)\:\/\/([^"]+?)(?:")/i', '/<script(.*?)src=(?:")([^"]+?)#(?:")/i', '/<script(.*?)src="(.*?)"/', '/<script(.*?)src=(?:\@)([^"]+?)(?:\@)/i' ) );
-                               $sub = array_merge( $sub , array( '<script$1src=@$2://$3@', '<script$1src=@$2@', '<script$1src="' . $path . '$2"', '<script$1src="$2"' ) );
-                       }
-
-                       if( in_array( "link", self::$path_replace_list ) ){
-                               $exp = array_merge( $exp , array( '/<link(.*?)href=(?:")(http|https)\:\/\/([^"]+?)(?:")/i', '/<link(.*?)href=(?:")([^"]+?)#(?:")/i', '/<link(.*?)href="(.*?)"/', '/<link(.*?)href=(?:\@)([^"]+?)(?:\@)/i' ) );
-                               $sub = array_merge( $sub , array( '<link$1href=@$2://$3@', '<link$1href=@$2@' , '<link$1href="' . $path . '$2"', '<link$1href="$2"' ) );
-                       }
-
-                       if( in_array( "a", self::$path_replace_list ) ){
-                               $exp = array_merge( $exp , array( '/<a(.*?)href=(?:")(http\:\/\/|https\:\/\/|javascript:)([^"]+?)(?:")/i', '/<a(.*?)href="(.*?)"/', '/<a(.*?)href=(?:\@)([^"]+?)(?:\@)/i'  ) );
-                               $sub = array_merge( $sub , array( '<a$1href=@$2$3@', '<a$1href="' . self::$base_url . '$2"', '<a$1href="$2"' ) );
-                       }
-
-                       if( in_array( "input", self::$path_replace_list ) ){
-                               $exp = array_merge( $exp , array( '/<input(.*?)src=(?:")(http|https)\:\/\/([^"]+?)(?:")/i', '/<input(.*?)src=(?:")([^"]+?)#(?:")/i', '/<input(.*?)src="(.*?)"/', '/<input(.*?)src=(?:\@)([^"]+?)(?:\@)/i' ) );
-                               $sub = array_merge( $sub , array( '<input$1src=@$2://$3@', '<input$1src=@$2@', '<input$1src="' . $path . '$2"', '<input$1src="$2"' ) );
-                       }
-
-                       return preg_replace( $exp, $sub, $html );
-
-               }
-               else
-                       return $html;
-
-       }
-
-
-
-
-
-       // replace const
-       function const_replace( $html, $tag_left_delimiter, $tag_right_delimiter, $php_left_delimiter = null, $php_right_delimiter = null, $loop_level = null, $echo = null ){
-               // const
-               return preg_replace( '/\{\#(\w+)\#{0,1}\}/', $php_left_delimiter . ( $echo ? " echo " : null ) . '\\1' . $php_right_delimiter, $html );
-       }
-
-
-
-       // replace functions/modifiers on constants and strings
-       function func_replace( $html, $tag_left_delimiter, $tag_right_delimiter, $php_left_delimiter = null, $php_right_delimiter = null, $loop_level = null, $echo = null ){
-
-               preg_match_all( '/' . '\{\#{0,1}(\"{0,1}.*?\"{0,1})(\|\w.*?)\#{0,1}\}' . '/', $html, $matches );
-
-               for( $i=0, $n=count($matches[0]); $i<$n; $i++ ){
-
-                       //complete tag ex: {$news.title|substr:0,100}
-                       $tag = $matches[ 0 ][ $i ];
-
-                       //variable name ex: news.title
-                       $var = $matches[ 1 ][ $i ];
-
-                       //function and parameters associate to the variable ex: substr:0,100
-                       $extra_var = $matches[ 2 ][ $i ];
-
-                       // check if there's any function disabled by black_list
-                       $this->function_check( $tag );
-
-                       $extra_var = $this->var_replace( $extra_var, null, null, null, null, $loop_level );
-
-
-                       // check if there's an operator = in the variable tags, if there's this is an initialization so it will not output any value
-                       $is_init_variable = preg_match( "/^(\s*?)\=[^=](.*?)$/", $extra_var );
-
-                       //function associate to variable
-                       $function_var = ( $extra_var and $extra_var[0] == '|') ? substr( $extra_var, 1 ) : null;
-
-                       //variable path split array (ex. $news.title o $news[title]) or object (ex. $news->title)
-                       $temp = preg_split( "/\.|\[|\-\>/", $var );
-
-                       //variable name
-                       $var_name = $temp[ 0 ];
-
-                       //variable path
-                       $variable_path = substr( $var, strlen( $var_name ) );
-
-                       //parentesis transform [ e ] in [" e in "]
-                       $variable_path = str_replace( '[', '["', $variable_path );
-                       $variable_path = str_replace( ']', '"]', $variable_path );
-
-                       //transform .$variable in ["$variable"]
-                       $variable_path = preg_replace('/\.\$(\w+)/', '["$\\1"]', $variable_path );
-
-                       //transform [variable] in ["variable"]
-                       $variable_path = preg_replace('/\.(\w+)/', '["\\1"]', $variable_path );
-
-                       //if there's a function
-                       if( $function_var ){
-
-                // check if there's a function or a static method and separate, function by parameters
-                               $function_var = str_replace("::", "@double_dot@", $function_var );
-
-                // get the position of the first :
-                if( $dot_position = strpos( $function_var, ":" ) ){
-
-                    // get the function and the parameters
-                    $function = substr( $function_var, 0, $dot_position );
-                    $params = substr( $function_var, $dot_position+1 );
-
-                }
-                else{
-
-                    //get the function
-                    $function = str_replace( "@double_dot@", "::", $function_var );
-                    $params = null;
-
-                }
-
-                // replace back the @double_dot@ with ::
-                $function = str_replace( "@double_dot@", "::", $function );
-                $params = str_replace( "@double_dot@", "::", $params );
-
-
-                       }
-                       else
-                               $function = $params = null;
-
-                       $php_var = $var_name . $variable_path;
-
-                       // compile the variable for php
-                       if( isset( $function ) ){
-                               if( $php_var )
-                                       $php_var = $php_left_delimiter . ( !$is_init_variable && $echo ? 'echo ' : null ) . ( $params ? "( $function( $php_var, $params ) )" : "$function( $php_var )" ) . $php_right_delimiter;
-                               else
-                                       $php_var = $php_left_delimiter . ( !$is_init_variable && $echo ? 'echo ' : null ) . ( $params ? "( $function( $params ) )" : "$function()" ) . $php_right_delimiter;
-                       }
-                       else
-                               $php_var = $php_left_delimiter . ( !$is_init_variable && $echo ? 'echo ' : null ) . $php_var . $extra_var . $php_right_delimiter;
-
-                       $html = str_replace( $tag, $php_var, $html );
-
-               }
-
-               return $html;
-
-       }
-
-
-
-       function var_replace( $html, $tag_left_delimiter, $tag_right_delimiter, $php_left_delimiter = null, $php_right_delimiter = null, $loop_level = null, $echo = null ){
-
-               //all variables
-               if( preg_match_all( '/' . $tag_left_delimiter . '\$(\w+(?:\.\${0,1}[A-Za-z0-9_]+)*(?:(?:\[\${0,1}[A-Za-z0-9_]+\])|(?:\-\>\${0,1}[A-Za-z0-9_]+))*)(.*?)' . $tag_right_delimiter . '/', $html, $matches ) ){
-
-                    for( $parsed=array(), $i=0, $n=count($matches[0]); $i<$n; $i++ )
-                        $parsed[$matches[0][$i]] = array('var'=>$matches[1][$i],'extra_var'=>$matches[2][$i]);
-
-                    foreach( $parsed as $tag => $array ){
-
-                            //variable name ex: news.title
-                            $var = $array['var'];
-
-                            //function and parameters associate to the variable ex: substr:0,100
-                            $extra_var = $array['extra_var'];
-
-                            // check if there's any function disabled by black_list
-                            $this->function_check( $tag );
-
-                            $extra_var = $this->var_replace( $extra_var, null, null, null, null, $loop_level );
-
-                            // check if there's an operator = in the variable tags, if there's this is an initialization so it will not output any value
-                            $is_init_variable = preg_match( "/^[a-z_A-Z\.\[\](\-\>)]*=[^=]*$/", $extra_var );
-
-                            //function associate to variable
-                            $function_var = ( $extra_var and $extra_var[0] == '|') ? substr( $extra_var, 1 ) : null;
-
-                            //variable path split array (ex. $news.title o $news[title]) or object (ex. $news->title)
-                            $temp = preg_split( "/\.|\[|\-\>/", $var );
-
-                            //variable name
-                            $var_name = $temp[ 0 ];
-
-                            //variable path
-                            $variable_path = substr( $var, strlen( $var_name ) );
-
-                            //parentesis transform [ e ] in [" e in "]
-                            $variable_path = str_replace( '[', '["', $variable_path );
-                            $variable_path = str_replace( ']', '"]', $variable_path );
-
-                            //transform .$variable in ["$variable"] and .variable in ["variable"]
-                            $variable_path = preg_replace('/\.(\${0,1}\w+)/', '["\\1"]', $variable_path );
-
-                            // if is an assignment also assign the variable to $this->var['value']
-                            if( $is_init_variable )
-                                $extra_var = "=\$this->var['{$var_name}']{$variable_path}" . $extra_var;
-
-
-
-                            //if there's a function
-                            if( $function_var ){
-
-                                    // check if there's a function or a static method and separate, function by parameters
-                                    $function_var = str_replace("::", "@double_dot@", $function_var );
-
-
-                                    // get the position of the first :
-                                    if( $dot_position = strpos( $function_var, ":" ) ){
-
-                                        // get the function and the parameters
-                                        $function = substr( $function_var, 0, $dot_position );
-                                        $params = substr( $function_var, $dot_position+1 );
-
-                                    }
-                                    else{
-
-                                        //get the function
-                                        $function = str_replace( "@double_dot@", "::", $function_var );
-                                        $params = null;
-
-                                    }
-
-                                    // replace back the @double_dot@ with ::
-                                    $function = str_replace( "@double_dot@", "::", $function );
-                                    $params = str_replace( "@double_dot@", "::", $params );
-                            }
-                            else
-                                    $function = $params = null;
-
-                            //if it is inside a loop
-                            if( $loop_level ){
-                                    //verify the variable name
-                                    if( $var_name == 'key' )
-                                            $php_var = '$key' . $loop_level;
-                                    elseif( $var_name == 'value' )
-                                            $php_var = '$value' . $loop_level . $variable_path;
-                                    elseif( $var_name == 'counter' )
-                                            $php_var = '$counter' . $loop_level;
-                                    else
-                                            $php_var = '$' . $var_name . $variable_path;
-                            }else
-                                    $php_var = '$' . $var_name . $variable_path;
-
-                            // compile the variable for php
-                            if( isset( $function ) )
-                                    $php_var = $php_left_delimiter . ( !$is_init_variable && $echo ? 'echo ' : null ) . ( $params ? "( $function( $php_var, $params ) )" : "$function( $php_var )" ) . $php_right_delimiter;
-                            else
-                                    $php_var = $php_left_delimiter . ( !$is_init_variable && $echo ? 'echo ' : null ) . $php_var . $extra_var . $php_right_delimiter;
-
-                            $html = str_replace( $tag, $php_var, $html );
-
-
-                    }
-                }
-
-               return $html;
-       }
-
-
-
-       /**
-        * Check if function is in black list (sandbox)
-        *
-        * @param string $code
-        * @param string $tag
-        */
-       protected function function_check( $code ){
-
-               $preg = '#(\W|\s)' . implode( '(\W|\s)|(\W|\s)', self::$black_list ) . '(\W|\s)#';
-
-               // check if the function is in the black list (or not in white list)
-               if( count(self::$black_list) && preg_match( $preg, $code, $match ) ){
-
-                       // find the line of the error
-                       $line = 0;
-                       $rows=explode("\n",$this->tpl['source']);
-                       while( !strpos($rows[$line],$code) )
-                               $line++;
-
-                       // stop the execution of the script
-                       $e = new RainTpl_SyntaxException('Unallowed syntax in ' . $this->tpl['tpl_filename'] . ' template');
-                       throw $e->setTemplateFile($this->tpl['tpl_filename'])
-                               ->setTag($code)
-                               ->setTemplateLine($line);
-               }
-
-       }
-
-       /**
-        * Prints debug info about exception or passes it further if debug is disabled.
-        *
-        * @param RainTpl_Exception $e
-        * @return string
-        */
-       protected function printDebug(RainTpl_Exception $e){
-               if (!self::$debug) {
-                       throw $e;
-               }
-               $output = sprintf('<h2>Exception: %s</h2><h3>%s</h3><p>template: %s</p>',
-                       get_class($e),
-                       $e->getMessage(),
-                       $e->getTemplateFile()
-               );
-               if ($e instanceof RainTpl_SyntaxException) {
-                       if (null != $e->getTemplateLine()) {
-                               $output .= '<p>line: ' . $e->getTemplateLine() . '</p>';
-                       }
-                       if (null != $e->getTag()) {
-                               $output .= '<p>in tag: ' . htmlspecialchars($e->getTag()) . '</p>';
-                       }
-                       if (null != $e->getTemplateLine() && null != $e->getTag()) {
-                               $rows=explode("\n",  htmlspecialchars($this->tpl['source']));
-                               $rows[$e->getTemplateLine()] = '<font color=red>' . $rows[$e->getTemplateLine()] . '</font>';
-                               $output .= '<h3>template code</h3>' . implode('<br />', $rows) . '</pre>';
-                       }
-               }
-               $output .= sprintf('<h3>trace</h3><p>In %s on line %d</p><pre>%s</pre>',
-                       $e->getFile(), $e->getLine(),
-                       nl2br(htmlspecialchars($e->getTraceAsString()))
-               );
-               return $output;
-       }
-}
-
-
-/**
- * Basic Rain tpl exception.
- */
-class RainTpl_Exception extends Exception{
-       /**
-        * Path of template file with error.
-        */
-       protected $templateFile = '';
-
-       /**
-        * Returns path of template file with error.
-        *
-        * @return string
-        */
-       public function getTemplateFile()
-       {
-               return $this->templateFile;
-       }
-
-       /**
-        * Sets path of template file with error.
-        *
-        * @param string $templateFile
-        * @return RainTpl_Exception
-        */
-       public function setTemplateFile($templateFile)
-       {
-               $this->templateFile = (string) $templateFile;
-               return $this;
-       }
-}
-
-/**
- * Exception thrown when template file does not exists.
- */
-class RainTpl_NotFoundException extends RainTpl_Exception{
-}
-
-/**
- * Exception thrown when syntax error occurs.
- */
-class RainTpl_SyntaxException extends RainTpl_Exception{
-       /**
-        * Line in template file where error has occured.
-        *
-        * @var int | null
-        */
-       protected $templateLine = null;
-
-       /**
-        * Tag which caused an error.
-        *
-        * @var string | null
-        */
-       protected $tag = null;
-
-       /**
-        * Returns line in template file where error has occured
-        * or null if line is not defined.
-        *
-        * @return int | null
-        */
-       public function getTemplateLine()
-       {
-               return $this->templateLine;
-       }
-
-       /**
-        * Sets  line in template file where error has occured.
-        *
-        * @param int $templateLine
-        * @return RainTpl_SyntaxException
-        */
-       public function setTemplateLine($templateLine)
-       {
-               $this->templateLine = (int) $templateLine;
-               return $this;
-       }
-
-       /**
-        * Returns tag which caused an error.
-        *
-        * @return string
-        */
-       public function getTag()
-       {
-               return $this->tag;
-       }
-
-       /**
-        * Sets tag which caused an error.
-        *
-        * @param string $tag
-        * @return RainTpl_SyntaxException
-        */
-       public function setTag($tag)
-       {
-               $this->tag = (string) $tag;
-               return $this;
-       }
-}
-
-// -- end
diff --git a/inc/store/file.class.php b/inc/store/file.class.php
deleted file mode 100644 (file)
index ad20937..0000000
+++ /dev/null
@@ -1,51 +0,0 @@
-<?php
-/**
- * poche, a read it later open source system
- *
- * @category   poche
- * @author     Nicolas Lœuillet <support@inthepoche.com>
- * @copyright  2013
- * @license    http://www.wtfpl.net/ see COPYING file
- */
-
-class File extends Store {
-    function __construct() {
-
-    }
-
-    public function add() {
-
-    }
-
-    public function retrieveOneById($id) {
-
-    }
-
-    public function retrieveOneByURL($url) {
-
-    }
-
-    public function deleteById($id) {
-
-    }
-
-    public function favoriteById($id) {
-
-    }
-
-    public function archiveById($id) {
-
-    }
-
-    public function getEntriesByView($view) {
-
-    }
-
-    public function getLastId() {
-
-    }
-
-    public function updateContentById($id) {
-
-    }
-}
diff --git a/inc/store/sqlite.class.php b/inc/store/sqlite.class.php
deleted file mode 100644 (file)
index 2108160..0000000
+++ /dev/null
@@ -1,202 +0,0 @@
-<?php
-/**
- * poche, a read it later open source system
- *
- * @category   poche
- * @author     Nicolas Lœuillet <support@inthepoche.com>
- * @copyright  2013
- * @license    http://www.wtfpl.net/ see COPYING file
- */
-
-class Sqlite extends Store {
-
-    public static $db_path = 'sqlite:./db/poche.sqlite';
-    var $handle;
-
-    function __construct() {
-        parent::__construct();
-
-        $this->handle = new PDO(self::$db_path);
-        $this->handle->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
-    }
-
-    private function getHandle() {
-        return $this->handle;
-    }
-
-    public function isInstalled() {
-        $sql        = "SELECT name FROM sqlite_sequence WHERE name=?";
-        $query      = $this->executeQuery($sql, array('config'));
-        $hasConfig  = $query->fetchAll();
-
-        if (count($hasConfig) == 0) 
-            return FALSE;
-
-        if (!$this->getLogin() || !$this->getPassword()) 
-            return FALSE;
-
-        return TRUE;
-    }
-
-    public function install($login, $password) {
-        $this->getHandle()->exec('CREATE TABLE IF NOT EXISTS "config" ("id" INTEGER PRIMARY KEY  AUTOINCREMENT  NOT NULL  UNIQUE , "name" VARCHAR UNIQUE, "value" BLOB)');
-
-        $this->handle->exec('CREATE TABLE IF NOT EXISTS "entries" ("id" INTEGER PRIMARY KEY  AUTOINCREMENT  NOT NULL  UNIQUE , "title" VARCHAR, "url" VARCHAR UNIQUE , "is_read" INTEGER DEFAULT 0, "is_fav" INTEGER DEFAULT 0, "content" BLOB)');
-
-        if (!$this->getLogin()) {
-            $sql_login     = 'INSERT INTO config ( name, value ) VALUES (?, ?)';
-            $params_login  = array('login', $login);
-            $query         = $this->executeQuery($sql_login, $params_login);
-        }
-
-        if (!$this->getPassword()) {
-            $sql_pass     = 'INSERT INTO config ( name, value ) VALUES (?, ?)';
-            $params_pass  = array('password', $password);
-            $query        = $this->executeQuery($sql_pass, $params_pass);
-        }
-
-        return TRUE;
-    }
-
-    public function getLogin() {
-        $sql    = "SELECT value FROM config WHERE name=?";
-        $query  = $this->executeQuery($sql, array('login'));
-        $login  = $query->fetchAll();
-
-        return isset($login[0]['value']) ? $login[0]['value'] : FALSE;
-    }
-
-    public function getPassword() {
-        $sql    = "SELECT value FROM config WHERE name=?";
-        $query  = $this->executeQuery($sql, array('password'));
-        $pass   = $query->fetchAll();
-
-        return isset($pass[0]['value']) ? $pass[0]['value'] : FALSE; 
-    }
-
-    public function updatePassword($password)
-    {
-        $sql_update     = "UPDATE config SET value=? WHERE name='password'";
-        $params_update  = array($password);
-        $query          = $this->executeQuery($sql_update, $params_update);
-    }
-
-    private function executeQuery($sql, $params) {
-        try
-        {
-            $query = $this->getHandle()->prepare($sql);
-            $query->execute($params);
-            return $query;
-        }
-        catch (Exception $e)
-        {
-            logm('execute query error : '.$e->getMessage());
-        }
-    }
-
-    public function retrieveAll() {
-        $sql        = "SELECT * FROM entries ORDER BY id";
-        $query      = $this->executeQuery($sql, array());
-        $entries    = $query->fetchAll();
-
-        return $entries;
-    }
-
-    public function retrieveOneById($id) {
-        parent::__construct();
-
-        $entry  = NULL;
-        $sql    = "SELECT * FROM entries WHERE id=?";
-        $params = array(intval($id));
-        $query  = $this->executeQuery($sql, $params);
-        $entry  = $query->fetchAll();
-
-        return $entry[0];
-    }
-
-    public function getEntriesByView($view) {
-        parent::__construct();
-
-        switch ($_SESSION['sort'])
-        {
-            case 'ia':
-                $order = 'ORDER BY id';
-                break;
-            case 'id':
-                $order = 'ORDER BY id DESC';
-                break;
-            case 'ta':
-                $order = 'ORDER BY lower(title)';
-                break;
-            case 'td':
-                $order = 'ORDER BY lower(title) DESC';
-                break;
-            default:
-                $order = 'ORDER BY id';
-                break;
-        }
-
-        switch ($view)
-        {
-            case 'archive':
-                $sql    = "SELECT * FROM entries WHERE is_read=? " . $order;
-                $params = array(-1);
-                break;
-            case 'fav' :
-                $sql    = "SELECT * FROM entries WHERE is_fav=? " . $order;
-                $params = array(-1);
-                break;
-            default:
-                $sql    = "SELECT * FROM entries WHERE is_read=? " . $order;
-                $params = array(0);
-                break;
-        }
-
-        $query      = $this->executeQuery($sql, $params);
-        $entries    = $query->fetchAll();
-
-        return $entries;
-    }
-
-    public function add($url, $title, $content) {
-        parent::__construct();
-        $sql_action     = 'INSERT INTO entries ( url, title, content ) VALUES (?, ?, ?)';
-        $params_action  = array($url, $title, $content);
-        $query          = $this->executeQuery($sql_action, $params_action);
-        return $query;
-    }
-
-    public function deleteById($id) {
-        parent::__construct();
-        $sql_action     = "DELETE FROM entries WHERE id=?";
-        $params_action  = array($id);
-        $query          = $this->executeQuery($sql_action, $params_action);
-        return $query;
-    }
-
-    public function favoriteById($id) {
-        parent::__construct();
-        $sql_action     = "UPDATE entries SET is_fav=~is_fav WHERE id=?";
-        $params_action  = array($id);
-        $query          = $this->executeQuery($sql_action, $params_action);
-    }
-
-    public function archiveById($id) {
-        parent::__construct();
-        $sql_action     = "UPDATE entries SET is_read=~is_read WHERE id=?";
-        $params_action  = array($id);
-        $query          = $this->executeQuery($sql_action, $params_action);
-    }
-
-    public function getLastId() {
-        parent::__construct();
-        return $this->getHandle()->lastInsertId();
-    }
-
-    public function updateContentById($id) {
-        parent::__construct();
-        $sql_update     = "UPDATE entries SET content=? WHERE id=?";
-        $params_update  = array($content, $id);
-        $query          = $this->executeQuery($sql_update, $params_update);
-    }
-}
diff --git a/inc/store/store.class.php b/inc/store/store.class.php
deleted file mode 100644 (file)
index dd7d4cf..0000000
+++ /dev/null
@@ -1,63 +0,0 @@
-<?php
-/**
- * poche, a read it later open source system
- *
- * @category   poche
- * @author     Nicolas Lœuillet <support@inthepoche.com>
- * @copyright  2013
- * @license    http://www.wtfpl.net/ see COPYING file
- */
-
-class Store {
-    function __construct() {
-
-    }
-
-    public function getLogin() {
-
-    }
-
-    public function getPassword() {
-        
-    }
-
-    public function add() {
-
-    }
-
-    public function retrieveAll() {
-        
-    }
-
-    public function retrieveOneById($id) {
-
-    }
-
-    public function retrieveOneByURL($url) {
-
-    }
-
-    public function deleteById($id) {
-
-    }
-
-    public function favoriteById($id) {
-
-    }
-
-    public function archiveById($id) {
-
-    }
-
-    public function getEntriesByView($view) {
-
-    }
-
-    public function getLastId() {
-
-    }
-
-    public function updateContentById($id) {
-
-    }
-}
index 0a778d0843f2fd20c55be7f1f089f3c1cf537aa4..a60e4e941704c14a64f9a66c24a2cb7b9dea01e8 100644 (file)
--- a/index.php
+++ b/index.php
@@ -8,83 +8,57 @@
  * @license    http://www.wtfpl.net/ see COPYING file
  */
 
-include dirname(__FILE__).'/inc/config.php';
+include dirname(__FILE__).'/inc/poche/config.inc.php';
 
-myTool::initPhp();
-
-# XSRF protection with token
-if (!empty($_POST)) {
-    if (!Session::isToken($_POST['token'])) {
-        die('Wrong token.');
-    }
-    unset($_SESSION['tokens']);
-}
-
-$ref = empty($_SERVER['HTTP_REFERER']) ? '' : $_SERVER['HTTP_REFERER'];
+# Parse GET & REFERER vars
+$referer = empty($_SERVER['HTTP_REFERER']) ? '' : $_SERVER['HTTP_REFERER'];
+$view = Tools::checkVar('view', 'home');
+$action = Tools::checkVar('action');
+$id = Tools::checkVar('id');
+$_SESSION['sort'] = Tools::checkVar('sort', 'id');
+$url = new Url((isset ($_GET['url'])) ? $_GET['url'] : '');
 
+# poche actions
 if (isset($_GET['login'])) {
-    // Login
-    if (!empty($_POST['login']) && !empty($_POST['password'])) {
-        if (Session::login($_SESSION['login'], $_SESSION['pass'], $_POST['login'], encode_string($_POST['password'] . $_POST['login']))) {
-            logm('login successful');
-            $msg->add('s', 'welcome in your poche!');
-            if (!empty($_POST['longlastingsession'])) {
-                $_SESSION['longlastingsession'] = 31536000;
-                $_SESSION['expires_on'] = time() + $_SESSION['longlastingsession'];
-                session_set_cookie_params($_SESSION['longlastingsession']);
-            } else {
-                session_set_cookie_params(0); // when browser closes
-            }
-            session_regenerate_id(true);
-
-            MyTool::redirect($ref);
-        }
-        logm('login failed');
-        die("Login failed !");
-    } else {
-        logm('login failed');
-    }
+    # hello you
+    $poche->login($referer);
 }
 elseif (isset($_GET['logout'])) {
-    logm('logout');
-    Session::logout();
-    MyTool::redirect();
+    # see you soon !
+    $poche->logout();
 }
-elseif  (isset($_GET['config'])) {
-    if (isset($_POST['password']) && isset($_POST['password_repeat'])) {
-        if ($_POST['password'] == $_POST['password_repeat'] && $_POST['password'] != "") {
-            logm('password updated');
-            if (!DEMO) {
-                $store->updatePassword(encode_string($_POST['password'] . $_SESSION['login']));
-                $msg->add('s', 'your password has been updated');
-            }
-            else {
-                $msg->add('i', 'in demo mode, you can\'t update password');
-            }
-        }
-        else
-            $msg->add('e', 'your password can\'t be empty and you have to repeat it in the second field');
-    }
+elseif (isset($_GET['config'])) {
+    # Update password
+    $poche->updatePassword();
+}
+elseif (isset($_GET['import'])) {
+    $import = $poche->import($_GET['from']);
+}
+elseif (isset($_GET['export'])) {
+    $poche->export();
 }
 
-# Traitement des paramètres et déclenchement des actions
-$view               = (isset ($_REQUEST['view'])) ? htmlentities($_REQUEST['view']) : 'index';
-$full_head          = (isset ($_REQUEST['full_head'])) ? htmlentities($_REQUEST['full_head']) : 'yes';
-$action             = (isset ($_REQUEST['action'])) ? htmlentities($_REQUEST['action']) : '';
-$_SESSION['sort']   = (isset ($_REQUEST['sort'])) ? htmlentities($_REQUEST['sort']) : 'id';
-$id                 = (isset ($_REQUEST['id'])) ? htmlspecialchars($_REQUEST['id']) : '';
-$url                = (isset ($_GET['url'])) ? $_GET['url'] : '';
-
-$tpl->assign('isLogged', Session::isLogged());
-$tpl->assign('referer', $ref);
-$tpl->assign('view', $view);
-$tpl->assign('poche_url', myTool::getUrl());
-$tpl->assign('title', 'poche, a read it later open source system');
+# vars to send to templates
+$tpl_vars = array(
+    'referer' => $referer,
+    'view' => $view,
+    'poche_url' => Tools::getPocheUrl(),
+    'title' => _('poche, a read it later open source system'),
+    'token' => Session::getToken(),
+);
 
 if (Session::isLogged()) {
-    action_to_do($action, $url, $id);
-    display_view($view, $id, $full_head);
+    $poche->action($action, $url, $id);
+    $tpl_file = Tools::getTplFile($view);
+    $tpl_vars = array_merge($tpl_vars, $poche->displayView($view, $id));
 }
 else {
-    $tpl->draw('login');
+    $tpl_file = 'login.twig';
 }
+
+# because messages can be added in $poche->action(), we have to add this entry now (we can add it before)
+$messages = $poche->messages->display('all', FALSE);
+$tpl_vars = array_merge($tpl_vars, array('messages' => $messages));
+
+# display poche
+echo $poche->tpl->render($tpl_file, $tpl_vars);
\ No newline at end of file
diff --git a/install/mysql.sql b/install/mysql.sql
new file mode 100644 (file)
index 0000000..cb232a8
--- /dev/null
@@ -0,0 +1,34 @@
+CREATE TABLE IF NOT EXISTS `config` (
+  `id` int(11) NOT NULL AUTO_INCREMENT,
+  `name` varchar(255) NOT NULL,
+  `value` varchar(255) NOT NULL,
+  PRIMARY KEY (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+
+CREATE TABLE IF NOT EXISTS `entries` (
+  `id` int(11) NOT NULL AUTO_INCREMENT,
+  `title` varchar(255) NOT NULL,
+  `url` varchar(255) NOT NULL,
+  `is_read` tinyint(1) NOT NULL,
+  `is_fav` tinyint(1) NOT NULL,
+  `content` blob NOT NULL,
+  `user_id` int(11) NOT NULL,
+  PRIMARY KEY (`id`)
+) ENGINE=InnoDB  DEFAULT CHARSET=latin1;
+
+CREATE TABLE IF NOT EXISTS `users` (
+  `id` int(11) NOT NULL AUTO_INCREMENT,
+  `username` varchar(255) NOT NULL,
+  `password` varchar(255) NOT NULL,
+  `name` int(255) NOT NULL,
+  `email` varchar(255) NOT NULL,
+  PRIMARY KEY (`id`)
+) ENGINE=InnoDB  DEFAULT CHARSET=latin1;
+
+CREATE TABLE IF NOT EXISTS `users_config` (
+  `id` int(11) NOT NULL AUTO_INCREMENT,
+  `user_id` int(11) NOT NULL,
+  `name` varchar(255) NOT NULL,
+  `value` varchar(255) NOT NULL,
+  PRIMARY KEY (`id`)
+) ENGINE=InnoDB  DEFAULT CHARSET=latin1;
\ No newline at end of file
similarity index 53%
rename from db/poche.sqlite
rename to install/poche.sqlite
index 2aee61f4b087d427b0e0f6ac0ad0c790b433d2dd..c268223db11c597b2102164aade2e2aeb6aa9030 100755 (executable)
Binary files a/db/poche.sqlite and b/install/poche.sqlite differ
diff --git a/install/postgres.sql b/install/postgres.sql
new file mode 100644 (file)
index 0000000..9e0e827
--- /dev/null
@@ -0,0 +1,30 @@
+CREATE TABLE config (
+    id bigserial primary key,
+    name varchar(255) NOT NULL,
+    value varchar(255) NOT NULL
+);
+
+CREATE TABLE entries (
+    id bigserial primary key,
+    title varchar(255) NOT NULL,
+    url varchar(255) NOT NULL,
+    is_read boolean DEFAULT false,
+    is_fav boolean DEFAULT false,
+    content TEXT,
+    user_id integer NOT NULL
+);
+
+CREATE TABLE users (
+    id bigserial primary key,
+    username varchar(255) NOT NULL,
+    password varchar(255) NOT NULL,
+    name varchar(255) NOT NULL,
+    email varchar(255) NOT NULL
+);
+
+CREATE TABLE users_config (
+    id bigserial primary key,
+    user_id integer NOT NULL,
+    name varchar(255) NOT NULL,
+    value varchar(255) NOT NULL
+);
\ No newline at end of file
diff --git a/install/update_sqlite_from_0_to_1.php b/install/update_sqlite_from_0_to_1.php
new file mode 100644 (file)
index 0000000..299abf4
--- /dev/null
@@ -0,0 +1,72 @@
+<?php
+# import script to upgrade from poche 0.3
+$db_path = 'sqlite:../db/poche.sqlite';
+$handle = new PDO($db_path);
+$handle->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
+
+# Requêtes à exécuter pour mettre à jour poche.sqlite en 1.x
+
+# ajout d'un champ user_id sur la table entries
+$sql = 'ALTER TABLE entries RENAME TO tempEntries;';
+$query = $handle->prepare($sql);
+$query->execute();
+
+$sql = 'CREATE TABLE entries (id INTEGER PRIMARY KEY, title TEXT, url TEXT, is_read NUMERIC DEFAULT 0, is_fav NUMERIC DEFAULT 0, content BLOB, user_id NUMERIC);';
+$query = $handle->prepare($sql);
+$query->execute();
+
+$sql = 'INSERT INTO entries (id, title, url, is_read, is_fav, content) SELECT id, title, url, is_read, is_fav, content FROM tempEntries;';
+$query = $handle->prepare($sql);
+$query->execute();
+
+# Update tout pour mettre user_id = 1
+$sql = 'UPDATE entries SET user_id = 1;';
+$query = $handle->prepare($sql);
+$query->execute();
+
+# Changement des flags pour les lus / favoris
+$sql = 'UPDATE entries SET is_read = 1 WHERE is_read = -1;';
+$query = $handle->prepare($sql);
+$query->execute();
+
+$sql = 'UPDATE entries SET is_fav = 1 WHERE is_fav = -1;';
+$query = $handle->prepare($sql);
+$query->execute();
+
+# Création de la table users
+$sql = 'CREATE TABLE users (id INTEGER PRIMARY KEY, username TEXT, password TEXT, name TEXT, email TEXT);';
+$query = $handle->prepare($sql);
+$query->execute();
+
+$sql = 'INSERT INTO users (username) SELECT value FROM config WHERE name = "login";';
+$query = $handle->prepare($sql);
+$query->execute();
+
+$sql = "UPDATE users SET password = (SELECT value FROM config WHERE name = 'password')";
+$query = $handle->prepare($sql);
+$query->execute();
+
+# Création de la table users_config
+$sql = 'CREATE TABLE users_config (id INTEGER PRIMARY KEY, user_id NUMERIC, name TEXT, value TEXT);';
+$query = $handle->prepare($sql);
+$query->execute();
+
+$sql = 'INSERT INTO users_config (user_id, name, value) VALUES (1, "pager", "10");';
+$query = $handle->prepare($sql);
+$query->execute();
+
+$sql = 'INSERT INTO users_config (user_id, name, value) VALUES (1, "language", "en_EN.UTF8");';
+$query = $handle->prepare($sql);
+$query->execute();
+
+# Suppression de la table temporaire
+$sql = 'DROP TABLE tempEntries;';
+$query = $handle->prepare($sql);
+$query->execute();
+
+# Vidage de la table de config
+$sql = 'DELETE FROM config;';
+$query = $handle->prepare($sql);
+$query->execute();
+
+echo 'welcome to poche 1.0 !';
\ No newline at end of file
diff --git a/js/jquery-1.9.1.min.js b/js/jquery-1.9.1.min.js
deleted file mode 100644 (file)
index 006e953..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-/*! jQuery v1.9.1 | (c) 2005, 2012 jQuery Foundation, Inc. | jquery.org/license
-//@ sourceMappingURL=jquery.min.map
-*/(function(e,t){var n,r,i=typeof t,o=e.document,a=e.location,s=e.jQuery,u=e.$,l={},c=[],p="1.9.1",f=c.concat,d=c.push,h=c.slice,g=c.indexOf,m=l.toString,y=l.hasOwnProperty,v=p.trim,b=function(e,t){return new b.fn.init(e,t,r)},x=/[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/.source,w=/\S+/g,T=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,N=/^(?:(<[\w\W]+>)[^>]*|#([\w-]*))$/,C=/^<(\w+)\s*\/?>(?:<\/\1>|)$/,k=/^[\],:{}\s]*$/,E=/(?:^|:|,)(?:\s*\[)+/g,S=/\\(?:["\\\/bfnrt]|u[\da-fA-F]{4})/g,A=/"[^"\\\r\n]*"|true|false|null|-?(?:\d+\.|)\d+(?:[eE][+-]?\d+|)/g,j=/^-ms-/,D=/-([\da-z])/gi,L=function(e,t){return t.toUpperCase()},H=function(e){(o.addEventListener||"load"===e.type||"complete"===o.readyState)&&(q(),b.ready())},q=function(){o.addEventListener?(o.removeEventListener("DOMContentLoaded",H,!1),e.removeEventListener("load",H,!1)):(o.detachEvent("onreadystatechange",H),e.detachEvent("onload",H))};b.fn=b.prototype={jquery:p,constructor:b,init:function(e,n,r){var i,a;if(!e)return this;if("string"==typeof e){if(i="<"===e.charAt(0)&&">"===e.charAt(e.length-1)&&e.length>=3?[null,e,null]:N.exec(e),!i||!i[1]&&n)return!n||n.jquery?(n||r).find(e):this.constructor(n).find(e);if(i[1]){if(n=n instanceof b?n[0]:n,b.merge(this,b.parseHTML(i[1],n&&n.nodeType?n.ownerDocument||n:o,!0)),C.test(i[1])&&b.isPlainObject(n))for(i in n)b.isFunction(this[i])?this[i](n[i]):this.attr(i,n[i]);return this}if(a=o.getElementById(i[2]),a&&a.parentNode){if(a.id!==i[2])return r.find(e);this.length=1,this[0]=a}return this.context=o,this.selector=e,this}return e.nodeType?(this.context=this[0]=e,this.length=1,this):b.isFunction(e)?r.ready(e):(e.selector!==t&&(this.selector=e.selector,this.context=e.context),b.makeArray(e,this))},selector:"",length:0,size:function(){return this.length},toArray:function(){return h.call(this)},get:function(e){return null==e?this.toArray():0>e?this[this.length+e]:this[e]},pushStack:function(e){var t=b.merge(this.constructor(),e);return t.prevObject=this,t.context=this.context,t},each:function(e,t){return b.each(this,e,t)},ready:function(e){return b.ready.promise().done(e),this},slice:function(){return this.pushStack(h.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(e){var t=this.length,n=+e+(0>e?t:0);return this.pushStack(n>=0&&t>n?[this[n]]:[])},map:function(e){return this.pushStack(b.map(this,function(t,n){return e.call(t,n,t)}))},end:function(){return this.prevObject||this.constructor(null)},push:d,sort:[].sort,splice:[].splice},b.fn.init.prototype=b.fn,b.extend=b.fn.extend=function(){var e,n,r,i,o,a,s=arguments[0]||{},u=1,l=arguments.length,c=!1;for("boolean"==typeof s&&(c=s,s=arguments[1]||{},u=2),"object"==typeof s||b.isFunction(s)||(s={}),l===u&&(s=this,--u);l>u;u++)if(null!=(o=arguments[u]))for(i in o)e=s[i],r=o[i],s!==r&&(c&&r&&(b.isPlainObject(r)||(n=b.isArray(r)))?(n?(n=!1,a=e&&b.isArray(e)?e:[]):a=e&&b.isPlainObject(e)?e:{},s[i]=b.extend(c,a,r)):r!==t&&(s[i]=r));return s},b.extend({noConflict:function(t){return e.$===b&&(e.$=u),t&&e.jQuery===b&&(e.jQuery=s),b},isReady:!1,readyWait:1,holdReady:function(e){e?b.readyWait++:b.ready(!0)},ready:function(e){if(e===!0?!--b.readyWait:!b.isReady){if(!o.body)return setTimeout(b.ready);b.isReady=!0,e!==!0&&--b.readyWait>0||(n.resolveWith(o,[b]),b.fn.trigger&&b(o).trigger("ready").off("ready"))}},isFunction:function(e){return"function"===b.type(e)},isArray:Array.isArray||function(e){return"array"===b.type(e)},isWindow:function(e){return null!=e&&e==e.window},isNumeric:function(e){return!isNaN(parseFloat(e))&&isFinite(e)},type:function(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?l[m.call(e)]||"object":typeof e},isPlainObject:function(e){if(!e||"object"!==b.type(e)||e.nodeType||b.isWindow(e))return!1;try{if(e.constructor&&!y.call(e,"constructor")&&!y.call(e.constructor.prototype,"isPrototypeOf"))return!1}catch(n){return!1}var r;for(r in e);return r===t||y.call(e,r)},isEmptyObject:function(e){var t;for(t in e)return!1;return!0},error:function(e){throw Error(e)},parseHTML:function(e,t,n){if(!e||"string"!=typeof e)return null;"boolean"==typeof t&&(n=t,t=!1),t=t||o;var r=C.exec(e),i=!n&&[];return r?[t.createElement(r[1])]:(r=b.buildFragment([e],t,i),i&&b(i).remove(),b.merge([],r.childNodes))},parseJSON:function(n){return e.JSON&&e.JSON.parse?e.JSON.parse(n):null===n?n:"string"==typeof n&&(n=b.trim(n),n&&k.test(n.replace(S,"@").replace(A,"]").replace(E,"")))?Function("return "+n)():(b.error("Invalid JSON: "+n),t)},parseXML:function(n){var r,i;if(!n||"string"!=typeof n)return null;try{e.DOMParser?(i=new DOMParser,r=i.parseFromString(n,"text/xml")):(r=new ActiveXObject("Microsoft.XMLDOM"),r.async="false",r.loadXML(n))}catch(o){r=t}return r&&r.documentElement&&!r.getElementsByTagName("parsererror").length||b.error("Invalid XML: "+n),r},noop:function(){},globalEval:function(t){t&&b.trim(t)&&(e.execScript||function(t){e.eval.call(e,t)})(t)},camelCase:function(e){return e.replace(j,"ms-").replace(D,L)},nodeName:function(e,t){return e.nodeName&&e.nodeName.toLowerCase()===t.toLowerCase()},each:function(e,t,n){var r,i=0,o=e.length,a=M(e);if(n){if(a){for(;o>i;i++)if(r=t.apply(e[i],n),r===!1)break}else for(i in e)if(r=t.apply(e[i],n),r===!1)break}else if(a){for(;o>i;i++)if(r=t.call(e[i],i,e[i]),r===!1)break}else for(i in e)if(r=t.call(e[i],i,e[i]),r===!1)break;return e},trim:v&&!v.call("\ufeff\u00a0")?function(e){return null==e?"":v.call(e)}:function(e){return null==e?"":(e+"").replace(T,"")},makeArray:function(e,t){var n=t||[];return null!=e&&(M(Object(e))?b.merge(n,"string"==typeof e?[e]:e):d.call(n,e)),n},inArray:function(e,t,n){var r;if(t){if(g)return g.call(t,e,n);for(r=t.length,n=n?0>n?Math.max(0,r+n):n:0;r>n;n++)if(n in t&&t[n]===e)return n}return-1},merge:function(e,n){var r=n.length,i=e.length,o=0;if("number"==typeof r)for(;r>o;o++)e[i++]=n[o];else while(n[o]!==t)e[i++]=n[o++];return e.length=i,e},grep:function(e,t,n){var r,i=[],o=0,a=e.length;for(n=!!n;a>o;o++)r=!!t(e[o],o),n!==r&&i.push(e[o]);return i},map:function(e,t,n){var r,i=0,o=e.length,a=M(e),s=[];if(a)for(;o>i;i++)r=t(e[i],i,n),null!=r&&(s[s.length]=r);else for(i in e)r=t(e[i],i,n),null!=r&&(s[s.length]=r);return f.apply([],s)},guid:1,proxy:function(e,n){var r,i,o;return"string"==typeof n&&(o=e[n],n=e,e=o),b.isFunction(e)?(r=h.call(arguments,2),i=function(){return e.apply(n||this,r.concat(h.call(arguments)))},i.guid=e.guid=e.guid||b.guid++,i):t},access:function(e,n,r,i,o,a,s){var u=0,l=e.length,c=null==r;if("object"===b.type(r)){o=!0;for(u in r)b.access(e,n,u,r[u],!0,a,s)}else if(i!==t&&(o=!0,b.isFunction(i)||(s=!0),c&&(s?(n.call(e,i),n=null):(c=n,n=function(e,t,n){return c.call(b(e),n)})),n))for(;l>u;u++)n(e[u],r,s?i:i.call(e[u],u,n(e[u],r)));return o?e:c?n.call(e):l?n(e[0],r):a},now:function(){return(new Date).getTime()}}),b.ready.promise=function(t){if(!n)if(n=b.Deferred(),"complete"===o.readyState)setTimeout(b.ready);else if(o.addEventListener)o.addEventListener("DOMContentLoaded",H,!1),e.addEventListener("load",H,!1);else{o.attachEvent("onreadystatechange",H),e.attachEvent("onload",H);var r=!1;try{r=null==e.frameElement&&o.documentElement}catch(i){}r&&r.doScroll&&function a(){if(!b.isReady){try{r.doScroll("left")}catch(e){return setTimeout(a,50)}q(),b.ready()}}()}return n.promise(t)},b.each("Boolean Number String Function Array Date RegExp Object Error".split(" "),function(e,t){l["[object "+t+"]"]=t.toLowerCase()});function M(e){var t=e.length,n=b.type(e);return b.isWindow(e)?!1:1===e.nodeType&&t?!0:"array"===n||"function"!==n&&(0===t||"number"==typeof t&&t>0&&t-1 in e)}r=b(o);var _={};function F(e){var t=_[e]={};return b.each(e.match(w)||[],function(e,n){t[n]=!0}),t}b.Callbacks=function(e){e="string"==typeof e?_[e]||F(e):b.extend({},e);var n,r,i,o,a,s,u=[],l=!e.once&&[],c=function(t){for(r=e.memory&&t,i=!0,a=s||0,s=0,o=u.length,n=!0;u&&o>a;a++)if(u[a].apply(t[0],t[1])===!1&&e.stopOnFalse){r=!1;break}n=!1,u&&(l?l.length&&c(l.shift()):r?u=[]:p.disable())},p={add:function(){if(u){var t=u.length;(function i(t){b.each(t,function(t,n){var r=b.type(n);"function"===r?e.unique&&p.has(n)||u.push(n):n&&n.length&&"string"!==r&&i(n)})})(arguments),n?o=u.length:r&&(s=t,c(r))}return this},remove:function(){return u&&b.each(arguments,function(e,t){var r;while((r=b.inArray(t,u,r))>-1)u.splice(r,1),n&&(o>=r&&o--,a>=r&&a--)}),this},has:function(e){return e?b.inArray(e,u)>-1:!(!u||!u.length)},empty:function(){return u=[],this},disable:function(){return u=l=r=t,this},disabled:function(){return!u},lock:function(){return l=t,r||p.disable(),this},locked:function(){return!l},fireWith:function(e,t){return t=t||[],t=[e,t.slice?t.slice():t],!u||i&&!l||(n?l.push(t):c(t)),this},fire:function(){return p.fireWith(this,arguments),this},fired:function(){return!!i}};return p},b.extend({Deferred:function(e){var t=[["resolve","done",b.Callbacks("once memory"),"resolved"],["reject","fail",b.Callbacks("once memory"),"rejected"],["notify","progress",b.Callbacks("memory")]],n="pending",r={state:function(){return n},always:function(){return i.done(arguments).fail(arguments),this},then:function(){var e=arguments;return b.Deferred(function(n){b.each(t,function(t,o){var a=o[0],s=b.isFunction(e[t])&&e[t];i[o[1]](function(){var e=s&&s.apply(this,arguments);e&&b.isFunction(e.promise)?e.promise().done(n.resolve).fail(n.reject).progress(n.notify):n[a+"With"](this===r?n.promise():this,s?[e]:arguments)})}),e=null}).promise()},promise:function(e){return null!=e?b.extend(e,r):r}},i={};return r.pipe=r.then,b.each(t,function(e,o){var a=o[2],s=o[3];r[o[1]]=a.add,s&&a.add(function(){n=s},t[1^e][2].disable,t[2][2].lock),i[o[0]]=function(){return i[o[0]+"With"](this===i?r:this,arguments),this},i[o[0]+"With"]=a.fireWith}),r.promise(i),e&&e.call(i,i),i},when:function(e){var t=0,n=h.call(arguments),r=n.length,i=1!==r||e&&b.isFunction(e.promise)?r:0,o=1===i?e:b.Deferred(),a=function(e,t,n){return function(r){t[e]=this,n[e]=arguments.length>1?h.call(arguments):r,n===s?o.notifyWith(t,n):--i||o.resolveWith(t,n)}},s,u,l;if(r>1)for(s=Array(r),u=Array(r),l=Array(r);r>t;t++)n[t]&&b.isFunction(n[t].promise)?n[t].promise().done(a(t,l,n)).fail(o.reject).progress(a(t,u,s)):--i;return i||o.resolveWith(l,n),o.promise()}}),b.support=function(){var t,n,r,a,s,u,l,c,p,f,d=o.createElement("div");if(d.setAttribute("className","t"),d.innerHTML="  <link/><table></table><a href='/a'>a</a><input type='checkbox'/>",n=d.getElementsByTagName("*"),r=d.getElementsByTagName("a")[0],!n||!r||!n.length)return{};s=o.createElement("select"),l=s.appendChild(o.createElement("option")),a=d.getElementsByTagName("input")[0],r.style.cssText="top:1px;float:left;opacity:.5",t={getSetAttribute:"t"!==d.className,leadingWhitespace:3===d.firstChild.nodeType,tbody:!d.getElementsByTagName("tbody").length,htmlSerialize:!!d.getElementsByTagName("link").length,style:/top/.test(r.getAttribute("style")),hrefNormalized:"/a"===r.getAttribute("href"),opacity:/^0.5/.test(r.style.opacity),cssFloat:!!r.style.cssFloat,checkOn:!!a.value,optSelected:l.selected,enctype:!!o.createElement("form").enctype,html5Clone:"<:nav></:nav>"!==o.createElement("nav").cloneNode(!0).outerHTML,boxModel:"CSS1Compat"===o.compatMode,deleteExpando:!0,noCloneEvent:!0,inlineBlockNeedsLayout:!1,shrinkWrapBlocks:!1,reliableMarginRight:!0,boxSizingReliable:!0,pixelPosition:!1},a.checked=!0,t.noCloneChecked=a.cloneNode(!0).checked,s.disabled=!0,t.optDisabled=!l.disabled;try{delete d.test}catch(h){t.deleteExpando=!1}a=o.createElement("input"),a.setAttribute("value",""),t.input=""===a.getAttribute("value"),a.value="t",a.setAttribute("type","radio"),t.radioValue="t"===a.value,a.setAttribute("checked","t"),a.setAttribute("name","t"),u=o.createDocumentFragment(),u.appendChild(a),t.appendChecked=a.checked,t.checkClone=u.cloneNode(!0).cloneNode(!0).lastChild.checked,d.attachEvent&&(d.attachEvent("onclick",function(){t.noCloneEvent=!1}),d.cloneNode(!0).click());for(f in{submit:!0,change:!0,focusin:!0})d.setAttribute(c="on"+f,"t"),t[f+"Bubbles"]=c in e||d.attributes[c].expando===!1;return d.style.backgroundClip="content-box",d.cloneNode(!0).style.backgroundClip="",t.clearCloneStyle="content-box"===d.style.backgroundClip,b(function(){var n,r,a,s="padding:0;margin:0;border:0;display:block;box-sizing:content-box;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;",u=o.getElementsByTagName("body")[0];u&&(n=o.createElement("div"),n.style.cssText="border:0;width:0;height:0;position:absolute;top:0;left:-9999px;margin-top:1px",u.appendChild(n).appendChild(d),d.innerHTML="<table><tr><td></td><td>t</td></tr></table>",a=d.getElementsByTagName("td"),a[0].style.cssText="padding:0;margin:0;border:0;display:none",p=0===a[0].offsetHeight,a[0].style.display="",a[1].style.display="none",t.reliableHiddenOffsets=p&&0===a[0].offsetHeight,d.innerHTML="",d.style.cssText="box-sizing:border-box;-moz-box-sizing:border-box;-webkit-box-sizing:border-box;padding:1px;border:1px;display:block;width:4px;margin-top:1%;position:absolute;top:1%;",t.boxSizing=4===d.offsetWidth,t.doesNotIncludeMarginInBodyOffset=1!==u.offsetTop,e.getComputedStyle&&(t.pixelPosition="1%"!==(e.getComputedStyle(d,null)||{}).top,t.boxSizingReliable="4px"===(e.getComputedStyle(d,null)||{width:"4px"}).width,r=d.appendChild(o.createElement("div")),r.style.cssText=d.style.cssText=s,r.style.marginRight=r.style.width="0",d.style.width="1px",t.reliableMarginRight=!parseFloat((e.getComputedStyle(r,null)||{}).marginRight)),typeof d.style.zoom!==i&&(d.innerHTML="",d.style.cssText=s+"width:1px;padding:1px;display:inline;zoom:1",t.inlineBlockNeedsLayout=3===d.offsetWidth,d.style.display="block",d.innerHTML="<div></div>",d.firstChild.style.width="5px",t.shrinkWrapBlocks=3!==d.offsetWidth,t.inlineBlockNeedsLayout&&(u.style.zoom=1)),u.removeChild(n),n=d=a=r=null)}),n=s=u=l=r=a=null,t}();var O=/(?:\{[\s\S]*\}|\[[\s\S]*\])$/,B=/([A-Z])/g;function P(e,n,r,i){if(b.acceptData(e)){var o,a,s=b.expando,u="string"==typeof n,l=e.nodeType,p=l?b.cache:e,f=l?e[s]:e[s]&&s;if(f&&p[f]&&(i||p[f].data)||!u||r!==t)return f||(l?e[s]=f=c.pop()||b.guid++:f=s),p[f]||(p[f]={},l||(p[f].toJSON=b.noop)),("object"==typeof n||"function"==typeof n)&&(i?p[f]=b.extend(p[f],n):p[f].data=b.extend(p[f].data,n)),o=p[f],i||(o.data||(o.data={}),o=o.data),r!==t&&(o[b.camelCase(n)]=r),u?(a=o[n],null==a&&(a=o[b.camelCase(n)])):a=o,a}}function R(e,t,n){if(b.acceptData(e)){var r,i,o,a=e.nodeType,s=a?b.cache:e,u=a?e[b.expando]:b.expando;if(s[u]){if(t&&(o=n?s[u]:s[u].data)){b.isArray(t)?t=t.concat(b.map(t,b.camelCase)):t in o?t=[t]:(t=b.camelCase(t),t=t in o?[t]:t.split(" "));for(r=0,i=t.length;i>r;r++)delete o[t[r]];if(!(n?$:b.isEmptyObject)(o))return}(n||(delete s[u].data,$(s[u])))&&(a?b.cleanData([e],!0):b.support.deleteExpando||s!=s.window?delete s[u]:s[u]=null)}}}b.extend({cache:{},expando:"jQuery"+(p+Math.random()).replace(/\D/g,""),noData:{embed:!0,object:"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000",applet:!0},hasData:function(e){return e=e.nodeType?b.cache[e[b.expando]]:e[b.expando],!!e&&!$(e)},data:function(e,t,n){return P(e,t,n)},removeData:function(e,t){return R(e,t)},_data:function(e,t,n){return P(e,t,n,!0)},_removeData:function(e,t){return R(e,t,!0)},acceptData:function(e){if(e.nodeType&&1!==e.nodeType&&9!==e.nodeType)return!1;var t=e.nodeName&&b.noData[e.nodeName.toLowerCase()];return!t||t!==!0&&e.getAttribute("classid")===t}}),b.fn.extend({data:function(e,n){var r,i,o=this[0],a=0,s=null;if(e===t){if(this.length&&(s=b.data(o),1===o.nodeType&&!b._data(o,"parsedAttrs"))){for(r=o.attributes;r.length>a;a++)i=r[a].name,i.indexOf("data-")||(i=b.camelCase(i.slice(5)),W(o,i,s[i]));b._data(o,"parsedAttrs",!0)}return s}return"object"==typeof e?this.each(function(){b.data(this,e)}):b.access(this,function(n){return n===t?o?W(o,e,b.data(o,e)):null:(this.each(function(){b.data(this,e,n)}),t)},null,n,arguments.length>1,null,!0)},removeData:function(e){return this.each(function(){b.removeData(this,e)})}});function W(e,n,r){if(r===t&&1===e.nodeType){var i="data-"+n.replace(B,"-$1").toLowerCase();if(r=e.getAttribute(i),"string"==typeof r){try{r="true"===r?!0:"false"===r?!1:"null"===r?null:+r+""===r?+r:O.test(r)?b.parseJSON(r):r}catch(o){}b.data(e,n,r)}else r=t}return r}function $(e){var t;for(t in e)if(("data"!==t||!b.isEmptyObject(e[t]))&&"toJSON"!==t)return!1;return!0}b.extend({queue:function(e,n,r){var i;return e?(n=(n||"fx")+"queue",i=b._data(e,n),r&&(!i||b.isArray(r)?i=b._data(e,n,b.makeArray(r)):i.push(r)),i||[]):t},dequeue:function(e,t){t=t||"fx";var n=b.queue(e,t),r=n.length,i=n.shift(),o=b._queueHooks(e,t),a=function(){b.dequeue(e,t)};"inprogress"===i&&(i=n.shift(),r--),o.cur=i,i&&("fx"===t&&n.unshift("inprogress"),delete o.stop,i.call(e,a,o)),!r&&o&&o.empty.fire()},_queueHooks:function(e,t){var n=t+"queueHooks";return b._data(e,n)||b._data(e,n,{empty:b.Callbacks("once memory").add(function(){b._removeData(e,t+"queue"),b._removeData(e,n)})})}}),b.fn.extend({queue:function(e,n){var r=2;return"string"!=typeof e&&(n=e,e="fx",r--),r>arguments.length?b.queue(this[0],e):n===t?this:this.each(function(){var t=b.queue(this,e,n);b._queueHooks(this,e),"fx"===e&&"inprogress"!==t[0]&&b.dequeue(this,e)})},dequeue:function(e){return this.each(function(){b.dequeue(this,e)})},delay:function(e,t){return e=b.fx?b.fx.speeds[e]||e:e,t=t||"fx",this.queue(t,function(t,n){var r=setTimeout(t,e);n.stop=function(){clearTimeout(r)}})},clearQueue:function(e){return this.queue(e||"fx",[])},promise:function(e,n){var r,i=1,o=b.Deferred(),a=this,s=this.length,u=function(){--i||o.resolveWith(a,[a])};"string"!=typeof e&&(n=e,e=t),e=e||"fx";while(s--)r=b._data(a[s],e+"queueHooks"),r&&r.empty&&(i++,r.empty.add(u));return u(),o.promise(n)}});var I,z,X=/[\t\r\n]/g,U=/\r/g,V=/^(?:input|select|textarea|button|object)$/i,Y=/^(?:a|area)$/i,J=/^(?:checked|selected|autofocus|autoplay|async|controls|defer|disabled|hidden|loop|multiple|open|readonly|required|scoped)$/i,G=/^(?:checked|selected)$/i,Q=b.support.getSetAttribute,K=b.support.input;b.fn.extend({attr:function(e,t){return b.access(this,b.attr,e,t,arguments.length>1)},removeAttr:function(e){return this.each(function(){b.removeAttr(this,e)})},prop:function(e,t){return b.access(this,b.prop,e,t,arguments.length>1)},removeProp:function(e){return e=b.propFix[e]||e,this.each(function(){try{this[e]=t,delete this[e]}catch(n){}})},addClass:function(e){var t,n,r,i,o,a=0,s=this.length,u="string"==typeof e&&e;if(b.isFunction(e))return this.each(function(t){b(this).addClass(e.call(this,t,this.className))});if(u)for(t=(e||"").match(w)||[];s>a;a++)if(n=this[a],r=1===n.nodeType&&(n.className?(" "+n.className+" ").replace(X," "):" ")){o=0;while(i=t[o++])0>r.indexOf(" "+i+" ")&&(r+=i+" ");n.className=b.trim(r)}return this},removeClass:function(e){var t,n,r,i,o,a=0,s=this.length,u=0===arguments.length||"string"==typeof e&&e;if(b.isFunction(e))return this.each(function(t){b(this).removeClass(e.call(this,t,this.className))});if(u)for(t=(e||"").match(w)||[];s>a;a++)if(n=this[a],r=1===n.nodeType&&(n.className?(" "+n.className+" ").replace(X," "):"")){o=0;while(i=t[o++])while(r.indexOf(" "+i+" ")>=0)r=r.replace(" "+i+" "," ");n.className=e?b.trim(r):""}return this},toggleClass:function(e,t){var n=typeof e,r="boolean"==typeof t;return b.isFunction(e)?this.each(function(n){b(this).toggleClass(e.call(this,n,this.className,t),t)}):this.each(function(){if("string"===n){var o,a=0,s=b(this),u=t,l=e.match(w)||[];while(o=l[a++])u=r?u:!s.hasClass(o),s[u?"addClass":"removeClass"](o)}else(n===i||"boolean"===n)&&(this.className&&b._data(this,"__className__",this.className),this.className=this.className||e===!1?"":b._data(this,"__className__")||"")})},hasClass:function(e){var t=" "+e+" ",n=0,r=this.length;for(;r>n;n++)if(1===this[n].nodeType&&(" "+this[n].className+" ").replace(X," ").indexOf(t)>=0)return!0;return!1},val:function(e){var n,r,i,o=this[0];{if(arguments.length)return i=b.isFunction(e),this.each(function(n){var o,a=b(this);1===this.nodeType&&(o=i?e.call(this,n,a.val()):e,null==o?o="":"number"==typeof o?o+="":b.isArray(o)&&(o=b.map(o,function(e){return null==e?"":e+""})),r=b.valHooks[this.type]||b.valHooks[this.nodeName.toLowerCase()],r&&"set"in r&&r.set(this,o,"value")!==t||(this.value=o))});if(o)return r=b.valHooks[o.type]||b.valHooks[o.nodeName.toLowerCase()],r&&"get"in r&&(n=r.get(o,"value"))!==t?n:(n=o.value,"string"==typeof n?n.replace(U,""):null==n?"":n)}}}),b.extend({valHooks:{option:{get:function(e){var t=e.attributes.value;return!t||t.specified?e.value:e.text}},select:{get:function(e){var t,n,r=e.options,i=e.selectedIndex,o="select-one"===e.type||0>i,a=o?null:[],s=o?i+1:r.length,u=0>i?s:o?i:0;for(;s>u;u++)if(n=r[u],!(!n.selected&&u!==i||(b.support.optDisabled?n.disabled:null!==n.getAttribute("disabled"))||n.parentNode.disabled&&b.nodeName(n.parentNode,"optgroup"))){if(t=b(n).val(),o)return t;a.push(t)}return a},set:function(e,t){var n=b.makeArray(t);return b(e).find("option").each(function(){this.selected=b.inArray(b(this).val(),n)>=0}),n.length||(e.selectedIndex=-1),n}}},attr:function(e,n,r){var o,a,s,u=e.nodeType;if(e&&3!==u&&8!==u&&2!==u)return typeof e.getAttribute===i?b.prop(e,n,r):(a=1!==u||!b.isXMLDoc(e),a&&(n=n.toLowerCase(),o=b.attrHooks[n]||(J.test(n)?z:I)),r===t?o&&a&&"get"in o&&null!==(s=o.get(e,n))?s:(typeof e.getAttribute!==i&&(s=e.getAttribute(n)),null==s?t:s):null!==r?o&&a&&"set"in o&&(s=o.set(e,r,n))!==t?s:(e.setAttribute(n,r+""),r):(b.removeAttr(e,n),t))},removeAttr:function(e,t){var n,r,i=0,o=t&&t.match(w);if(o&&1===e.nodeType)while(n=o[i++])r=b.propFix[n]||n,J.test(n)?!Q&&G.test(n)?e[b.camelCase("default-"+n)]=e[r]=!1:e[r]=!1:b.attr(e,n,""),e.removeAttribute(Q?n:r)},attrHooks:{type:{set:function(e,t){if(!b.support.radioValue&&"radio"===t&&b.nodeName(e,"input")){var n=e.value;return e.setAttribute("type",t),n&&(e.value=n),t}}}},propFix:{tabindex:"tabIndex",readonly:"readOnly","for":"htmlFor","class":"className",maxlength:"maxLength",cellspacing:"cellSpacing",cellpadding:"cellPadding",rowspan:"rowSpan",colspan:"colSpan",usemap:"useMap",frameborder:"frameBorder",contenteditable:"contentEditable"},prop:function(e,n,r){var i,o,a,s=e.nodeType;if(e&&3!==s&&8!==s&&2!==s)return a=1!==s||!b.isXMLDoc(e),a&&(n=b.propFix[n]||n,o=b.propHooks[n]),r!==t?o&&"set"in o&&(i=o.set(e,r,n))!==t?i:e[n]=r:o&&"get"in o&&null!==(i=o.get(e,n))?i:e[n]},propHooks:{tabIndex:{get:function(e){var n=e.getAttributeNode("tabindex");return n&&n.specified?parseInt(n.value,10):V.test(e.nodeName)||Y.test(e.nodeName)&&e.href?0:t}}}}),z={get:function(e,n){var r=b.prop(e,n),i="boolean"==typeof r&&e.getAttribute(n),o="boolean"==typeof r?K&&Q?null!=i:G.test(n)?e[b.camelCase("default-"+n)]:!!i:e.getAttributeNode(n);return o&&o.value!==!1?n.toLowerCase():t},set:function(e,t,n){return t===!1?b.removeAttr(e,n):K&&Q||!G.test(n)?e.setAttribute(!Q&&b.propFix[n]||n,n):e[b.camelCase("default-"+n)]=e[n]=!0,n}},K&&Q||(b.attrHooks.value={get:function(e,n){var r=e.getAttributeNode(n);return b.nodeName(e,"input")?e.defaultValue:r&&r.specified?r.value:t},set:function(e,n,r){return b.nodeName(e,"input")?(e.defaultValue=n,t):I&&I.set(e,n,r)}}),Q||(I=b.valHooks.button={get:function(e,n){var r=e.getAttributeNode(n);return r&&("id"===n||"name"===n||"coords"===n?""!==r.value:r.specified)?r.value:t},set:function(e,n,r){var i=e.getAttributeNode(r);return i||e.setAttributeNode(i=e.ownerDocument.createAttribute(r)),i.value=n+="","value"===r||n===e.getAttribute(r)?n:t}},b.attrHooks.contenteditable={get:I.get,set:function(e,t,n){I.set(e,""===t?!1:t,n)}},b.each(["width","height"],function(e,n){b.attrHooks[n]=b.extend(b.attrHooks[n],{set:function(e,r){return""===r?(e.setAttribute(n,"auto"),r):t}})})),b.support.hrefNormalized||(b.each(["href","src","width","height"],function(e,n){b.attrHooks[n]=b.extend(b.attrHooks[n],{get:function(e){var r=e.getAttribute(n,2);return null==r?t:r}})}),b.each(["href","src"],function(e,t){b.propHooks[t]={get:function(e){return e.getAttribute(t,4)}}})),b.support.style||(b.attrHooks.style={get:function(e){return e.style.cssText||t},set:function(e,t){return e.style.cssText=t+""}}),b.support.optSelected||(b.propHooks.selected=b.extend(b.propHooks.selected,{get:function(e){var t=e.parentNode;return t&&(t.selectedIndex,t.parentNode&&t.parentNode.selectedIndex),null}})),b.support.enctype||(b.propFix.enctype="encoding"),b.support.checkOn||b.each(["radio","checkbox"],function(){b.valHooks[this]={get:function(e){return null===e.getAttribute("value")?"on":e.value}}}),b.each(["radio","checkbox"],function(){b.valHooks[this]=b.extend(b.valHooks[this],{set:function(e,n){return b.isArray(n)?e.checked=b.inArray(b(e).val(),n)>=0:t}})});var Z=/^(?:input|select|textarea)$/i,et=/^key/,tt=/^(?:mouse|contextmenu)|click/,nt=/^(?:focusinfocus|focusoutblur)$/,rt=/^([^.]*)(?:\.(.+)|)$/;function it(){return!0}function ot(){return!1}b.event={global:{},add:function(e,n,r,o,a){var s,u,l,c,p,f,d,h,g,m,y,v=b._data(e);if(v){r.handler&&(c=r,r=c.handler,a=c.selector),r.guid||(r.guid=b.guid++),(u=v.events)||(u=v.events={}),(f=v.handle)||(f=v.handle=function(e){return typeof b===i||e&&b.event.triggered===e.type?t:b.event.dispatch.apply(f.elem,arguments)},f.elem=e),n=(n||"").match(w)||[""],l=n.length;while(l--)s=rt.exec(n[l])||[],g=y=s[1],m=(s[2]||"").split(".").sort(),p=b.event.special[g]||{},g=(a?p.delegateType:p.bindType)||g,p=b.event.special[g]||{},d=b.extend({type:g,origType:y,data:o,handler:r,guid:r.guid,selector:a,needsContext:a&&b.expr.match.needsContext.test(a),namespace:m.join(".")},c),(h=u[g])||(h=u[g]=[],h.delegateCount=0,p.setup&&p.setup.call(e,o,m,f)!==!1||(e.addEventListener?e.addEventListener(g,f,!1):e.attachEvent&&e.attachEvent("on"+g,f))),p.add&&(p.add.call(e,d),d.handler.guid||(d.handler.guid=r.guid)),a?h.splice(h.delegateCount++,0,d):h.push(d),b.event.global[g]=!0;e=null}},remove:function(e,t,n,r,i){var o,a,s,u,l,c,p,f,d,h,g,m=b.hasData(e)&&b._data(e);if(m&&(c=m.events)){t=(t||"").match(w)||[""],l=t.length;while(l--)if(s=rt.exec(t[l])||[],d=g=s[1],h=(s[2]||"").split(".").sort(),d){p=b.event.special[d]||{},d=(r?p.delegateType:p.bindType)||d,f=c[d]||[],s=s[2]&&RegExp("(^|\\.)"+h.join("\\.(?:.*\\.|)")+"(\\.|$)"),u=o=f.length;while(o--)a=f[o],!i&&g!==a.origType||n&&n.guid!==a.guid||s&&!s.test(a.namespace)||r&&r!==a.selector&&("**"!==r||!a.selector)||(f.splice(o,1),a.selector&&f.delegateCount--,p.remove&&p.remove.call(e,a));u&&!f.length&&(p.teardown&&p.teardown.call(e,h,m.handle)!==!1||b.removeEvent(e,d,m.handle),delete c[d])}else for(d in c)b.event.remove(e,d+t[l],n,r,!0);b.isEmptyObject(c)&&(delete m.handle,b._removeData(e,"events"))}},trigger:function(n,r,i,a){var s,u,l,c,p,f,d,h=[i||o],g=y.call(n,"type")?n.type:n,m=y.call(n,"namespace")?n.namespace.split("."):[];if(l=f=i=i||o,3!==i.nodeType&&8!==i.nodeType&&!nt.test(g+b.event.triggered)&&(g.indexOf(".")>=0&&(m=g.split("."),g=m.shift(),m.sort()),u=0>g.indexOf(":")&&"on"+g,n=n[b.expando]?n:new b.Event(g,"object"==typeof n&&n),n.isTrigger=!0,n.namespace=m.join("."),n.namespace_re=n.namespace?RegExp("(^|\\.)"+m.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,n.result=t,n.target||(n.target=i),r=null==r?[n]:b.makeArray(r,[n]),p=b.event.special[g]||{},a||!p.trigger||p.trigger.apply(i,r)!==!1)){if(!a&&!p.noBubble&&!b.isWindow(i)){for(c=p.delegateType||g,nt.test(c+g)||(l=l.parentNode);l;l=l.parentNode)h.push(l),f=l;f===(i.ownerDocument||o)&&h.push(f.defaultView||f.parentWindow||e)}d=0;while((l=h[d++])&&!n.isPropagationStopped())n.type=d>1?c:p.bindType||g,s=(b._data(l,"events")||{})[n.type]&&b._data(l,"handle"),s&&s.apply(l,r),s=u&&l[u],s&&b.acceptData(l)&&s.apply&&s.apply(l,r)===!1&&n.preventDefault();if(n.type=g,!(a||n.isDefaultPrevented()||p._default&&p._default.apply(i.ownerDocument,r)!==!1||"click"===g&&b.nodeName(i,"a")||!b.acceptData(i)||!u||!i[g]||b.isWindow(i))){f=i[u],f&&(i[u]=null),b.event.triggered=g;try{i[g]()}catch(v){}b.event.triggered=t,f&&(i[u]=f)}return n.result}},dispatch:function(e){e=b.event.fix(e);var n,r,i,o,a,s=[],u=h.call(arguments),l=(b._data(this,"events")||{})[e.type]||[],c=b.event.special[e.type]||{};if(u[0]=e,e.delegateTarget=this,!c.preDispatch||c.preDispatch.call(this,e)!==!1){s=b.event.handlers.call(this,e,l),n=0;while((o=s[n++])&&!e.isPropagationStopped()){e.currentTarget=o.elem,a=0;while((i=o.handlers[a++])&&!e.isImmediatePropagationStopped())(!e.namespace_re||e.namespace_re.test(i.namespace))&&(e.handleObj=i,e.data=i.data,r=((b.event.special[i.origType]||{}).handle||i.handler).apply(o.elem,u),r!==t&&(e.result=r)===!1&&(e.preventDefault(),e.stopPropagation()))}return c.postDispatch&&c.postDispatch.call(this,e),e.result}},handlers:function(e,n){var r,i,o,a,s=[],u=n.delegateCount,l=e.target;if(u&&l.nodeType&&(!e.button||"click"!==e.type))for(;l!=this;l=l.parentNode||this)if(1===l.nodeType&&(l.disabled!==!0||"click"!==e.type)){for(o=[],a=0;u>a;a++)i=n[a],r=i.selector+" ",o[r]===t&&(o[r]=i.needsContext?b(r,this).index(l)>=0:b.find(r,this,null,[l]).length),o[r]&&o.push(i);o.length&&s.push({elem:l,handlers:o})}return n.length>u&&s.push({elem:this,handlers:n.slice(u)}),s},fix:function(e){if(e[b.expando])return e;var t,n,r,i=e.type,a=e,s=this.fixHooks[i];s||(this.fixHooks[i]=s=tt.test(i)?this.mouseHooks:et.test(i)?this.keyHooks:{}),r=s.props?this.props.concat(s.props):this.props,e=new b.Event(a),t=r.length;while(t--)n=r[t],e[n]=a[n];return e.target||(e.target=a.srcElement||o),3===e.target.nodeType&&(e.target=e.target.parentNode),e.metaKey=!!e.metaKey,s.filter?s.filter(e,a):e},props:"altKey bubbles cancelable ctrlKey currentTarget eventPhase metaKey relatedTarget shiftKey target timeStamp view which".split(" "),fixHooks:{},keyHooks:{props:"char charCode key keyCode".split(" "),filter:function(e,t){return null==e.which&&(e.which=null!=t.charCode?t.charCode:t.keyCode),e}},mouseHooks:{props:"button buttons clientX clientY fromElement offsetX offsetY pageX pageY screenX screenY toElement".split(" "),filter:function(e,n){var r,i,a,s=n.button,u=n.fromElement;return null==e.pageX&&null!=n.clientX&&(i=e.target.ownerDocument||o,a=i.documentElement,r=i.body,e.pageX=n.clientX+(a&&a.scrollLeft||r&&r.scrollLeft||0)-(a&&a.clientLeft||r&&r.clientLeft||0),e.pageY=n.clientY+(a&&a.scrollTop||r&&r.scrollTop||0)-(a&&a.clientTop||r&&r.clientTop||0)),!e.relatedTarget&&u&&(e.relatedTarget=u===e.target?n.toElement:u),e.which||s===t||(e.which=1&s?1:2&s?3:4&s?2:0),e}},special:{load:{noBubble:!0},click:{trigger:function(){return b.nodeName(this,"input")&&"checkbox"===this.type&&this.click?(this.click(),!1):t}},focus:{trigger:function(){if(this!==o.activeElement&&this.focus)try{return this.focus(),!1}catch(e){}},delegateType:"focusin"},blur:{trigger:function(){return this===o.activeElement&&this.blur?(this.blur(),!1):t},delegateType:"focusout"},beforeunload:{postDispatch:function(e){e.result!==t&&(e.originalEvent.returnValue=e.result)}}},simulate:function(e,t,n,r){var i=b.extend(new b.Event,n,{type:e,isSimulated:!0,originalEvent:{}});r?b.event.trigger(i,null,t):b.event.dispatch.call(t,i),i.isDefaultPrevented()&&n.preventDefault()}},b.removeEvent=o.removeEventListener?function(e,t,n){e.removeEventListener&&e.removeEventListener(t,n,!1)}:function(e,t,n){var r="on"+t;e.detachEvent&&(typeof e[r]===i&&(e[r]=null),e.detachEvent(r,n))},b.Event=function(e,n){return this instanceof b.Event?(e&&e.type?(this.originalEvent=e,this.type=e.type,this.isDefaultPrevented=e.defaultPrevented||e.returnValue===!1||e.getPreventDefault&&e.getPreventDefault()?it:ot):this.type=e,n&&b.extend(this,n),this.timeStamp=e&&e.timeStamp||b.now(),this[b.expando]=!0,t):new b.Event(e,n)},b.Event.prototype={isDefaultPrevented:ot,isPropagationStopped:ot,isImmediatePropagationStopped:ot,preventDefault:function(){var e=this.originalEvent;this.isDefaultPrevented=it,e&&(e.preventDefault?e.preventDefault():e.returnValue=!1)},stopPropagation:function(){var e=this.originalEvent;this.isPropagationStopped=it,e&&(e.stopPropagation&&e.stopPropagation(),e.cancelBubble=!0)},stopImmediatePropagation:function(){this.isImmediatePropagationStopped=it,this.stopPropagation()}},b.each({mouseenter:"mouseover",mouseleave:"mouseout"},function(e,t){b.event.special[e]={delegateType:t,bindType:t,handle:function(e){var n,r=this,i=e.relatedTarget,o=e.handleObj;
-return(!i||i!==r&&!b.contains(r,i))&&(e.type=o.origType,n=o.handler.apply(this,arguments),e.type=t),n}}}),b.support.submitBubbles||(b.event.special.submit={setup:function(){return b.nodeName(this,"form")?!1:(b.event.add(this,"click._submit keypress._submit",function(e){var n=e.target,r=b.nodeName(n,"input")||b.nodeName(n,"button")?n.form:t;r&&!b._data(r,"submitBubbles")&&(b.event.add(r,"submit._submit",function(e){e._submit_bubble=!0}),b._data(r,"submitBubbles",!0))}),t)},postDispatch:function(e){e._submit_bubble&&(delete e._submit_bubble,this.parentNode&&!e.isTrigger&&b.event.simulate("submit",this.parentNode,e,!0))},teardown:function(){return b.nodeName(this,"form")?!1:(b.event.remove(this,"._submit"),t)}}),b.support.changeBubbles||(b.event.special.change={setup:function(){return Z.test(this.nodeName)?(("checkbox"===this.type||"radio"===this.type)&&(b.event.add(this,"propertychange._change",function(e){"checked"===e.originalEvent.propertyName&&(this._just_changed=!0)}),b.event.add(this,"click._change",function(e){this._just_changed&&!e.isTrigger&&(this._just_changed=!1),b.event.simulate("change",this,e,!0)})),!1):(b.event.add(this,"beforeactivate._change",function(e){var t=e.target;Z.test(t.nodeName)&&!b._data(t,"changeBubbles")&&(b.event.add(t,"change._change",function(e){!this.parentNode||e.isSimulated||e.isTrigger||b.event.simulate("change",this.parentNode,e,!0)}),b._data(t,"changeBubbles",!0))}),t)},handle:function(e){var n=e.target;return this!==n||e.isSimulated||e.isTrigger||"radio"!==n.type&&"checkbox"!==n.type?e.handleObj.handler.apply(this,arguments):t},teardown:function(){return b.event.remove(this,"._change"),!Z.test(this.nodeName)}}),b.support.focusinBubbles||b.each({focus:"focusin",blur:"focusout"},function(e,t){var n=0,r=function(e){b.event.simulate(t,e.target,b.event.fix(e),!0)};b.event.special[t]={setup:function(){0===n++&&o.addEventListener(e,r,!0)},teardown:function(){0===--n&&o.removeEventListener(e,r,!0)}}}),b.fn.extend({on:function(e,n,r,i,o){var a,s;if("object"==typeof e){"string"!=typeof n&&(r=r||n,n=t);for(a in e)this.on(a,n,r,e[a],o);return this}if(null==r&&null==i?(i=n,r=n=t):null==i&&("string"==typeof n?(i=r,r=t):(i=r,r=n,n=t)),i===!1)i=ot;else if(!i)return this;return 1===o&&(s=i,i=function(e){return b().off(e),s.apply(this,arguments)},i.guid=s.guid||(s.guid=b.guid++)),this.each(function(){b.event.add(this,e,i,r,n)})},one:function(e,t,n,r){return this.on(e,t,n,r,1)},off:function(e,n,r){var i,o;if(e&&e.preventDefault&&e.handleObj)return i=e.handleObj,b(e.delegateTarget).off(i.namespace?i.origType+"."+i.namespace:i.origType,i.selector,i.handler),this;if("object"==typeof e){for(o in e)this.off(o,n,e[o]);return this}return(n===!1||"function"==typeof n)&&(r=n,n=t),r===!1&&(r=ot),this.each(function(){b.event.remove(this,e,r,n)})},bind:function(e,t,n){return this.on(e,null,t,n)},unbind:function(e,t){return this.off(e,null,t)},delegate:function(e,t,n,r){return this.on(t,e,n,r)},undelegate:function(e,t,n){return 1===arguments.length?this.off(e,"**"):this.off(t,e||"**",n)},trigger:function(e,t){return this.each(function(){b.event.trigger(e,t,this)})},triggerHandler:function(e,n){var r=this[0];return r?b.event.trigger(e,n,r,!0):t}}),function(e,t){var n,r,i,o,a,s,u,l,c,p,f,d,h,g,m,y,v,x="sizzle"+-new Date,w=e.document,T={},N=0,C=0,k=it(),E=it(),S=it(),A=typeof t,j=1<<31,D=[],L=D.pop,H=D.push,q=D.slice,M=D.indexOf||function(e){var t=0,n=this.length;for(;n>t;t++)if(this[t]===e)return t;return-1},_="[\\x20\\t\\r\\n\\f]",F="(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+",O=F.replace("w","w#"),B="([*^$|!~]?=)",P="\\["+_+"*("+F+")"+_+"*(?:"+B+_+"*(?:(['\"])((?:\\\\.|[^\\\\])*?)\\3|("+O+")|)|)"+_+"*\\]",R=":("+F+")(?:\\(((['\"])((?:\\\\.|[^\\\\])*?)\\3|((?:\\\\.|[^\\\\()[\\]]|"+P.replace(3,8)+")*)|.*)\\)|)",W=RegExp("^"+_+"+|((?:^|[^\\\\])(?:\\\\.)*)"+_+"+$","g"),$=RegExp("^"+_+"*,"+_+"*"),I=RegExp("^"+_+"*([\\x20\\t\\r\\n\\f>+~])"+_+"*"),z=RegExp(R),X=RegExp("^"+O+"$"),U={ID:RegExp("^#("+F+")"),CLASS:RegExp("^\\.("+F+")"),NAME:RegExp("^\\[name=['\"]?("+F+")['\"]?\\]"),TAG:RegExp("^("+F.replace("w","w*")+")"),ATTR:RegExp("^"+P),PSEUDO:RegExp("^"+R),CHILD:RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+_+"*(even|odd|(([+-]|)(\\d*)n|)"+_+"*(?:([+-]|)"+_+"*(\\d+)|))"+_+"*\\)|)","i"),needsContext:RegExp("^"+_+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+_+"*((?:-\\d)?\\d*)"+_+"*\\)|)(?=[^-]|$)","i")},V=/[\x20\t\r\n\f]*[+~]/,Y=/^[^{]+\{\s*\[native code/,J=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,G=/^(?:input|select|textarea|button)$/i,Q=/^h\d$/i,K=/'|\\/g,Z=/\=[\x20\t\r\n\f]*([^'"\]]*)[\x20\t\r\n\f]*\]/g,et=/\\([\da-fA-F]{1,6}[\x20\t\r\n\f]?|.)/g,tt=function(e,t){var n="0x"+t-65536;return n!==n?t:0>n?String.fromCharCode(n+65536):String.fromCharCode(55296|n>>10,56320|1023&n)};try{q.call(w.documentElement.childNodes,0)[0].nodeType}catch(nt){q=function(e){var t,n=[];while(t=this[e++])n.push(t);return n}}function rt(e){return Y.test(e+"")}function it(){var e,t=[];return e=function(n,r){return t.push(n+=" ")>i.cacheLength&&delete e[t.shift()],e[n]=r}}function ot(e){return e[x]=!0,e}function at(e){var t=p.createElement("div");try{return e(t)}catch(n){return!1}finally{t=null}}function st(e,t,n,r){var i,o,a,s,u,l,f,g,m,v;if((t?t.ownerDocument||t:w)!==p&&c(t),t=t||p,n=n||[],!e||"string"!=typeof e)return n;if(1!==(s=t.nodeType)&&9!==s)return[];if(!d&&!r){if(i=J.exec(e))if(a=i[1]){if(9===s){if(o=t.getElementById(a),!o||!o.parentNode)return n;if(o.id===a)return n.push(o),n}else if(t.ownerDocument&&(o=t.ownerDocument.getElementById(a))&&y(t,o)&&o.id===a)return n.push(o),n}else{if(i[2])return H.apply(n,q.call(t.getElementsByTagName(e),0)),n;if((a=i[3])&&T.getByClassName&&t.getElementsByClassName)return H.apply(n,q.call(t.getElementsByClassName(a),0)),n}if(T.qsa&&!h.test(e)){if(f=!0,g=x,m=t,v=9===s&&e,1===s&&"object"!==t.nodeName.toLowerCase()){l=ft(e),(f=t.getAttribute("id"))?g=f.replace(K,"\\$&"):t.setAttribute("id",g),g="[id='"+g+"'] ",u=l.length;while(u--)l[u]=g+dt(l[u]);m=V.test(e)&&t.parentNode||t,v=l.join(",")}if(v)try{return H.apply(n,q.call(m.querySelectorAll(v),0)),n}catch(b){}finally{f||t.removeAttribute("id")}}}return wt(e.replace(W,"$1"),t,n,r)}a=st.isXML=function(e){var t=e&&(e.ownerDocument||e).documentElement;return t?"HTML"!==t.nodeName:!1},c=st.setDocument=function(e){var n=e?e.ownerDocument||e:w;return n!==p&&9===n.nodeType&&n.documentElement?(p=n,f=n.documentElement,d=a(n),T.tagNameNoComments=at(function(e){return e.appendChild(n.createComment("")),!e.getElementsByTagName("*").length}),T.attributes=at(function(e){e.innerHTML="<select></select>";var t=typeof e.lastChild.getAttribute("multiple");return"boolean"!==t&&"string"!==t}),T.getByClassName=at(function(e){return e.innerHTML="<div class='hidden e'></div><div class='hidden'></div>",e.getElementsByClassName&&e.getElementsByClassName("e").length?(e.lastChild.className="e",2===e.getElementsByClassName("e").length):!1}),T.getByName=at(function(e){e.id=x+0,e.innerHTML="<a name='"+x+"'></a><div name='"+x+"'></div>",f.insertBefore(e,f.firstChild);var t=n.getElementsByName&&n.getElementsByName(x).length===2+n.getElementsByName(x+0).length;return T.getIdNotName=!n.getElementById(x),f.removeChild(e),t}),i.attrHandle=at(function(e){return e.innerHTML="<a href='#'></a>",e.firstChild&&typeof e.firstChild.getAttribute!==A&&"#"===e.firstChild.getAttribute("href")})?{}:{href:function(e){return e.getAttribute("href",2)},type:function(e){return e.getAttribute("type")}},T.getIdNotName?(i.find.ID=function(e,t){if(typeof t.getElementById!==A&&!d){var n=t.getElementById(e);return n&&n.parentNode?[n]:[]}},i.filter.ID=function(e){var t=e.replace(et,tt);return function(e){return e.getAttribute("id")===t}}):(i.find.ID=function(e,n){if(typeof n.getElementById!==A&&!d){var r=n.getElementById(e);return r?r.id===e||typeof r.getAttributeNode!==A&&r.getAttributeNode("id").value===e?[r]:t:[]}},i.filter.ID=function(e){var t=e.replace(et,tt);return function(e){var n=typeof e.getAttributeNode!==A&&e.getAttributeNode("id");return n&&n.value===t}}),i.find.TAG=T.tagNameNoComments?function(e,n){return typeof n.getElementsByTagName!==A?n.getElementsByTagName(e):t}:function(e,t){var n,r=[],i=0,o=t.getElementsByTagName(e);if("*"===e){while(n=o[i++])1===n.nodeType&&r.push(n);return r}return o},i.find.NAME=T.getByName&&function(e,n){return typeof n.getElementsByName!==A?n.getElementsByName(name):t},i.find.CLASS=T.getByClassName&&function(e,n){return typeof n.getElementsByClassName===A||d?t:n.getElementsByClassName(e)},g=[],h=[":focus"],(T.qsa=rt(n.querySelectorAll))&&(at(function(e){e.innerHTML="<select><option selected=''></option></select>",e.querySelectorAll("[selected]").length||h.push("\\["+_+"*(?:checked|disabled|ismap|multiple|readonly|selected|value)"),e.querySelectorAll(":checked").length||h.push(":checked")}),at(function(e){e.innerHTML="<input type='hidden' i=''/>",e.querySelectorAll("[i^='']").length&&h.push("[*^$]="+_+"*(?:\"\"|'')"),e.querySelectorAll(":enabled").length||h.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),h.push(",.*:")})),(T.matchesSelector=rt(m=f.matchesSelector||f.mozMatchesSelector||f.webkitMatchesSelector||f.oMatchesSelector||f.msMatchesSelector))&&at(function(e){T.disconnectedMatch=m.call(e,"div"),m.call(e,"[s!='']:x"),g.push("!=",R)}),h=RegExp(h.join("|")),g=RegExp(g.join("|")),y=rt(f.contains)||f.compareDocumentPosition?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)while(t=t.parentNode)if(t===e)return!0;return!1},v=f.compareDocumentPosition?function(e,t){var r;return e===t?(u=!0,0):(r=t.compareDocumentPosition&&e.compareDocumentPosition&&e.compareDocumentPosition(t))?1&r||e.parentNode&&11===e.parentNode.nodeType?e===n||y(w,e)?-1:t===n||y(w,t)?1:0:4&r?-1:1:e.compareDocumentPosition?-1:1}:function(e,t){var r,i=0,o=e.parentNode,a=t.parentNode,s=[e],l=[t];if(e===t)return u=!0,0;if(!o||!a)return e===n?-1:t===n?1:o?-1:a?1:0;if(o===a)return ut(e,t);r=e;while(r=r.parentNode)s.unshift(r);r=t;while(r=r.parentNode)l.unshift(r);while(s[i]===l[i])i++;return i?ut(s[i],l[i]):s[i]===w?-1:l[i]===w?1:0},u=!1,[0,0].sort(v),T.detectDuplicates=u,p):p},st.matches=function(e,t){return st(e,null,null,t)},st.matchesSelector=function(e,t){if((e.ownerDocument||e)!==p&&c(e),t=t.replace(Z,"='$1']"),!(!T.matchesSelector||d||g&&g.test(t)||h.test(t)))try{var n=m.call(e,t);if(n||T.disconnectedMatch||e.document&&11!==e.document.nodeType)return n}catch(r){}return st(t,p,null,[e]).length>0},st.contains=function(e,t){return(e.ownerDocument||e)!==p&&c(e),y(e,t)},st.attr=function(e,t){var n;return(e.ownerDocument||e)!==p&&c(e),d||(t=t.toLowerCase()),(n=i.attrHandle[t])?n(e):d||T.attributes?e.getAttribute(t):((n=e.getAttributeNode(t))||e.getAttribute(t))&&e[t]===!0?t:n&&n.specified?n.value:null},st.error=function(e){throw Error("Syntax error, unrecognized expression: "+e)},st.uniqueSort=function(e){var t,n=[],r=1,i=0;if(u=!T.detectDuplicates,e.sort(v),u){for(;t=e[r];r++)t===e[r-1]&&(i=n.push(r));while(i--)e.splice(n[i],1)}return e};function ut(e,t){var n=t&&e,r=n&&(~t.sourceIndex||j)-(~e.sourceIndex||j);if(r)return r;if(n)while(n=n.nextSibling)if(n===t)return-1;return e?1:-1}function lt(e){return function(t){var n=t.nodeName.toLowerCase();return"input"===n&&t.type===e}}function ct(e){return function(t){var n=t.nodeName.toLowerCase();return("input"===n||"button"===n)&&t.type===e}}function pt(e){return ot(function(t){return t=+t,ot(function(n,r){var i,o=e([],n.length,t),a=o.length;while(a--)n[i=o[a]]&&(n[i]=!(r[i]=n[i]))})})}o=st.getText=function(e){var t,n="",r=0,i=e.nodeType;if(i){if(1===i||9===i||11===i){if("string"==typeof e.textContent)return e.textContent;for(e=e.firstChild;e;e=e.nextSibling)n+=o(e)}else if(3===i||4===i)return e.nodeValue}else for(;t=e[r];r++)n+=o(t);return n},i=st.selectors={cacheLength:50,createPseudo:ot,match:U,find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(et,tt),e[3]=(e[4]||e[5]||"").replace(et,tt),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||st.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&st.error(e[0]),e},PSEUDO:function(e){var t,n=!e[5]&&e[2];return U.CHILD.test(e[0])?null:(e[4]?e[2]=e[4]:n&&z.test(n)&&(t=ft(n,!0))&&(t=n.indexOf(")",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){return"*"===e?function(){return!0}:(e=e.replace(et,tt).toLowerCase(),function(t){return t.nodeName&&t.nodeName.toLowerCase()===e})},CLASS:function(e){var t=k[e+" "];return t||(t=RegExp("(^|"+_+")"+e+"("+_+"|$)"))&&k(e,function(e){return t.test(e.className||typeof e.getAttribute!==A&&e.getAttribute("class")||"")})},ATTR:function(e,t,n){return function(r){var i=st.attr(r,e);return null==i?"!="===t:t?(i+="","="===t?i===n:"!="===t?i!==n:"^="===t?n&&0===i.indexOf(n):"*="===t?n&&i.indexOf(n)>-1:"$="===t?n&&i.slice(-n.length)===n:"~="===t?(" "+i+" ").indexOf(n)>-1:"|="===t?i===n||i.slice(0,n.length+1)===n+"-":!1):!0}},CHILD:function(e,t,n,r,i){var o="nth"!==e.slice(0,3),a="last"!==e.slice(-4),s="of-type"===t;return 1===r&&0===i?function(e){return!!e.parentNode}:function(t,n,u){var l,c,p,f,d,h,g=o!==a?"nextSibling":"previousSibling",m=t.parentNode,y=s&&t.nodeName.toLowerCase(),v=!u&&!s;if(m){if(o){while(g){p=t;while(p=p[g])if(s?p.nodeName.toLowerCase()===y:1===p.nodeType)return!1;h=g="only"===e&&!h&&"nextSibling"}return!0}if(h=[a?m.firstChild:m.lastChild],a&&v){c=m[x]||(m[x]={}),l=c[e]||[],d=l[0]===N&&l[1],f=l[0]===N&&l[2],p=d&&m.childNodes[d];while(p=++d&&p&&p[g]||(f=d=0)||h.pop())if(1===p.nodeType&&++f&&p===t){c[e]=[N,d,f];break}}else if(v&&(l=(t[x]||(t[x]={}))[e])&&l[0]===N)f=l[1];else while(p=++d&&p&&p[g]||(f=d=0)||h.pop())if((s?p.nodeName.toLowerCase()===y:1===p.nodeType)&&++f&&(v&&((p[x]||(p[x]={}))[e]=[N,f]),p===t))break;return f-=i,f===r||0===f%r&&f/r>=0}}},PSEUDO:function(e,t){var n,r=i.pseudos[e]||i.setFilters[e.toLowerCase()]||st.error("unsupported pseudo: "+e);return r[x]?r(t):r.length>1?(n=[e,e,"",t],i.setFilters.hasOwnProperty(e.toLowerCase())?ot(function(e,n){var i,o=r(e,t),a=o.length;while(a--)i=M.call(e,o[a]),e[i]=!(n[i]=o[a])}):function(e){return r(e,0,n)}):r}},pseudos:{not:ot(function(e){var t=[],n=[],r=s(e.replace(W,"$1"));return r[x]?ot(function(e,t,n,i){var o,a=r(e,null,i,[]),s=e.length;while(s--)(o=a[s])&&(e[s]=!(t[s]=o))}):function(e,i,o){return t[0]=e,r(t,null,o,n),!n.pop()}}),has:ot(function(e){return function(t){return st(e,t).length>0}}),contains:ot(function(e){return function(t){return(t.textContent||t.innerText||o(t)).indexOf(e)>-1}}),lang:ot(function(e){return X.test(e||"")||st.error("unsupported lang: "+e),e=e.replace(et,tt).toLowerCase(),function(t){var n;do if(n=d?t.getAttribute("xml:lang")||t.getAttribute("lang"):t.lang)return n=n.toLowerCase(),n===e||0===n.indexOf(e+"-");while((t=t.parentNode)&&1===t.nodeType);return!1}}),target:function(t){var n=e.location&&e.location.hash;return n&&n.slice(1)===t.id},root:function(e){return e===f},focus:function(e){return e===p.activeElement&&(!p.hasFocus||p.hasFocus())&&!!(e.type||e.href||~e.tabIndex)},enabled:function(e){return e.disabled===!1},disabled:function(e){return e.disabled===!0},checked:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&!!e.checked||"option"===t&&!!e.selected},selected:function(e){return e.parentNode&&e.parentNode.selectedIndex,e.selected===!0},empty:function(e){for(e=e.firstChild;e;e=e.nextSibling)if(e.nodeName>"@"||3===e.nodeType||4===e.nodeType)return!1;return!0},parent:function(e){return!i.pseudos.empty(e)},header:function(e){return Q.test(e.nodeName)},input:function(e){return G.test(e.nodeName)},button:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&"button"===e.type||"button"===t},text:function(e){var t;return"input"===e.nodeName.toLowerCase()&&"text"===e.type&&(null==(t=e.getAttribute("type"))||t.toLowerCase()===e.type)},first:pt(function(){return[0]}),last:pt(function(e,t){return[t-1]}),eq:pt(function(e,t,n){return[0>n?n+t:n]}),even:pt(function(e,t){var n=0;for(;t>n;n+=2)e.push(n);return e}),odd:pt(function(e,t){var n=1;for(;t>n;n+=2)e.push(n);return e}),lt:pt(function(e,t,n){var r=0>n?n+t:n;for(;--r>=0;)e.push(r);return e}),gt:pt(function(e,t,n){var r=0>n?n+t:n;for(;t>++r;)e.push(r);return e})}};for(n in{radio:!0,checkbox:!0,file:!0,password:!0,image:!0})i.pseudos[n]=lt(n);for(n in{submit:!0,reset:!0})i.pseudos[n]=ct(n);function ft(e,t){var n,r,o,a,s,u,l,c=E[e+" "];if(c)return t?0:c.slice(0);s=e,u=[],l=i.preFilter;while(s){(!n||(r=$.exec(s)))&&(r&&(s=s.slice(r[0].length)||s),u.push(o=[])),n=!1,(r=I.exec(s))&&(n=r.shift(),o.push({value:n,type:r[0].replace(W," ")}),s=s.slice(n.length));for(a in i.filter)!(r=U[a].exec(s))||l[a]&&!(r=l[a](r))||(n=r.shift(),o.push({value:n,type:a,matches:r}),s=s.slice(n.length));if(!n)break}return t?s.length:s?st.error(e):E(e,u).slice(0)}function dt(e){var t=0,n=e.length,r="";for(;n>t;t++)r+=e[t].value;return r}function ht(e,t,n){var i=t.dir,o=n&&"parentNode"===i,a=C++;return t.first?function(t,n,r){while(t=t[i])if(1===t.nodeType||o)return e(t,n,r)}:function(t,n,s){var u,l,c,p=N+" "+a;if(s){while(t=t[i])if((1===t.nodeType||o)&&e(t,n,s))return!0}else while(t=t[i])if(1===t.nodeType||o)if(c=t[x]||(t[x]={}),(l=c[i])&&l[0]===p){if((u=l[1])===!0||u===r)return u===!0}else if(l=c[i]=[p],l[1]=e(t,n,s)||r,l[1]===!0)return!0}}function gt(e){return e.length>1?function(t,n,r){var i=e.length;while(i--)if(!e[i](t,n,r))return!1;return!0}:e[0]}function mt(e,t,n,r,i){var o,a=[],s=0,u=e.length,l=null!=t;for(;u>s;s++)(o=e[s])&&(!n||n(o,r,i))&&(a.push(o),l&&t.push(s));return a}function yt(e,t,n,r,i,o){return r&&!r[x]&&(r=yt(r)),i&&!i[x]&&(i=yt(i,o)),ot(function(o,a,s,u){var l,c,p,f=[],d=[],h=a.length,g=o||xt(t||"*",s.nodeType?[s]:s,[]),m=!e||!o&&t?g:mt(g,f,e,s,u),y=n?i||(o?e:h||r)?[]:a:m;if(n&&n(m,y,s,u),r){l=mt(y,d),r(l,[],s,u),c=l.length;while(c--)(p=l[c])&&(y[d[c]]=!(m[d[c]]=p))}if(o){if(i||e){if(i){l=[],c=y.length;while(c--)(p=y[c])&&l.push(m[c]=p);i(null,y=[],l,u)}c=y.length;while(c--)(p=y[c])&&(l=i?M.call(o,p):f[c])>-1&&(o[l]=!(a[l]=p))}}else y=mt(y===a?y.splice(h,y.length):y),i?i(null,a,y,u):H.apply(a,y)})}function vt(e){var t,n,r,o=e.length,a=i.relative[e[0].type],s=a||i.relative[" "],u=a?1:0,c=ht(function(e){return e===t},s,!0),p=ht(function(e){return M.call(t,e)>-1},s,!0),f=[function(e,n,r){return!a&&(r||n!==l)||((t=n).nodeType?c(e,n,r):p(e,n,r))}];for(;o>u;u++)if(n=i.relative[e[u].type])f=[ht(gt(f),n)];else{if(n=i.filter[e[u].type].apply(null,e[u].matches),n[x]){for(r=++u;o>r;r++)if(i.relative[e[r].type])break;return yt(u>1&&gt(f),u>1&&dt(e.slice(0,u-1)).replace(W,"$1"),n,r>u&&vt(e.slice(u,r)),o>r&&vt(e=e.slice(r)),o>r&&dt(e))}f.push(n)}return gt(f)}function bt(e,t){var n=0,o=t.length>0,a=e.length>0,s=function(s,u,c,f,d){var h,g,m,y=[],v=0,b="0",x=s&&[],w=null!=d,T=l,C=s||a&&i.find.TAG("*",d&&u.parentNode||u),k=N+=null==T?1:Math.random()||.1;for(w&&(l=u!==p&&u,r=n);null!=(h=C[b]);b++){if(a&&h){g=0;while(m=e[g++])if(m(h,u,c)){f.push(h);break}w&&(N=k,r=++n)}o&&((h=!m&&h)&&v--,s&&x.push(h))}if(v+=b,o&&b!==v){g=0;while(m=t[g++])m(x,y,u,c);if(s){if(v>0)while(b--)x[b]||y[b]||(y[b]=L.call(f));y=mt(y)}H.apply(f,y),w&&!s&&y.length>0&&v+t.length>1&&st.uniqueSort(f)}return w&&(N=k,l=T),x};return o?ot(s):s}s=st.compile=function(e,t){var n,r=[],i=[],o=S[e+" "];if(!o){t||(t=ft(e)),n=t.length;while(n--)o=vt(t[n]),o[x]?r.push(o):i.push(o);o=S(e,bt(i,r))}return o};function xt(e,t,n){var r=0,i=t.length;for(;i>r;r++)st(e,t[r],n);return n}function wt(e,t,n,r){var o,a,u,l,c,p=ft(e);if(!r&&1===p.length){if(a=p[0]=p[0].slice(0),a.length>2&&"ID"===(u=a[0]).type&&9===t.nodeType&&!d&&i.relative[a[1].type]){if(t=i.find.ID(u.matches[0].replace(et,tt),t)[0],!t)return n;e=e.slice(a.shift().value.length)}o=U.needsContext.test(e)?0:a.length;while(o--){if(u=a[o],i.relative[l=u.type])break;if((c=i.find[l])&&(r=c(u.matches[0].replace(et,tt),V.test(a[0].type)&&t.parentNode||t))){if(a.splice(o,1),e=r.length&&dt(a),!e)return H.apply(n,q.call(r,0)),n;break}}}return s(e,p)(r,t,d,n,V.test(e)),n}i.pseudos.nth=i.pseudos.eq;function Tt(){}i.filters=Tt.prototype=i.pseudos,i.setFilters=new Tt,c(),st.attr=b.attr,b.find=st,b.expr=st.selectors,b.expr[":"]=b.expr.pseudos,b.unique=st.uniqueSort,b.text=st.getText,b.isXMLDoc=st.isXML,b.contains=st.contains}(e);var at=/Until$/,st=/^(?:parents|prev(?:Until|All))/,ut=/^.[^:#\[\.,]*$/,lt=b.expr.match.needsContext,ct={children:!0,contents:!0,next:!0,prev:!0};b.fn.extend({find:function(e){var t,n,r,i=this.length;if("string"!=typeof e)return r=this,this.pushStack(b(e).filter(function(){for(t=0;i>t;t++)if(b.contains(r[t],this))return!0}));for(n=[],t=0;i>t;t++)b.find(e,this[t],n);return n=this.pushStack(i>1?b.unique(n):n),n.selector=(this.selector?this.selector+" ":"")+e,n},has:function(e){var t,n=b(e,this),r=n.length;return this.filter(function(){for(t=0;r>t;t++)if(b.contains(this,n[t]))return!0})},not:function(e){return this.pushStack(ft(this,e,!1))},filter:function(e){return this.pushStack(ft(this,e,!0))},is:function(e){return!!e&&("string"==typeof e?lt.test(e)?b(e,this.context).index(this[0])>=0:b.filter(e,this).length>0:this.filter(e).length>0)},closest:function(e,t){var n,r=0,i=this.length,o=[],a=lt.test(e)||"string"!=typeof e?b(e,t||this.context):0;for(;i>r;r++){n=this[r];while(n&&n.ownerDocument&&n!==t&&11!==n.nodeType){if(a?a.index(n)>-1:b.find.matchesSelector(n,e)){o.push(n);break}n=n.parentNode}}return this.pushStack(o.length>1?b.unique(o):o)},index:function(e){return e?"string"==typeof e?b.inArray(this[0],b(e)):b.inArray(e.jquery?e[0]:e,this):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(e,t){var n="string"==typeof e?b(e,t):b.makeArray(e&&e.nodeType?[e]:e),r=b.merge(this.get(),n);return this.pushStack(b.unique(r))},addBack:function(e){return this.add(null==e?this.prevObject:this.prevObject.filter(e))}}),b.fn.andSelf=b.fn.addBack;function pt(e,t){do e=e[t];while(e&&1!==e.nodeType);return e}b.each({parent:function(e){var t=e.parentNode;return t&&11!==t.nodeType?t:null},parents:function(e){return b.dir(e,"parentNode")},parentsUntil:function(e,t,n){return b.dir(e,"parentNode",n)},next:function(e){return pt(e,"nextSibling")},prev:function(e){return pt(e,"previousSibling")},nextAll:function(e){return b.dir(e,"nextSibling")},prevAll:function(e){return b.dir(e,"previousSibling")},nextUntil:function(e,t,n){return b.dir(e,"nextSibling",n)},prevUntil:function(e,t,n){return b.dir(e,"previousSibling",n)},siblings:function(e){return b.sibling((e.parentNode||{}).firstChild,e)},children:function(e){return b.sibling(e.firstChild)},contents:function(e){return b.nodeName(e,"iframe")?e.contentDocument||e.contentWindow.document:b.merge([],e.childNodes)}},function(e,t){b.fn[e]=function(n,r){var i=b.map(this,t,n);return at.test(e)||(r=n),r&&"string"==typeof r&&(i=b.filter(r,i)),i=this.length>1&&!ct[e]?b.unique(i):i,this.length>1&&st.test(e)&&(i=i.reverse()),this.pushStack(i)}}),b.extend({filter:function(e,t,n){return n&&(e=":not("+e+")"),1===t.length?b.find.matchesSelector(t[0],e)?[t[0]]:[]:b.find.matches(e,t)},dir:function(e,n,r){var i=[],o=e[n];while(o&&9!==o.nodeType&&(r===t||1!==o.nodeType||!b(o).is(r)))1===o.nodeType&&i.push(o),o=o[n];return i},sibling:function(e,t){var n=[];for(;e;e=e.nextSibling)1===e.nodeType&&e!==t&&n.push(e);return n}});function ft(e,t,n){if(t=t||0,b.isFunction(t))return b.grep(e,function(e,r){var i=!!t.call(e,r,e);return i===n});if(t.nodeType)return b.grep(e,function(e){return e===t===n});if("string"==typeof t){var r=b.grep(e,function(e){return 1===e.nodeType});if(ut.test(t))return b.filter(t,r,!n);t=b.filter(t,r)}return b.grep(e,function(e){return b.inArray(e,t)>=0===n})}function dt(e){var t=ht.split("|"),n=e.createDocumentFragment();if(n.createElement)while(t.length)n.createElement(t.pop());return n}var ht="abbr|article|aside|audio|bdi|canvas|data|datalist|details|figcaption|figure|footer|header|hgroup|mark|meter|nav|output|progress|section|summary|time|video",gt=/ jQuery\d+="(?:null|\d+)"/g,mt=RegExp("<(?:"+ht+")[\\s/>]","i"),yt=/^\s+/,vt=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi,bt=/<([\w:]+)/,xt=/<tbody/i,wt=/<|&#?\w+;/,Tt=/<(?:script|style|link)/i,Nt=/^(?:checkbox|radio)$/i,Ct=/checked\s*(?:[^=]|=\s*.checked.)/i,kt=/^$|\/(?:java|ecma)script/i,Et=/^true\/(.*)/,St=/^\s*<!(?:\[CDATA\[|--)|(?:\]\]|--)>\s*$/g,At={option:[1,"<select multiple='multiple'>","</select>"],legend:[1,"<fieldset>","</fieldset>"],area:[1,"<map>","</map>"],param:[1,"<object>","</object>"],thead:[1,"<table>","</table>"],tr:[2,"<table><tbody>","</tbody></table>"],col:[2,"<table><tbody></tbody><colgroup>","</colgroup></table>"],td:[3,"<table><tbody><tr>","</tr></tbody></table>"],_default:b.support.htmlSerialize?[0,"",""]:[1,"X<div>","</div>"]},jt=dt(o),Dt=jt.appendChild(o.createElement("div"));At.optgroup=At.option,At.tbody=At.tfoot=At.colgroup=At.caption=At.thead,At.th=At.td,b.fn.extend({text:function(e){return b.access(this,function(e){return e===t?b.text(this):this.empty().append((this[0]&&this[0].ownerDocument||o).createTextNode(e))},null,e,arguments.length)},wrapAll:function(e){if(b.isFunction(e))return this.each(function(t){b(this).wrapAll(e.call(this,t))});if(this[0]){var t=b(e,this[0].ownerDocument).eq(0).clone(!0);this[0].parentNode&&t.insertBefore(this[0]),t.map(function(){var e=this;while(e.firstChild&&1===e.firstChild.nodeType)e=e.firstChild;return e}).append(this)}return this},wrapInner:function(e){return b.isFunction(e)?this.each(function(t){b(this).wrapInner(e.call(this,t))}):this.each(function(){var t=b(this),n=t.contents();n.length?n.wrapAll(e):t.append(e)})},wrap:function(e){var t=b.isFunction(e);return this.each(function(n){b(this).wrapAll(t?e.call(this,n):e)})},unwrap:function(){return this.parent().each(function(){b.nodeName(this,"body")||b(this).replaceWith(this.childNodes)}).end()},append:function(){return this.domManip(arguments,!0,function(e){(1===this.nodeType||11===this.nodeType||9===this.nodeType)&&this.appendChild(e)})},prepend:function(){return this.domManip(arguments,!0,function(e){(1===this.nodeType||11===this.nodeType||9===this.nodeType)&&this.insertBefore(e,this.firstChild)})},before:function(){return this.domManip(arguments,!1,function(e){this.parentNode&&this.parentNode.insertBefore(e,this)})},after:function(){return this.domManip(arguments,!1,function(e){this.parentNode&&this.parentNode.insertBefore(e,this.nextSibling)})},remove:function(e,t){var n,r=0;for(;null!=(n=this[r]);r++)(!e||b.filter(e,[n]).length>0)&&(t||1!==n.nodeType||b.cleanData(Ot(n)),n.parentNode&&(t&&b.contains(n.ownerDocument,n)&&Mt(Ot(n,"script")),n.parentNode.removeChild(n)));return this},empty:function(){var e,t=0;for(;null!=(e=this[t]);t++){1===e.nodeType&&b.cleanData(Ot(e,!1));while(e.firstChild)e.removeChild(e.firstChild);e.options&&b.nodeName(e,"select")&&(e.options.length=0)}return this},clone:function(e,t){return e=null==e?!1:e,t=null==t?e:t,this.map(function(){return b.clone(this,e,t)})},html:function(e){return b.access(this,function(e){var n=this[0]||{},r=0,i=this.length;if(e===t)return 1===n.nodeType?n.innerHTML.replace(gt,""):t;if(!("string"!=typeof e||Tt.test(e)||!b.support.htmlSerialize&&mt.test(e)||!b.support.leadingWhitespace&&yt.test(e)||At[(bt.exec(e)||["",""])[1].toLowerCase()])){e=e.replace(vt,"<$1></$2>");try{for(;i>r;r++)n=this[r]||{},1===n.nodeType&&(b.cleanData(Ot(n,!1)),n.innerHTML=e);n=0}catch(o){}}n&&this.empty().append(e)},null,e,arguments.length)},replaceWith:function(e){var t=b.isFunction(e);return t||"string"==typeof e||(e=b(e).not(this).detach()),this.domManip([e],!0,function(e){var t=this.nextSibling,n=this.parentNode;n&&(b(this).remove(),n.insertBefore(e,t))})},detach:function(e){return this.remove(e,!0)},domManip:function(e,n,r){e=f.apply([],e);var i,o,a,s,u,l,c=0,p=this.length,d=this,h=p-1,g=e[0],m=b.isFunction(g);if(m||!(1>=p||"string"!=typeof g||b.support.checkClone)&&Ct.test(g))return this.each(function(i){var o=d.eq(i);m&&(e[0]=g.call(this,i,n?o.html():t)),o.domManip(e,n,r)});if(p&&(l=b.buildFragment(e,this[0].ownerDocument,!1,this),i=l.firstChild,1===l.childNodes.length&&(l=i),i)){for(n=n&&b.nodeName(i,"tr"),s=b.map(Ot(l,"script"),Ht),a=s.length;p>c;c++)o=l,c!==h&&(o=b.clone(o,!0,!0),a&&b.merge(s,Ot(o,"script"))),r.call(n&&b.nodeName(this[c],"table")?Lt(this[c],"tbody"):this[c],o,c);if(a)for(u=s[s.length-1].ownerDocument,b.map(s,qt),c=0;a>c;c++)o=s[c],kt.test(o.type||"")&&!b._data(o,"globalEval")&&b.contains(u,o)&&(o.src?b.ajax({url:o.src,type:"GET",dataType:"script",async:!1,global:!1,"throws":!0}):b.globalEval((o.text||o.textContent||o.innerHTML||"").replace(St,"")));l=i=null}return this}});function Lt(e,t){return e.getElementsByTagName(t)[0]||e.appendChild(e.ownerDocument.createElement(t))}function Ht(e){var t=e.getAttributeNode("type");return e.type=(t&&t.specified)+"/"+e.type,e}function qt(e){var t=Et.exec(e.type);return t?e.type=t[1]:e.removeAttribute("type"),e}function Mt(e,t){var n,r=0;for(;null!=(n=e[r]);r++)b._data(n,"globalEval",!t||b._data(t[r],"globalEval"))}function _t(e,t){if(1===t.nodeType&&b.hasData(e)){var n,r,i,o=b._data(e),a=b._data(t,o),s=o.events;if(s){delete a.handle,a.events={};for(n in s)for(r=0,i=s[n].length;i>r;r++)b.event.add(t,n,s[n][r])}a.data&&(a.data=b.extend({},a.data))}}function Ft(e,t){var n,r,i;if(1===t.nodeType){if(n=t.nodeName.toLowerCase(),!b.support.noCloneEvent&&t[b.expando]){i=b._data(t);for(r in i.events)b.removeEvent(t,r,i.handle);t.removeAttribute(b.expando)}"script"===n&&t.text!==e.text?(Ht(t).text=e.text,qt(t)):"object"===n?(t.parentNode&&(t.outerHTML=e.outerHTML),b.support.html5Clone&&e.innerHTML&&!b.trim(t.innerHTML)&&(t.innerHTML=e.innerHTML)):"input"===n&&Nt.test(e.type)?(t.defaultChecked=t.checked=e.checked,t.value!==e.value&&(t.value=e.value)):"option"===n?t.defaultSelected=t.selected=e.defaultSelected:("input"===n||"textarea"===n)&&(t.defaultValue=e.defaultValue)}}b.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(e,t){b.fn[e]=function(e){var n,r=0,i=[],o=b(e),a=o.length-1;for(;a>=r;r++)n=r===a?this:this.clone(!0),b(o[r])[t](n),d.apply(i,n.get());return this.pushStack(i)}});function Ot(e,n){var r,o,a=0,s=typeof e.getElementsByTagName!==i?e.getElementsByTagName(n||"*"):typeof e.querySelectorAll!==i?e.querySelectorAll(n||"*"):t;if(!s)for(s=[],r=e.childNodes||e;null!=(o=r[a]);a++)!n||b.nodeName(o,n)?s.push(o):b.merge(s,Ot(o,n));return n===t||n&&b.nodeName(e,n)?b.merge([e],s):s}function Bt(e){Nt.test(e.type)&&(e.defaultChecked=e.checked)}b.extend({clone:function(e,t,n){var r,i,o,a,s,u=b.contains(e.ownerDocument,e);if(b.support.html5Clone||b.isXMLDoc(e)||!mt.test("<"+e.nodeName+">")?o=e.cloneNode(!0):(Dt.innerHTML=e.outerHTML,Dt.removeChild(o=Dt.firstChild)),!(b.support.noCloneEvent&&b.support.noCloneChecked||1!==e.nodeType&&11!==e.nodeType||b.isXMLDoc(e)))for(r=Ot(o),s=Ot(e),a=0;null!=(i=s[a]);++a)r[a]&&Ft(i,r[a]);if(t)if(n)for(s=s||Ot(e),r=r||Ot(o),a=0;null!=(i=s[a]);a++)_t(i,r[a]);else _t(e,o);return r=Ot(o,"script"),r.length>0&&Mt(r,!u&&Ot(e,"script")),r=s=i=null,o},buildFragment:function(e,t,n,r){var i,o,a,s,u,l,c,p=e.length,f=dt(t),d=[],h=0;for(;p>h;h++)if(o=e[h],o||0===o)if("object"===b.type(o))b.merge(d,o.nodeType?[o]:o);else if(wt.test(o)){s=s||f.appendChild(t.createElement("div")),u=(bt.exec(o)||["",""])[1].toLowerCase(),c=At[u]||At._default,s.innerHTML=c[1]+o.replace(vt,"<$1></$2>")+c[2],i=c[0];while(i--)s=s.lastChild;if(!b.support.leadingWhitespace&&yt.test(o)&&d.push(t.createTextNode(yt.exec(o)[0])),!b.support.tbody){o="table"!==u||xt.test(o)?"<table>"!==c[1]||xt.test(o)?0:s:s.firstChild,i=o&&o.childNodes.length;while(i--)b.nodeName(l=o.childNodes[i],"tbody")&&!l.childNodes.length&&o.removeChild(l)
-}b.merge(d,s.childNodes),s.textContent="";while(s.firstChild)s.removeChild(s.firstChild);s=f.lastChild}else d.push(t.createTextNode(o));s&&f.removeChild(s),b.support.appendChecked||b.grep(Ot(d,"input"),Bt),h=0;while(o=d[h++])if((!r||-1===b.inArray(o,r))&&(a=b.contains(o.ownerDocument,o),s=Ot(f.appendChild(o),"script"),a&&Mt(s),n)){i=0;while(o=s[i++])kt.test(o.type||"")&&n.push(o)}return s=null,f},cleanData:function(e,t){var n,r,o,a,s=0,u=b.expando,l=b.cache,p=b.support.deleteExpando,f=b.event.special;for(;null!=(n=e[s]);s++)if((t||b.acceptData(n))&&(o=n[u],a=o&&l[o])){if(a.events)for(r in a.events)f[r]?b.event.remove(n,r):b.removeEvent(n,r,a.handle);l[o]&&(delete l[o],p?delete n[u]:typeof n.removeAttribute!==i?n.removeAttribute(u):n[u]=null,c.push(o))}}});var Pt,Rt,Wt,$t=/alpha\([^)]*\)/i,It=/opacity\s*=\s*([^)]*)/,zt=/^(top|right|bottom|left)$/,Xt=/^(none|table(?!-c[ea]).+)/,Ut=/^margin/,Vt=RegExp("^("+x+")(.*)$","i"),Yt=RegExp("^("+x+")(?!px)[a-z%]+$","i"),Jt=RegExp("^([+-])=("+x+")","i"),Gt={BODY:"block"},Qt={position:"absolute",visibility:"hidden",display:"block"},Kt={letterSpacing:0,fontWeight:400},Zt=["Top","Right","Bottom","Left"],en=["Webkit","O","Moz","ms"];function tn(e,t){if(t in e)return t;var n=t.charAt(0).toUpperCase()+t.slice(1),r=t,i=en.length;while(i--)if(t=en[i]+n,t in e)return t;return r}function nn(e,t){return e=t||e,"none"===b.css(e,"display")||!b.contains(e.ownerDocument,e)}function rn(e,t){var n,r,i,o=[],a=0,s=e.length;for(;s>a;a++)r=e[a],r.style&&(o[a]=b._data(r,"olddisplay"),n=r.style.display,t?(o[a]||"none"!==n||(r.style.display=""),""===r.style.display&&nn(r)&&(o[a]=b._data(r,"olddisplay",un(r.nodeName)))):o[a]||(i=nn(r),(n&&"none"!==n||!i)&&b._data(r,"olddisplay",i?n:b.css(r,"display"))));for(a=0;s>a;a++)r=e[a],r.style&&(t&&"none"!==r.style.display&&""!==r.style.display||(r.style.display=t?o[a]||"":"none"));return e}b.fn.extend({css:function(e,n){return b.access(this,function(e,n,r){var i,o,a={},s=0;if(b.isArray(n)){for(o=Rt(e),i=n.length;i>s;s++)a[n[s]]=b.css(e,n[s],!1,o);return a}return r!==t?b.style(e,n,r):b.css(e,n)},e,n,arguments.length>1)},show:function(){return rn(this,!0)},hide:function(){return rn(this)},toggle:function(e){var t="boolean"==typeof e;return this.each(function(){(t?e:nn(this))?b(this).show():b(this).hide()})}}),b.extend({cssHooks:{opacity:{get:function(e,t){if(t){var n=Wt(e,"opacity");return""===n?"1":n}}}},cssNumber:{columnCount:!0,fillOpacity:!0,fontWeight:!0,lineHeight:!0,opacity:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{"float":b.support.cssFloat?"cssFloat":"styleFloat"},style:function(e,n,r,i){if(e&&3!==e.nodeType&&8!==e.nodeType&&e.style){var o,a,s,u=b.camelCase(n),l=e.style;if(n=b.cssProps[u]||(b.cssProps[u]=tn(l,u)),s=b.cssHooks[n]||b.cssHooks[u],r===t)return s&&"get"in s&&(o=s.get(e,!1,i))!==t?o:l[n];if(a=typeof r,"string"===a&&(o=Jt.exec(r))&&(r=(o[1]+1)*o[2]+parseFloat(b.css(e,n)),a="number"),!(null==r||"number"===a&&isNaN(r)||("number"!==a||b.cssNumber[u]||(r+="px"),b.support.clearCloneStyle||""!==r||0!==n.indexOf("background")||(l[n]="inherit"),s&&"set"in s&&(r=s.set(e,r,i))===t)))try{l[n]=r}catch(c){}}},css:function(e,n,r,i){var o,a,s,u=b.camelCase(n);return n=b.cssProps[u]||(b.cssProps[u]=tn(e.style,u)),s=b.cssHooks[n]||b.cssHooks[u],s&&"get"in s&&(a=s.get(e,!0,r)),a===t&&(a=Wt(e,n,i)),"normal"===a&&n in Kt&&(a=Kt[n]),""===r||r?(o=parseFloat(a),r===!0||b.isNumeric(o)?o||0:a):a},swap:function(e,t,n,r){var i,o,a={};for(o in t)a[o]=e.style[o],e.style[o]=t[o];i=n.apply(e,r||[]);for(o in t)e.style[o]=a[o];return i}}),e.getComputedStyle?(Rt=function(t){return e.getComputedStyle(t,null)},Wt=function(e,n,r){var i,o,a,s=r||Rt(e),u=s?s.getPropertyValue(n)||s[n]:t,l=e.style;return s&&(""!==u||b.contains(e.ownerDocument,e)||(u=b.style(e,n)),Yt.test(u)&&Ut.test(n)&&(i=l.width,o=l.minWidth,a=l.maxWidth,l.minWidth=l.maxWidth=l.width=u,u=s.width,l.width=i,l.minWidth=o,l.maxWidth=a)),u}):o.documentElement.currentStyle&&(Rt=function(e){return e.currentStyle},Wt=function(e,n,r){var i,o,a,s=r||Rt(e),u=s?s[n]:t,l=e.style;return null==u&&l&&l[n]&&(u=l[n]),Yt.test(u)&&!zt.test(n)&&(i=l.left,o=e.runtimeStyle,a=o&&o.left,a&&(o.left=e.currentStyle.left),l.left="fontSize"===n?"1em":u,u=l.pixelLeft+"px",l.left=i,a&&(o.left=a)),""===u?"auto":u});function on(e,t,n){var r=Vt.exec(t);return r?Math.max(0,r[1]-(n||0))+(r[2]||"px"):t}function an(e,t,n,r,i){var o=n===(r?"border":"content")?4:"width"===t?1:0,a=0;for(;4>o;o+=2)"margin"===n&&(a+=b.css(e,n+Zt[o],!0,i)),r?("content"===n&&(a-=b.css(e,"padding"+Zt[o],!0,i)),"margin"!==n&&(a-=b.css(e,"border"+Zt[o]+"Width",!0,i))):(a+=b.css(e,"padding"+Zt[o],!0,i),"padding"!==n&&(a+=b.css(e,"border"+Zt[o]+"Width",!0,i)));return a}function sn(e,t,n){var r=!0,i="width"===t?e.offsetWidth:e.offsetHeight,o=Rt(e),a=b.support.boxSizing&&"border-box"===b.css(e,"boxSizing",!1,o);if(0>=i||null==i){if(i=Wt(e,t,o),(0>i||null==i)&&(i=e.style[t]),Yt.test(i))return i;r=a&&(b.support.boxSizingReliable||i===e.style[t]),i=parseFloat(i)||0}return i+an(e,t,n||(a?"border":"content"),r,o)+"px"}function un(e){var t=o,n=Gt[e];return n||(n=ln(e,t),"none"!==n&&n||(Pt=(Pt||b("<iframe frameborder='0' width='0' height='0'/>").css("cssText","display:block !important")).appendTo(t.documentElement),t=(Pt[0].contentWindow||Pt[0].contentDocument).document,t.write("<!doctype html><html><body>"),t.close(),n=ln(e,t),Pt.detach()),Gt[e]=n),n}function ln(e,t){var n=b(t.createElement(e)).appendTo(t.body),r=b.css(n[0],"display");return n.remove(),r}b.each(["height","width"],function(e,n){b.cssHooks[n]={get:function(e,r,i){return r?0===e.offsetWidth&&Xt.test(b.css(e,"display"))?b.swap(e,Qt,function(){return sn(e,n,i)}):sn(e,n,i):t},set:function(e,t,r){var i=r&&Rt(e);return on(e,t,r?an(e,n,r,b.support.boxSizing&&"border-box"===b.css(e,"boxSizing",!1,i),i):0)}}}),b.support.opacity||(b.cssHooks.opacity={get:function(e,t){return It.test((t&&e.currentStyle?e.currentStyle.filter:e.style.filter)||"")?.01*parseFloat(RegExp.$1)+"":t?"1":""},set:function(e,t){var n=e.style,r=e.currentStyle,i=b.isNumeric(t)?"alpha(opacity="+100*t+")":"",o=r&&r.filter||n.filter||"";n.zoom=1,(t>=1||""===t)&&""===b.trim(o.replace($t,""))&&n.removeAttribute&&(n.removeAttribute("filter"),""===t||r&&!r.filter)||(n.filter=$t.test(o)?o.replace($t,i):o+" "+i)}}),b(function(){b.support.reliableMarginRight||(b.cssHooks.marginRight={get:function(e,n){return n?b.swap(e,{display:"inline-block"},Wt,[e,"marginRight"]):t}}),!b.support.pixelPosition&&b.fn.position&&b.each(["top","left"],function(e,n){b.cssHooks[n]={get:function(e,r){return r?(r=Wt(e,n),Yt.test(r)?b(e).position()[n]+"px":r):t}}})}),b.expr&&b.expr.filters&&(b.expr.filters.hidden=function(e){return 0>=e.offsetWidth&&0>=e.offsetHeight||!b.support.reliableHiddenOffsets&&"none"===(e.style&&e.style.display||b.css(e,"display"))},b.expr.filters.visible=function(e){return!b.expr.filters.hidden(e)}),b.each({margin:"",padding:"",border:"Width"},function(e,t){b.cssHooks[e+t]={expand:function(n){var r=0,i={},o="string"==typeof n?n.split(" "):[n];for(;4>r;r++)i[e+Zt[r]+t]=o[r]||o[r-2]||o[0];return i}},Ut.test(e)||(b.cssHooks[e+t].set=on)});var cn=/%20/g,pn=/\[\]$/,fn=/\r?\n/g,dn=/^(?:submit|button|image|reset|file)$/i,hn=/^(?:input|select|textarea|keygen)/i;b.fn.extend({serialize:function(){return b.param(this.serializeArray())},serializeArray:function(){return this.map(function(){var e=b.prop(this,"elements");return e?b.makeArray(e):this}).filter(function(){var e=this.type;return this.name&&!b(this).is(":disabled")&&hn.test(this.nodeName)&&!dn.test(e)&&(this.checked||!Nt.test(e))}).map(function(e,t){var n=b(this).val();return null==n?null:b.isArray(n)?b.map(n,function(e){return{name:t.name,value:e.replace(fn,"\r\n")}}):{name:t.name,value:n.replace(fn,"\r\n")}}).get()}}),b.param=function(e,n){var r,i=[],o=function(e,t){t=b.isFunction(t)?t():null==t?"":t,i[i.length]=encodeURIComponent(e)+"="+encodeURIComponent(t)};if(n===t&&(n=b.ajaxSettings&&b.ajaxSettings.traditional),b.isArray(e)||e.jquery&&!b.isPlainObject(e))b.each(e,function(){o(this.name,this.value)});else for(r in e)gn(r,e[r],n,o);return i.join("&").replace(cn,"+")};function gn(e,t,n,r){var i;if(b.isArray(t))b.each(t,function(t,i){n||pn.test(e)?r(e,i):gn(e+"["+("object"==typeof i?t:"")+"]",i,n,r)});else if(n||"object"!==b.type(t))r(e,t);else for(i in t)gn(e+"["+i+"]",t[i],n,r)}b.each("blur focus focusin focusout load resize scroll unload click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup error contextmenu".split(" "),function(e,t){b.fn[t]=function(e,n){return arguments.length>0?this.on(t,null,e,n):this.trigger(t)}}),b.fn.hover=function(e,t){return this.mouseenter(e).mouseleave(t||e)};var mn,yn,vn=b.now(),bn=/\?/,xn=/#.*$/,wn=/([?&])_=[^&]*/,Tn=/^(.*?):[ \t]*([^\r\n]*)\r?$/gm,Nn=/^(?:about|app|app-storage|.+-extension|file|res|widget):$/,Cn=/^(?:GET|HEAD)$/,kn=/^\/\//,En=/^([\w.+-]+:)(?:\/\/([^\/?#:]*)(?::(\d+)|)|)/,Sn=b.fn.load,An={},jn={},Dn="*/".concat("*");try{yn=a.href}catch(Ln){yn=o.createElement("a"),yn.href="",yn=yn.href}mn=En.exec(yn.toLowerCase())||[];function Hn(e){return function(t,n){"string"!=typeof t&&(n=t,t="*");var r,i=0,o=t.toLowerCase().match(w)||[];if(b.isFunction(n))while(r=o[i++])"+"===r[0]?(r=r.slice(1)||"*",(e[r]=e[r]||[]).unshift(n)):(e[r]=e[r]||[]).push(n)}}function qn(e,n,r,i){var o={},a=e===jn;function s(u){var l;return o[u]=!0,b.each(e[u]||[],function(e,u){var c=u(n,r,i);return"string"!=typeof c||a||o[c]?a?!(l=c):t:(n.dataTypes.unshift(c),s(c),!1)}),l}return s(n.dataTypes[0])||!o["*"]&&s("*")}function Mn(e,n){var r,i,o=b.ajaxSettings.flatOptions||{};for(i in n)n[i]!==t&&((o[i]?e:r||(r={}))[i]=n[i]);return r&&b.extend(!0,e,r),e}b.fn.load=function(e,n,r){if("string"!=typeof e&&Sn)return Sn.apply(this,arguments);var i,o,a,s=this,u=e.indexOf(" ");return u>=0&&(i=e.slice(u,e.length),e=e.slice(0,u)),b.isFunction(n)?(r=n,n=t):n&&"object"==typeof n&&(a="POST"),s.length>0&&b.ajax({url:e,type:a,dataType:"html",data:n}).done(function(e){o=arguments,s.html(i?b("<div>").append(b.parseHTML(e)).find(i):e)}).complete(r&&function(e,t){s.each(r,o||[e.responseText,t,e])}),this},b.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(e,t){b.fn[t]=function(e){return this.on(t,e)}}),b.each(["get","post"],function(e,n){b[n]=function(e,r,i,o){return b.isFunction(r)&&(o=o||i,i=r,r=t),b.ajax({url:e,type:n,dataType:o,data:r,success:i})}}),b.extend({active:0,lastModified:{},etag:{},ajaxSettings:{url:yn,type:"GET",isLocal:Nn.test(mn[1]),global:!0,processData:!0,async:!0,contentType:"application/x-www-form-urlencoded; charset=UTF-8",accepts:{"*":Dn,text:"text/plain",html:"text/html",xml:"application/xml, text/xml",json:"application/json, text/javascript"},contents:{xml:/xml/,html:/html/,json:/json/},responseFields:{xml:"responseXML",text:"responseText"},converters:{"* text":e.String,"text html":!0,"text json":b.parseJSON,"text xml":b.parseXML},flatOptions:{url:!0,context:!0}},ajaxSetup:function(e,t){return t?Mn(Mn(e,b.ajaxSettings),t):Mn(b.ajaxSettings,e)},ajaxPrefilter:Hn(An),ajaxTransport:Hn(jn),ajax:function(e,n){"object"==typeof e&&(n=e,e=t),n=n||{};var r,i,o,a,s,u,l,c,p=b.ajaxSetup({},n),f=p.context||p,d=p.context&&(f.nodeType||f.jquery)?b(f):b.event,h=b.Deferred(),g=b.Callbacks("once memory"),m=p.statusCode||{},y={},v={},x=0,T="canceled",N={readyState:0,getResponseHeader:function(e){var t;if(2===x){if(!c){c={};while(t=Tn.exec(a))c[t[1].toLowerCase()]=t[2]}t=c[e.toLowerCase()]}return null==t?null:t},getAllResponseHeaders:function(){return 2===x?a:null},setRequestHeader:function(e,t){var n=e.toLowerCase();return x||(e=v[n]=v[n]||e,y[e]=t),this},overrideMimeType:function(e){return x||(p.mimeType=e),this},statusCode:function(e){var t;if(e)if(2>x)for(t in e)m[t]=[m[t],e[t]];else N.always(e[N.status]);return this},abort:function(e){var t=e||T;return l&&l.abort(t),k(0,t),this}};if(h.promise(N).complete=g.add,N.success=N.done,N.error=N.fail,p.url=((e||p.url||yn)+"").replace(xn,"").replace(kn,mn[1]+"//"),p.type=n.method||n.type||p.method||p.type,p.dataTypes=b.trim(p.dataType||"*").toLowerCase().match(w)||[""],null==p.crossDomain&&(r=En.exec(p.url.toLowerCase()),p.crossDomain=!(!r||r[1]===mn[1]&&r[2]===mn[2]&&(r[3]||("http:"===r[1]?80:443))==(mn[3]||("http:"===mn[1]?80:443)))),p.data&&p.processData&&"string"!=typeof p.data&&(p.data=b.param(p.data,p.traditional)),qn(An,p,n,N),2===x)return N;u=p.global,u&&0===b.active++&&b.event.trigger("ajaxStart"),p.type=p.type.toUpperCase(),p.hasContent=!Cn.test(p.type),o=p.url,p.hasContent||(p.data&&(o=p.url+=(bn.test(o)?"&":"?")+p.data,delete p.data),p.cache===!1&&(p.url=wn.test(o)?o.replace(wn,"$1_="+vn++):o+(bn.test(o)?"&":"?")+"_="+vn++)),p.ifModified&&(b.lastModified[o]&&N.setRequestHeader("If-Modified-Since",b.lastModified[o]),b.etag[o]&&N.setRequestHeader("If-None-Match",b.etag[o])),(p.data&&p.hasContent&&p.contentType!==!1||n.contentType)&&N.setRequestHeader("Content-Type",p.contentType),N.setRequestHeader("Accept",p.dataTypes[0]&&p.accepts[p.dataTypes[0]]?p.accepts[p.dataTypes[0]]+("*"!==p.dataTypes[0]?", "+Dn+"; q=0.01":""):p.accepts["*"]);for(i in p.headers)N.setRequestHeader(i,p.headers[i]);if(p.beforeSend&&(p.beforeSend.call(f,N,p)===!1||2===x))return N.abort();T="abort";for(i in{success:1,error:1,complete:1})N[i](p[i]);if(l=qn(jn,p,n,N)){N.readyState=1,u&&d.trigger("ajaxSend",[N,p]),p.async&&p.timeout>0&&(s=setTimeout(function(){N.abort("timeout")},p.timeout));try{x=1,l.send(y,k)}catch(C){if(!(2>x))throw C;k(-1,C)}}else k(-1,"No Transport");function k(e,n,r,i){var c,y,v,w,T,C=n;2!==x&&(x=2,s&&clearTimeout(s),l=t,a=i||"",N.readyState=e>0?4:0,r&&(w=_n(p,N,r)),e>=200&&300>e||304===e?(p.ifModified&&(T=N.getResponseHeader("Last-Modified"),T&&(b.lastModified[o]=T),T=N.getResponseHeader("etag"),T&&(b.etag[o]=T)),204===e?(c=!0,C="nocontent"):304===e?(c=!0,C="notmodified"):(c=Fn(p,w),C=c.state,y=c.data,v=c.error,c=!v)):(v=C,(e||!C)&&(C="error",0>e&&(e=0))),N.status=e,N.statusText=(n||C)+"",c?h.resolveWith(f,[y,C,N]):h.rejectWith(f,[N,C,v]),N.statusCode(m),m=t,u&&d.trigger(c?"ajaxSuccess":"ajaxError",[N,p,c?y:v]),g.fireWith(f,[N,C]),u&&(d.trigger("ajaxComplete",[N,p]),--b.active||b.event.trigger("ajaxStop")))}return N},getScript:function(e,n){return b.get(e,t,n,"script")},getJSON:function(e,t,n){return b.get(e,t,n,"json")}});function _n(e,n,r){var i,o,a,s,u=e.contents,l=e.dataTypes,c=e.responseFields;for(s in c)s in r&&(n[c[s]]=r[s]);while("*"===l[0])l.shift(),o===t&&(o=e.mimeType||n.getResponseHeader("Content-Type"));if(o)for(s in u)if(u[s]&&u[s].test(o)){l.unshift(s);break}if(l[0]in r)a=l[0];else{for(s in r){if(!l[0]||e.converters[s+" "+l[0]]){a=s;break}i||(i=s)}a=a||i}return a?(a!==l[0]&&l.unshift(a),r[a]):t}function Fn(e,t){var n,r,i,o,a={},s=0,u=e.dataTypes.slice(),l=u[0];if(e.dataFilter&&(t=e.dataFilter(t,e.dataType)),u[1])for(i in e.converters)a[i.toLowerCase()]=e.converters[i];for(;r=u[++s];)if("*"!==r){if("*"!==l&&l!==r){if(i=a[l+" "+r]||a["* "+r],!i)for(n in a)if(o=n.split(" "),o[1]===r&&(i=a[l+" "+o[0]]||a["* "+o[0]])){i===!0?i=a[n]:a[n]!==!0&&(r=o[0],u.splice(s--,0,r));break}if(i!==!0)if(i&&e["throws"])t=i(t);else try{t=i(t)}catch(c){return{state:"parsererror",error:i?c:"No conversion from "+l+" to "+r}}}l=r}return{state:"success",data:t}}b.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/(?:java|ecma)script/},converters:{"text script":function(e){return b.globalEval(e),e}}}),b.ajaxPrefilter("script",function(e){e.cache===t&&(e.cache=!1),e.crossDomain&&(e.type="GET",e.global=!1)}),b.ajaxTransport("script",function(e){if(e.crossDomain){var n,r=o.head||b("head")[0]||o.documentElement;return{send:function(t,i){n=o.createElement("script"),n.async=!0,e.scriptCharset&&(n.charset=e.scriptCharset),n.src=e.url,n.onload=n.onreadystatechange=function(e,t){(t||!n.readyState||/loaded|complete/.test(n.readyState))&&(n.onload=n.onreadystatechange=null,n.parentNode&&n.parentNode.removeChild(n),n=null,t||i(200,"success"))},r.insertBefore(n,r.firstChild)},abort:function(){n&&n.onload(t,!0)}}}});var On=[],Bn=/(=)\?(?=&|$)|\?\?/;b.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var e=On.pop()||b.expando+"_"+vn++;return this[e]=!0,e}}),b.ajaxPrefilter("json jsonp",function(n,r,i){var o,a,s,u=n.jsonp!==!1&&(Bn.test(n.url)?"url":"string"==typeof n.data&&!(n.contentType||"").indexOf("application/x-www-form-urlencoded")&&Bn.test(n.data)&&"data");return u||"jsonp"===n.dataTypes[0]?(o=n.jsonpCallback=b.isFunction(n.jsonpCallback)?n.jsonpCallback():n.jsonpCallback,u?n[u]=n[u].replace(Bn,"$1"+o):n.jsonp!==!1&&(n.url+=(bn.test(n.url)?"&":"?")+n.jsonp+"="+o),n.converters["script json"]=function(){return s||b.error(o+" was not called"),s[0]},n.dataTypes[0]="json",a=e[o],e[o]=function(){s=arguments},i.always(function(){e[o]=a,n[o]&&(n.jsonpCallback=r.jsonpCallback,On.push(o)),s&&b.isFunction(a)&&a(s[0]),s=a=t}),"script"):t});var Pn,Rn,Wn=0,$n=e.ActiveXObject&&function(){var e;for(e in Pn)Pn[e](t,!0)};function In(){try{return new e.XMLHttpRequest}catch(t){}}function zn(){try{return new e.ActiveXObject("Microsoft.XMLHTTP")}catch(t){}}b.ajaxSettings.xhr=e.ActiveXObject?function(){return!this.isLocal&&In()||zn()}:In,Rn=b.ajaxSettings.xhr(),b.support.cors=!!Rn&&"withCredentials"in Rn,Rn=b.support.ajax=!!Rn,Rn&&b.ajaxTransport(function(n){if(!n.crossDomain||b.support.cors){var r;return{send:function(i,o){var a,s,u=n.xhr();if(n.username?u.open(n.type,n.url,n.async,n.username,n.password):u.open(n.type,n.url,n.async),n.xhrFields)for(s in n.xhrFields)u[s]=n.xhrFields[s];n.mimeType&&u.overrideMimeType&&u.overrideMimeType(n.mimeType),n.crossDomain||i["X-Requested-With"]||(i["X-Requested-With"]="XMLHttpRequest");try{for(s in i)u.setRequestHeader(s,i[s])}catch(l){}u.send(n.hasContent&&n.data||null),r=function(e,i){var s,l,c,p;try{if(r&&(i||4===u.readyState))if(r=t,a&&(u.onreadystatechange=b.noop,$n&&delete Pn[a]),i)4!==u.readyState&&u.abort();else{p={},s=u.status,l=u.getAllResponseHeaders(),"string"==typeof u.responseText&&(p.text=u.responseText);try{c=u.statusText}catch(f){c=""}s||!n.isLocal||n.crossDomain?1223===s&&(s=204):s=p.text?200:404}}catch(d){i||o(-1,d)}p&&o(s,c,p,l)},n.async?4===u.readyState?setTimeout(r):(a=++Wn,$n&&(Pn||(Pn={},b(e).unload($n)),Pn[a]=r),u.onreadystatechange=r):r()},abort:function(){r&&r(t,!0)}}}});var Xn,Un,Vn=/^(?:toggle|show|hide)$/,Yn=RegExp("^(?:([+-])=|)("+x+")([a-z%]*)$","i"),Jn=/queueHooks$/,Gn=[nr],Qn={"*":[function(e,t){var n,r,i=this.createTween(e,t),o=Yn.exec(t),a=i.cur(),s=+a||0,u=1,l=20;if(o){if(n=+o[2],r=o[3]||(b.cssNumber[e]?"":"px"),"px"!==r&&s){s=b.css(i.elem,e,!0)||n||1;do u=u||".5",s/=u,b.style(i.elem,e,s+r);while(u!==(u=i.cur()/a)&&1!==u&&--l)}i.unit=r,i.start=s,i.end=o[1]?s+(o[1]+1)*n:n}return i}]};function Kn(){return setTimeout(function(){Xn=t}),Xn=b.now()}function Zn(e,t){b.each(t,function(t,n){var r=(Qn[t]||[]).concat(Qn["*"]),i=0,o=r.length;for(;o>i;i++)if(r[i].call(e,t,n))return})}function er(e,t,n){var r,i,o=0,a=Gn.length,s=b.Deferred().always(function(){delete u.elem}),u=function(){if(i)return!1;var t=Xn||Kn(),n=Math.max(0,l.startTime+l.duration-t),r=n/l.duration||0,o=1-r,a=0,u=l.tweens.length;for(;u>a;a++)l.tweens[a].run(o);return s.notifyWith(e,[l,o,n]),1>o&&u?n:(s.resolveWith(e,[l]),!1)},l=s.promise({elem:e,props:b.extend({},t),opts:b.extend(!0,{specialEasing:{}},n),originalProperties:t,originalOptions:n,startTime:Xn||Kn(),duration:n.duration,tweens:[],createTween:function(t,n){var r=b.Tween(e,l.opts,t,n,l.opts.specialEasing[t]||l.opts.easing);return l.tweens.push(r),r},stop:function(t){var n=0,r=t?l.tweens.length:0;if(i)return this;for(i=!0;r>n;n++)l.tweens[n].run(1);return t?s.resolveWith(e,[l,t]):s.rejectWith(e,[l,t]),this}}),c=l.props;for(tr(c,l.opts.specialEasing);a>o;o++)if(r=Gn[o].call(l,e,c,l.opts))return r;return Zn(l,c),b.isFunction(l.opts.start)&&l.opts.start.call(e,l),b.fx.timer(b.extend(u,{elem:e,anim:l,queue:l.opts.queue})),l.progress(l.opts.progress).done(l.opts.done,l.opts.complete).fail(l.opts.fail).always(l.opts.always)}function tr(e,t){var n,r,i,o,a;for(i in e)if(r=b.camelCase(i),o=t[r],n=e[i],b.isArray(n)&&(o=n[1],n=e[i]=n[0]),i!==r&&(e[r]=n,delete e[i]),a=b.cssHooks[r],a&&"expand"in a){n=a.expand(n),delete e[r];for(i in n)i in e||(e[i]=n[i],t[i]=o)}else t[r]=o}b.Animation=b.extend(er,{tweener:function(e,t){b.isFunction(e)?(t=e,e=["*"]):e=e.split(" ");var n,r=0,i=e.length;for(;i>r;r++)n=e[r],Qn[n]=Qn[n]||[],Qn[n].unshift(t)},prefilter:function(e,t){t?Gn.unshift(e):Gn.push(e)}});function nr(e,t,n){var r,i,o,a,s,u,l,c,p,f=this,d=e.style,h={},g=[],m=e.nodeType&&nn(e);n.queue||(c=b._queueHooks(e,"fx"),null==c.unqueued&&(c.unqueued=0,p=c.empty.fire,c.empty.fire=function(){c.unqueued||p()}),c.unqueued++,f.always(function(){f.always(function(){c.unqueued--,b.queue(e,"fx").length||c.empty.fire()})})),1===e.nodeType&&("height"in t||"width"in t)&&(n.overflow=[d.overflow,d.overflowX,d.overflowY],"inline"===b.css(e,"display")&&"none"===b.css(e,"float")&&(b.support.inlineBlockNeedsLayout&&"inline"!==un(e.nodeName)?d.zoom=1:d.display="inline-block")),n.overflow&&(d.overflow="hidden",b.support.shrinkWrapBlocks||f.always(function(){d.overflow=n.overflow[0],d.overflowX=n.overflow[1],d.overflowY=n.overflow[2]}));for(i in t)if(a=t[i],Vn.exec(a)){if(delete t[i],u=u||"toggle"===a,a===(m?"hide":"show"))continue;g.push(i)}if(o=g.length){s=b._data(e,"fxshow")||b._data(e,"fxshow",{}),"hidden"in s&&(m=s.hidden),u&&(s.hidden=!m),m?b(e).show():f.done(function(){b(e).hide()}),f.done(function(){var t;b._removeData(e,"fxshow");for(t in h)b.style(e,t,h[t])});for(i=0;o>i;i++)r=g[i],l=f.createTween(r,m?s[r]:0),h[r]=s[r]||b.style(e,r),r in s||(s[r]=l.start,m&&(l.end=l.start,l.start="width"===r||"height"===r?1:0))}}function rr(e,t,n,r,i){return new rr.prototype.init(e,t,n,r,i)}b.Tween=rr,rr.prototype={constructor:rr,init:function(e,t,n,r,i,o){this.elem=e,this.prop=n,this.easing=i||"swing",this.options=t,this.start=this.now=this.cur(),this.end=r,this.unit=o||(b.cssNumber[n]?"":"px")},cur:function(){var e=rr.propHooks[this.prop];return e&&e.get?e.get(this):rr.propHooks._default.get(this)},run:function(e){var t,n=rr.propHooks[this.prop];return this.pos=t=this.options.duration?b.easing[this.easing](e,this.options.duration*e,0,1,this.options.duration):e,this.now=(this.end-this.start)*t+this.start,this.options.step&&this.options.step.call(this.elem,this.now,this),n&&n.set?n.set(this):rr.propHooks._default.set(this),this}},rr.prototype.init.prototype=rr.prototype,rr.propHooks={_default:{get:function(e){var t;return null==e.elem[e.prop]||e.elem.style&&null!=e.elem.style[e.prop]?(t=b.css(e.elem,e.prop,""),t&&"auto"!==t?t:0):e.elem[e.prop]},set:function(e){b.fx.step[e.prop]?b.fx.step[e.prop](e):e.elem.style&&(null!=e.elem.style[b.cssProps[e.prop]]||b.cssHooks[e.prop])?b.style(e.elem,e.prop,e.now+e.unit):e.elem[e.prop]=e.now}}},rr.propHooks.scrollTop=rr.propHooks.scrollLeft={set:function(e){e.elem.nodeType&&e.elem.parentNode&&(e.elem[e.prop]=e.now)}},b.each(["toggle","show","hide"],function(e,t){var n=b.fn[t];b.fn[t]=function(e,r,i){return null==e||"boolean"==typeof e?n.apply(this,arguments):this.animate(ir(t,!0),e,r,i)}}),b.fn.extend({fadeTo:function(e,t,n,r){return this.filter(nn).css("opacity",0).show().end().animate({opacity:t},e,n,r)},animate:function(e,t,n,r){var i=b.isEmptyObject(e),o=b.speed(t,n,r),a=function(){var t=er(this,b.extend({},e),o);a.finish=function(){t.stop(!0)},(i||b._data(this,"finish"))&&t.stop(!0)};return a.finish=a,i||o.queue===!1?this.each(a):this.queue(o.queue,a)},stop:function(e,n,r){var i=function(e){var t=e.stop;delete e.stop,t(r)};return"string"!=typeof e&&(r=n,n=e,e=t),n&&e!==!1&&this.queue(e||"fx",[]),this.each(function(){var t=!0,n=null!=e&&e+"queueHooks",o=b.timers,a=b._data(this);if(n)a[n]&&a[n].stop&&i(a[n]);else for(n in a)a[n]&&a[n].stop&&Jn.test(n)&&i(a[n]);for(n=o.length;n--;)o[n].elem!==this||null!=e&&o[n].queue!==e||(o[n].anim.stop(r),t=!1,o.splice(n,1));(t||!r)&&b.dequeue(this,e)})},finish:function(e){return e!==!1&&(e=e||"fx"),this.each(function(){var t,n=b._data(this),r=n[e+"queue"],i=n[e+"queueHooks"],o=b.timers,a=r?r.length:0;for(n.finish=!0,b.queue(this,e,[]),i&&i.cur&&i.cur.finish&&i.cur.finish.call(this),t=o.length;t--;)o[t].elem===this&&o[t].queue===e&&(o[t].anim.stop(!0),o.splice(t,1));for(t=0;a>t;t++)r[t]&&r[t].finish&&r[t].finish.call(this);delete n.finish})}});function ir(e,t){var n,r={height:e},i=0;for(t=t?1:0;4>i;i+=2-t)n=Zt[i],r["margin"+n]=r["padding"+n]=e;return t&&(r.opacity=r.width=e),r}b.each({slideDown:ir("show"),slideUp:ir("hide"),slideToggle:ir("toggle"),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"},fadeToggle:{opacity:"toggle"}},function(e,t){b.fn[e]=function(e,n,r){return this.animate(t,e,n,r)}}),b.speed=function(e,t,n){var r=e&&"object"==typeof e?b.extend({},e):{complete:n||!n&&t||b.isFunction(e)&&e,duration:e,easing:n&&t||t&&!b.isFunction(t)&&t};return r.duration=b.fx.off?0:"number"==typeof r.duration?r.duration:r.duration in b.fx.speeds?b.fx.speeds[r.duration]:b.fx.speeds._default,(null==r.queue||r.queue===!0)&&(r.queue="fx"),r.old=r.complete,r.complete=function(){b.isFunction(r.old)&&r.old.call(this),r.queue&&b.dequeue(this,r.queue)},r},b.easing={linear:function(e){return e},swing:function(e){return.5-Math.cos(e*Math.PI)/2}},b.timers=[],b.fx=rr.prototype.init,b.fx.tick=function(){var e,n=b.timers,r=0;for(Xn=b.now();n.length>r;r++)e=n[r],e()||n[r]!==e||n.splice(r--,1);n.length||b.fx.stop(),Xn=t},b.fx.timer=function(e){e()&&b.timers.push(e)&&b.fx.start()},b.fx.interval=13,b.fx.start=function(){Un||(Un=setInterval(b.fx.tick,b.fx.interval))},b.fx.stop=function(){clearInterval(Un),Un=null},b.fx.speeds={slow:600,fast:200,_default:400},b.fx.step={},b.expr&&b.expr.filters&&(b.expr.filters.animated=function(e){return b.grep(b.timers,function(t){return e===t.elem}).length}),b.fn.offset=function(e){if(arguments.length)return e===t?this:this.each(function(t){b.offset.setOffset(this,e,t)});var n,r,o={top:0,left:0},a=this[0],s=a&&a.ownerDocument;if(s)return n=s.documentElement,b.contains(n,a)?(typeof a.getBoundingClientRect!==i&&(o=a.getBoundingClientRect()),r=or(s),{top:o.top+(r.pageYOffset||n.scrollTop)-(n.clientTop||0),left:o.left+(r.pageXOffset||n.scrollLeft)-(n.clientLeft||0)}):o},b.offset={setOffset:function(e,t,n){var r=b.css(e,"position");"static"===r&&(e.style.position="relative");var i=b(e),o=i.offset(),a=b.css(e,"top"),s=b.css(e,"left"),u=("absolute"===r||"fixed"===r)&&b.inArray("auto",[a,s])>-1,l={},c={},p,f;u?(c=i.position(),p=c.top,f=c.left):(p=parseFloat(a)||0,f=parseFloat(s)||0),b.isFunction(t)&&(t=t.call(e,n,o)),null!=t.top&&(l.top=t.top-o.top+p),null!=t.left&&(l.left=t.left-o.left+f),"using"in t?t.using.call(e,l):i.css(l)}},b.fn.extend({position:function(){if(this[0]){var e,t,n={top:0,left:0},r=this[0];return"fixed"===b.css(r,"position")?t=r.getBoundingClientRect():(e=this.offsetParent(),t=this.offset(),b.nodeName(e[0],"html")||(n=e.offset()),n.top+=b.css(e[0],"borderTopWidth",!0),n.left+=b.css(e[0],"borderLeftWidth",!0)),{top:t.top-n.top-b.css(r,"marginTop",!0),left:t.left-n.left-b.css(r,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){var e=this.offsetParent||o.documentElement;while(e&&!b.nodeName(e,"html")&&"static"===b.css(e,"position"))e=e.offsetParent;return e||o.documentElement})}}),b.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(e,n){var r=/Y/.test(n);b.fn[e]=function(i){return b.access(this,function(e,i,o){var a=or(e);return o===t?a?n in a?a[n]:a.document.documentElement[i]:e[i]:(a?a.scrollTo(r?b(a).scrollLeft():o,r?o:b(a).scrollTop()):e[i]=o,t)},e,i,arguments.length,null)}});function or(e){return b.isWindow(e)?e:9===e.nodeType?e.defaultView||e.parentWindow:!1}b.each({Height:"height",Width:"width"},function(e,n){b.each({padding:"inner"+e,content:n,"":"outer"+e},function(r,i){b.fn[i]=function(i,o){var a=arguments.length&&(r||"boolean"!=typeof i),s=r||(i===!0||o===!0?"margin":"border");return b.access(this,function(n,r,i){var o;return b.isWindow(n)?n.document.documentElement["client"+e]:9===n.nodeType?(o=n.documentElement,Math.max(n.body["scroll"+e],o["scroll"+e],n.body["offset"+e],o["offset"+e],o["client"+e])):i===t?b.css(n,r,s):b.style(n,r,i,s)},n,a?i:t,a,null)}})}),e.jQuery=e.$=b,"function"==typeof define&&define.amd&&define.amd.jQuery&&define("jquery",[],function(){return b})})(window);
\ No newline at end of file
diff --git a/js/jquery.masonry.min.js b/js/jquery.masonry.min.js
deleted file mode 100644 (file)
index 57c081c..0000000
+++ /dev/null
@@ -1,10 +0,0 @@
-/**
- * jQuery Masonry v2.1.08
- * A dynamic layout plugin for jQuery
- * The flip-side of CSS Floats
- * http://masonry.desandro.com
- *
- * Licensed under the MIT license.
- * Copyright 2012 David DeSandro
- */
-(function(e,t,n){"use strict";var r=t.event,i;r.special.smartresize={setup:function(){t(this).bind("resize",r.special.smartresize.handler)},teardown:function(){t(this).unbind("resize",r.special.smartresize.handler)},handler:function(e,t){var n=this,s=arguments;e.type="smartresize",i&&clearTimeout(i),i=setTimeout(function(){r.dispatch.apply(n,s)},t==="execAsap"?0:100)}},t.fn.smartresize=function(e){return e?this.bind("smartresize",e):this.trigger("smartresize",["execAsap"])},t.Mason=function(e,n){this.element=t(n),this._create(e),this._init()},t.Mason.settings={isResizable:!0,isAnimated:!1,animationOptions:{queue:!1,duration:500},gutterWidth:0,isRTL:!1,isFitWidth:!1,containerStyle:{position:"relative"}},t.Mason.prototype={_filterFindBricks:function(e){var t=this.options.itemSelector;return t?e.filter(t).add(e.find(t)):e},_getBricks:function(e){var t=this._filterFindBricks(e).css({position:"absolute"}).addClass("masonry-brick");return t},_create:function(n){this.options=t.extend(!0,{},t.Mason.settings,n),this.styleQueue=[];var r=this.element[0].style;this.originalStyle={height:r.height||""};var i=this.options.containerStyle;for(var s in i)this.originalStyle[s]=r[s]||"";this.element.css(i),this.horizontalDirection=this.options.isRTL?"right":"left";var o=this.element.css("padding-"+this.horizontalDirection),u=this.element.css("padding-top");this.offset={x:o?parseInt(o,10):0,y:u?parseInt(u,10):0},this.isFluid=this.options.columnWidth&&typeof this.options.columnWidth=="function";var a=this;setTimeout(function(){a.element.addClass("masonry")},0),this.options.isResizable&&t(e).bind("smartresize.masonry",function(){a.resize()}),this.reloadItems()},_init:function(e){this._getColumns(),this._reLayout(e)},option:function(e,n){t.isPlainObject(e)&&(this.options=t.extend(!0,this.options,e))},layout:function(e,t){for(var n=0,r=e.length;n<r;n++)this._placeBrick(e[n]);var i={};i.height=Math.max.apply(Math,this.colYs);if(this.options.isFitWidth){var s=0;n=this.cols;while(--n){if(this.colYs[n]!==0)break;s++}i.width=(this.cols-s)*this.columnWidth-this.options.gutterWidth}this.styleQueue.push({$el:this.element,style:i});var o=this.isLaidOut?this.options.isAnimated?"animate":"css":"css",u=this.options.animationOptions,a;for(n=0,r=this.styleQueue.length;n<r;n++)a=this.styleQueue[n],a.$el[o](a.style,u);this.styleQueue=[],t&&t.call(e),this.isLaidOut=!0},_getColumns:function(){var e=this.options.isFitWidth?this.element.parent():this.element,t=e.width();this.columnWidth=this.isFluid?this.options.columnWidth(t):this.options.columnWidth||this.$bricks.outerWidth(!0)||t,this.columnWidth+=this.options.gutterWidth,this.cols=Math.floor((t+this.options.gutterWidth)/this.columnWidth),this.cols=Math.max(this.cols,1)},_placeBrick:function(e){var n=t(e),r,i,s,o,u;r=Math.ceil(n.outerWidth(!0)/this.columnWidth),r=Math.min(r,this.cols);if(r===1)s=this.colYs;else{i=this.cols+1-r,s=[];for(u=0;u<i;u++)o=this.colYs.slice(u,u+r),s[u]=Math.max.apply(Math,o)}var a=Math.min.apply(Math,s),f=0;for(var l=0,c=s.length;l<c;l++)if(s[l]===a){f=l;break}var h={top:a+this.offset.y};h[this.horizontalDirection]=this.columnWidth*f+this.offset.x,this.styleQueue.push({$el:n,style:h});var p=a+n.outerHeight(!0),d=this.cols+1-c;for(l=0;l<d;l++)this.colYs[f+l]=p},resize:function(){var e=this.cols;this._getColumns(),(this.isFluid||this.cols!==e)&&this._reLayout()},_reLayout:function(e){var t=this.cols;this.colYs=[];while(t--)this.colYs.push(0);this.layout(this.$bricks,e)},reloadItems:function(){this.$bricks=this._getBricks(this.element.children())},reload:function(e){this.reloadItems(),this._init(e)},appended:function(e,t,n){if(t){this._filterFindBricks(e).css({top:this.element.height()});var r=this;setTimeout(function(){r._appended(e,n)},1)}else this._appended(e,n)},_appended:function(e,t){var n=this._getBricks(e);this.$bricks=this.$bricks.add(n),this.layout(n,t)},remove:function(e){this.$bricks=this.$bricks.not(e),e.remove()},destroy:function(){this.$bricks.removeClass("masonry-brick").each(function(){this.style.position="",this.style.top="",this.style.left=""});var n=this.element[0].style;for(var r in this.originalStyle)n[r]=this.originalStyle[r];this.element.unbind(".masonry").removeClass("masonry").removeData("masonry"),t(e).unbind(".masonry")}},t.fn.imagesLoaded=function(e){function u(){e.call(n,r)}function a(e){var n=e.target;n.src!==s&&t.inArray(n,o)===-1&&(o.push(n),--i<=0&&(setTimeout(u),r.unbind(".imagesLoaded",a)))}var n=this,r=n.find("img").add(n.filter("img")),i=r.length,s="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///ywAAAAAAQABAAACAUwAOw==",o=[];return i||u(),r.bind("load.imagesLoaded error.imagesLoaded",a).each(function(){var e=this.src;this.src=s,this.src=e}),n};var s=function(t){e.console&&e.console.error(t)};t.fn.masonry=function(e){if(typeof e=="string"){var n=Array.prototype.slice.call(arguments,1);this.each(function(){var r=t.data(this,"masonry");if(!r){s("cannot call methods on masonry prior to initialization; attempted to call method '"+e+"'");return}if(!t.isFunction(r[e])||e.charAt(0)==="_"){s("no such method '"+e+"' for masonry instance");return}r[e].apply(r,n)})}else this.each(function(){var n=t.data(this,"masonry");n?(n.option(e||{}),n._init()):t.data(this,"masonry",new t.Mason(e,this))});return this}})(window,jQuery);
\ No newline at end of file
diff --git a/js/poche.js b/js/poche.js
deleted file mode 100644 (file)
index 97d9911..0000000
+++ /dev/null
@@ -1,57 +0,0 @@
-function toggle_favorite(element, id) {
-    $(element).toggleClass('fav-off');
-    $.ajax ({
-        url: "index.php?action=toggle_fav",
-        data:{id:id}
-    });
-}
-
-function toggle_archive(element, id, view_article) {
-    $(element).toggleClass('archive-off');
-    $.ajax ({
-        url: "index.php?action=toggle_archive",
-        data:{id:id}
-    });
-    var obj = $('#entry-'+id);
-
-    // on vient de la vue de l'article, donc pas de gestion de grille
-    if (view_article != 1) {
-        $('#content').masonry('remove',obj);
-        $('#content').masonry('reloadItems');
-        $('#content').masonry('reload');
-    }
-}
-
-function sort_links(view, sort) {
-    $.get('index.php', { view: view, sort: sort, full_head: 'no' }, function(data) {
-      $('#content').html(data);
-    });
-}
-
-
-// ---------- Swith light or dark view
-function setActiveStyleSheet(title) {
-       var i, a, main;
-       for(i=0; (a = document.getElementsByTagName("link")[i]); i++) {
-               if(a.getAttribute("rel").indexOf("style") != -1 && a.getAttribute("title")) {
-                       a.disabled = true;
-                       if(a.getAttribute("title") == title) a.disabled = false;
-               }
-       }
-}
-$('#themeswitch').click(function() {
-       // we want the dark
-       if ($('body').hasClass('light-style')) {
-               setActiveStyleSheet('dark-style');
-               $('body').addClass('dark-style');
-               $('body').removeClass('light-style');
-               $('#themeswitch').text('light');
-       // we want the light
-       } else if ($('body').hasClass('dark-style')) {
-               setActiveStyleSheet('light-style');
-               $('body').addClass('light-style');
-               $('body').removeClass('dark-style');
-               $('#themeswitch').text('dark');
-       }
-       return false;
-});
diff --git a/locale/fr_FR.UTF8/LC_MESSAGES/fr_FR.UTF8.mo b/locale/fr_FR.UTF8/LC_MESSAGES/fr_FR.UTF8.mo
new file mode 100644 (file)
index 0000000..c0d4a9d
Binary files /dev/null and b/locale/fr_FR.UTF8/LC_MESSAGES/fr_FR.UTF8.mo differ
diff --git a/locale/fr_FR.UTF8/LC_MESSAGES/fr_FR.UTF8.po b/locale/fr_FR.UTF8/LC_MESSAGES/fr_FR.UTF8.po
new file mode 100644 (file)
index 0000000..7f8cf78
--- /dev/null
@@ -0,0 +1,376 @@
+msgid ""
+msgstr ""
+"Project-Id-Version: poche\n"
+"POT-Creation-Date: 2013-08-06 08:35+0100\n"
+"PO-Revision-Date: 2013-08-06 08:35+0100\n"
+"Last-Translator: Nicolas Lœuillet <nicolas.loeuillet@gmail.com>\n"
+"Language-Team: poche <support@inthepoche.com>\n"
+"Language: Français\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"X-Generator: Poedit 1.5.4\n"
+"X-Poedit-KeywordsList: _;gettext;gettext_noop\n"
+"X-Poedit-Basepath: /\n"
+"Plural-Forms: nplurals=2; plural=(n > 1);\n"
+"X-Poedit-SourceCharset: UTF-8\n"
+"X-Poedit-SearchPath-0: /var/www/poche-i18n\n"
+
+#: /var/www/poche-i18n/index.php:43
+msgid "poche, a read it later open source system"
+msgstr "poche, a read it later open source system"
+
+#: /var/www/poche-i18n/inc/poche/Poche.class.php:101
+msgid "the link has been added successfully"
+msgstr "le lien a été ajouté avec succès"
+
+#: /var/www/poche-i18n/inc/poche/Poche.class.php:104
+msgid "error during insertion : the link wasn't added"
+msgstr "erreur durant l'insertion : le lien n'a pas été ajouté"
+
+#: /var/www/poche-i18n/inc/poche/Poche.class.php:109
+msgid "error during fetching content : the link wasn't added"
+msgstr "erreur durant la récupération du contenu : le lien n'a pas été ajouté"
+
+#: /var/www/poche-i18n/inc/poche/Poche.class.php:119
+msgid "the link has been deleted successfully"
+msgstr "le lien a été supprimé avec succès"
+
+#: /var/www/poche-i18n/inc/poche/Poche.class.php:123
+msgid "the link wasn't deleted"
+msgstr "le lien n'a pas été supprimé"
+
+#: /var/www/poche-i18n/inc/poche/Tools.class.php:18
+msgid "Oops, it seems you don't have PHP 5."
+msgstr "Oups, il semblerait que PHP 5 ne soit pas installé. "
+
+#: /var/www/poche-i18n/cache/c9/b0/845a8dc93165e6c00b6b43068799.php:32
+#: /var/www/poche-i18n/cache/c9/b0/845a8dc93165e6c00b6b43068799.php:70
+#: /var/www/poche-i18n/cache/76/a4/e7c21f2e0ba29104fc654cd8ba41.php:50
+msgid "config"
+msgstr "config"
+
+#: /var/www/poche-i18n/cache/c9/b0/845a8dc93165e6c00b6b43068799.php:46
+#: /var/www/poche-i18n/cache/7a/1e/68e6b4aec1301ae024cc85232e7c.php:31
+#: /var/www/poche-i18n/cache/76/a4/e7c21f2e0ba29104fc654cd8ba41.php:26
+#: /var/www/poche-i18n/cache/dd/a8/530129765655dcde0a70a31a78b6.php:34
+msgid "home"
+msgstr "accueil"
+
+#: /var/www/poche-i18n/cache/c9/b0/845a8dc93165e6c00b6b43068799.php:54
+#: /var/www/poche-i18n/cache/76/a4/e7c21f2e0ba29104fc654cd8ba41.php:34
+msgid "favorites"
+msgstr "favoris"
+
+#: /var/www/poche-i18n/cache/c9/b0/845a8dc93165e6c00b6b43068799.php:62
+#: /var/www/poche-i18n/cache/76/a4/e7c21f2e0ba29104fc654cd8ba41.php:42
+msgid "archive"
+msgstr "archives"
+
+#: /var/www/poche-i18n/cache/c9/b0/845a8dc93165e6c00b6b43068799.php:74
+#: /var/www/poche-i18n/cache/c9/b0/845a8dc93165e6c00b6b43068799.php:76
+#: /var/www/poche-i18n/cache/76/a4/e7c21f2e0ba29104fc654cd8ba41.php:54
+#: /var/www/poche-i18n/cache/76/a4/e7c21f2e0ba29104fc654cd8ba41.php:56
+msgid "logout"
+msgstr "déconnexion"
+
+#: /var/www/poche-i18n/cache/c9/b0/845a8dc93165e6c00b6b43068799.php:87
+msgid "Bookmarklet"
+msgstr "Bookmarklet"
+
+#: /var/www/poche-i18n/cache/c9/b0/845a8dc93165e6c00b6b43068799.php:91
+msgid ""
+"Thanks to the bookmarklet, you will be able to easily add a link to your "
+"poche."
+msgstr ""
+"Grâce au bookmarklet, vous pouvez ajouter facilement un lien dans votre "
+"poche."
+
+#: /var/www/poche-i18n/cache/c9/b0/845a8dc93165e6c00b6b43068799.php:93
+msgid "Have a look to this documentation:"
+msgstr "Jetez un œil à la documentation :"
+
+#: /var/www/poche-i18n/cache/c9/b0/845a8dc93165e6c00b6b43068799.php:97
+msgid "Drag & drop this link to your bookmarks bar and have fun with poche."
+msgstr ""
+"Glissez / déposez ce lien dans votre barre de favoris de votre navigateur et "
+"prenez du bon temps avec poche."
+
+#: /var/www/poche-i18n/cache/c9/b0/845a8dc93165e6c00b6b43068799.php:103
+msgid "poche it!"
+msgstr "poche-le !"
+
+#: /var/www/poche-i18n/cache/c9/b0/845a8dc93165e6c00b6b43068799.php:108
+msgid "Updating poche"
+msgstr "Mettre à jour poche"
+
+#: /var/www/poche-i18n/cache/c9/b0/845a8dc93165e6c00b6b43068799.php:113
+msgid "your version"
+msgstr "votre version"
+
+#: /var/www/poche-i18n/cache/c9/b0/845a8dc93165e6c00b6b43068799.php:119
+msgid "latest stable version"
+msgstr "dernière version stable"
+
+#: /var/www/poche-i18n/cache/c9/b0/845a8dc93165e6c00b6b43068799.php:125
+msgid "a more recent stable version is available."
+msgstr "une version stable plus récente est disponible."
+
+#: /var/www/poche-i18n/cache/c9/b0/845a8dc93165e6c00b6b43068799.php:128
+#: /var/www/poche-i18n/cache/c9/b0/845a8dc93165e6c00b6b43068799.php:142
+msgid "you are up to date."
+msgstr "vous êtes à jour."
+
+#: /var/www/poche-i18n/cache/c9/b0/845a8dc93165e6c00b6b43068799.php:133
+msgid "latest dev version"
+msgstr "dernière version de développement"
+
+#: /var/www/poche-i18n/cache/c9/b0/845a8dc93165e6c00b6b43068799.php:139
+msgid "a more recent development version is available."
+msgstr "une version de développement plus récente est disponible."
+
+#: /var/www/poche-i18n/cache/c9/b0/845a8dc93165e6c00b6b43068799.php:150
+msgid "Change your password"
+msgstr "Modifier votre mot de passe"
+
+#: /var/www/poche-i18n/cache/c9/b0/845a8dc93165e6c00b6b43068799.php:157
+msgid "New password:"
+msgstr "Nouveau mot de passe :"
+
+#: /var/www/poche-i18n/cache/c9/b0/845a8dc93165e6c00b6b43068799.php:161
+#: /var/www/poche-i18n/cache/c9/b0/845a8dc93165e6c00b6b43068799.php:171
+#: /var/www/poche-i18n/cache/d4/28/e0d08991ec2d8a7b133505e7c651.php:60
+#: /var/www/poche-i18n/cache/ae/26/05eb67771213c16bd8c9aaf2d2c4.php:68
+msgid "Password"
+msgstr "Mot de passe"
+
+#: /var/www/poche-i18n/cache/c9/b0/845a8dc93165e6c00b6b43068799.php:167
+msgid "Repeat your new password:"
+msgstr "Répétez le nouveau mot de passe :"
+
+#: /var/www/poche-i18n/cache/c9/b0/845a8dc93165e6c00b6b43068799.php:177
+msgid "Update"
+msgstr "Mettre à jour"
+
+#: /var/www/poche-i18n/cache/c9/b0/845a8dc93165e6c00b6b43068799.php:193
+msgid "Import"
+msgstr "Import"
+
+#: /var/www/poche-i18n/cache/c9/b0/845a8dc93165e6c00b6b43068799.php:197
+msgid "Please execute the import script locally, it can take a very long time."
+msgstr "Merci d'exécuter l'import en local, cela peut prendre du temps. "
+
+#: /var/www/poche-i18n/cache/c9/b0/845a8dc93165e6c00b6b43068799.php:201
+msgid "More infos in the official doc:"
+msgstr "Plus d'infos sur la documentation officielle :"
+
+#: /var/www/poche-i18n/cache/c9/b0/845a8dc93165e6c00b6b43068799.php:206
+msgid "import from Pocket"
+msgstr "l'import depuis Pocket est terminé."
+
+#: /var/www/poche-i18n/cache/c9/b0/845a8dc93165e6c00b6b43068799.php:210
+msgid "import from Readability"
+msgstr "l'import depuis Readability est terminé."
+
+#: /var/www/poche-i18n/cache/c9/b0/845a8dc93165e6c00b6b43068799.php:214
+msgid "import from Instapaper"
+msgstr "Import depuis Instapaper"
+
+#: /var/www/poche-i18n/cache/c9/b0/845a8dc93165e6c00b6b43068799.php:220
+msgid "Export your poche datas"
+msgstr "Exporter vos données de poche"
+
+#: /var/www/poche-i18n/cache/c9/b0/845a8dc93165e6c00b6b43068799.php:224
+msgid "Click here"
+msgstr "Cliquez-ici"
+
+#: /var/www/poche-i18n/cache/c9/b0/845a8dc93165e6c00b6b43068799.php:226
+msgid "to export your poche datas."
+msgstr "pour exporter vos données de poche."
+
+#: /var/www/poche-i18n/cache/7a/1e/68e6b4aec1301ae024cc85232e7c.php:46
+#: /var/www/poche-i18n/cache/7a/1e/68e6b4aec1301ae024cc85232e7c.php:139
+#: /var/www/poche-i18n/cache/30/97/b548692380c89d047a16cec7af79.php:22
+msgid "back to home"
+msgstr "retour à l'accueil"
+
+#: /var/www/poche-i18n/cache/7a/1e/68e6b4aec1301ae024cc85232e7c.php:50
+#: /var/www/poche-i18n/cache/7a/1e/68e6b4aec1301ae024cc85232e7c.php:147
+#: /var/www/poche-i18n/cache/dd/a8/530129765655dcde0a70a31a78b6.php:119
+msgid "toggle mark as read"
+msgstr "marquer comme lu"
+
+#: /var/www/poche-i18n/cache/7a/1e/68e6b4aec1301ae024cc85232e7c.php:60
+#: /var/www/poche-i18n/cache/7a/1e/68e6b4aec1301ae024cc85232e7c.php:157
+#: /var/www/poche-i18n/cache/dd/a8/530129765655dcde0a70a31a78b6.php:129
+msgid "toggle favorite"
+msgstr "favori"
+
+#: /var/www/poche-i18n/cache/7a/1e/68e6b4aec1301ae024cc85232e7c.php:70
+#: /var/www/poche-i18n/cache/7a/1e/68e6b4aec1301ae024cc85232e7c.php:167
+#: /var/www/poche-i18n/cache/dd/a8/530129765655dcde0a70a31a78b6.php:139
+msgid "delete"
+msgstr "supprimer"
+
+#: /var/www/poche-i18n/cache/7a/1e/68e6b4aec1301ae024cc85232e7c.php:82
+#: /var/www/poche-i18n/cache/7a/1e/68e6b4aec1301ae024cc85232e7c.php:179
+msgid "tweet"
+msgstr "tweeter"
+
+#: /var/www/poche-i18n/cache/7a/1e/68e6b4aec1301ae024cc85232e7c.php:93
+#: /var/www/poche-i18n/cache/7a/1e/68e6b4aec1301ae024cc85232e7c.php:190
+msgid "email"
+msgstr "envoyer par email"
+
+#: /var/www/poche-i18n/cache/7a/1e/68e6b4aec1301ae024cc85232e7c.php:109
+#: /var/www/poche-i18n/cache/7a/1e/68e6b4aec1301ae024cc85232e7c.php:125
+#: /var/www/poche-i18n/cache/dd/a8/530129765655dcde0a70a31a78b6.php:153
+msgid "original"
+msgstr "original"
+
+#: /var/www/poche-i18n/cache/7a/1e/68e6b4aec1301ae024cc85232e7c.php:143
+msgid "back to top"
+msgstr "retour en haut de page"
+
+#: /var/www/poche-i18n/cache/7a/1e/68e6b4aec1301ae024cc85232e7c.php:198
+msgid "this article appears wrong?"
+msgstr "cet article s'affiche mal ?"
+
+#: /var/www/poche-i18n/cache/7a/1e/68e6b4aec1301ae024cc85232e7c.php:200
+msgid "create an issue"
+msgstr "créer un ticket"
+
+#: /var/www/poche-i18n/cache/7a/1e/68e6b4aec1301ae024cc85232e7c.php:202
+msgid "or"
+msgstr "ou"
+
+#: /var/www/poche-i18n/cache/7a/1e/68e6b4aec1301ae024cc85232e7c.php:206
+msgid "contact us by mail"
+msgstr "contactez-nous par email"
+
+#: /var/www/poche-i18n/cache/88/8a/ee3b7080c13204391c14947a0c2c.php:22
+msgid "powered by"
+msgstr "propulsé par"
+
+#: /var/www/poche-i18n/cache/d4/28/e0d08991ec2d8a7b133505e7c651.php:31
+msgid "installation"
+msgstr "installation"
+
+#: /var/www/poche-i18n/cache/d4/28/e0d08991ec2d8a7b133505e7c651.php:42
+msgid "install your poche"
+msgstr "installez votre poche"
+
+#: /var/www/poche-i18n/cache/d4/28/e0d08991ec2d8a7b133505e7c651.php:47
+msgid ""
+"poche is still not installed. Please fill the below form to install it. "
+"Don't hesitate to <a href='http://inthepoche.com/?pages/Documentation'>read "
+"the documentation on poche website</a>."
+msgstr ""
+"poche n'est pas encore installé. Merci de remplir les champs ci-dessous pour "
+"l'installer. N'hésitez pas à <a href='http://inthepoche.com/?pages/"
+"Documentation'>lire la documentation sur le site de poche</a>."
+
+#: /var/www/poche-i18n/cache/d4/28/e0d08991ec2d8a7b133505e7c651.php:53
+#: /var/www/poche-i18n/cache/ae/26/05eb67771213c16bd8c9aaf2d2c4.php:55
+msgid "Login"
+msgstr "Nom d'utilisateur"
+
+#: /var/www/poche-i18n/cache/d4/28/e0d08991ec2d8a7b133505e7c651.php:67
+msgid "Repeat your password"
+msgstr "Répétez votre mot de passe"
+
+#: /var/www/poche-i18n/cache/d4/28/e0d08991ec2d8a7b133505e7c651.php:74
+msgid "Install"
+msgstr "Installer"
+
+#: /var/www/poche-i18n/cache/ae/26/05eb67771213c16bd8c9aaf2d2c4.php:31
+#: /var/www/poche-i18n/cache/ae/26/05eb67771213c16bd8c9aaf2d2c4.php:42
+msgid "login to your poche"
+msgstr "Se connecter à votre poche"
+
+#: /var/www/poche-i18n/cache/ae/26/05eb67771213c16bd8c9aaf2d2c4.php:48
+msgid "you are in demo mode, some features may be disabled."
+msgstr ""
+"vous êtes en mode démo, certaines fonctionnalités sont peut-être désactivées."
+
+#: /var/www/poche-i18n/cache/ae/26/05eb67771213c16bd8c9aaf2d2c4.php:80
+msgid "Stay signed in"
+msgstr "rester connecté"
+
+#: /var/www/poche-i18n/cache/ae/26/05eb67771213c16bd8c9aaf2d2c4.php:86
+msgid "(Do not check on public computers)"
+msgstr "(à ne pas cocher sur un ordinateur public)"
+
+#: /var/www/poche-i18n/cache/ae/26/05eb67771213c16bd8c9aaf2d2c4.php:93
+msgid "Sign in"
+msgstr ""
+
+#: /var/www/poche-i18n/cache/dd/a8/530129765655dcde0a70a31a78b6.php:55
+#: /var/www/poche-i18n/cache/dd/a8/530129765655dcde0a70a31a78b6.php:57
+msgid "by date asc"
+msgstr "par date asc"
+
+#: /var/www/poche-i18n/cache/dd/a8/530129765655dcde0a70a31a78b6.php:59
+msgid "by date"
+msgstr "par date"
+
+#: /var/www/poche-i18n/cache/dd/a8/530129765655dcde0a70a31a78b6.php:65
+#: /var/www/poche-i18n/cache/dd/a8/530129765655dcde0a70a31a78b6.php:67
+msgid "by date desc"
+msgstr "par date desc"
+
+#: /var/www/poche-i18n/cache/dd/a8/530129765655dcde0a70a31a78b6.php:75
+#: /var/www/poche-i18n/cache/dd/a8/530129765655dcde0a70a31a78b6.php:77
+msgid "by title asc"
+msgstr "par titre asc"
+
+#: /var/www/poche-i18n/cache/dd/a8/530129765655dcde0a70a31a78b6.php:79
+msgid "by title"
+msgstr "par titre"
+
+#: /var/www/poche-i18n/cache/dd/a8/530129765655dcde0a70a31a78b6.php:85
+#: /var/www/poche-i18n/cache/dd/a8/530129765655dcde0a70a31a78b6.php:87
+msgid "by title desc"
+msgstr "par titre desc"
+
+#~ msgid "Please choose between Pocket & Readabilty :"
+#~ msgstr "Merci de choisir entre Pocket & Readability :"
+
+#~ msgid "Bye bye Pocket, let's go !"
+#~ msgstr "Bye bye Pocket, en route !"
+
+#~ msgid "Bye bye Readability, let's go !"
+#~ msgstr "Bye bye Readability, en route !"
+
+#~ msgid "Welcome to poche !"
+#~ msgstr "Bienvenue dans poche !"
+
+#~ msgid "Error with the import."
+#~ msgstr "Erreur durant l'import."
+
+#~ msgid "Wrong token."
+#~ msgstr "Mauvais jeton."
+
+#~ msgid "Login failed !"
+#~ msgstr "Connexion échouée."
+
+#~ msgid "your password has been updated"
+#~ msgstr "Votre mot de passe a été mis à jour. "
+
+#~ msgid "in demo mode, you can't update password"
+#~ msgstr "En mode démo, le mot de passe ne peut être modifié."
+
+#~ msgid ""
+#~ "your password can't be empty and you have to repeat it in the second field"
+#~ msgstr ""
+#~ "Votre mot de passe ne peut être vide et vous devez le répéter dans le "
+#~ "second champ."
+
+#~ msgid "error during url preparation : the link wasn't added"
+#~ msgstr "erreur durant l'insertion : le lien n'a pas été ajouté"
+
+#~ msgid "error during url preparation : the link is not valid"
+#~ msgstr "erreur durant la préparation de l'URL : le lien n'est pas valide"
+
+#~ msgid "TEST"
+#~ msgstr "NICOLAS"
diff --git a/phpunit.xml.dist b/phpunit.xml.dist
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/tpl/_bookmarklet.twig b/tpl/_bookmarklet.twig
new file mode 100644 (file)
index 0000000..2f3b2d1
--- /dev/null
@@ -0,0 +1,3 @@
+        <script type="text/javascript">
+          top["bookmarklet-url@inthepoche.com"]=""+"<!DOCTYPE html>"+"<html>"+"<head>"+"<title>poche it !</title>"+'<link rel="icon" href="{{poche_url}}tpl/img/favicon.ico" />'+"</head>"+"<body>"+"<script>"+"window.onload=function(){"+"window.setTimeout(function(){"+"history.back();"+"},250);"+"};"+"</scr"+"ipt>"+"</body>"+"</html>"
+        </script>
\ No newline at end of file
diff --git a/tpl/_footer.twig b/tpl/_footer.twig
new file mode 100644 (file)
index 0000000..6891756
--- /dev/null
@@ -0,0 +1,4 @@
+        <footer class="w600p center mt3 smaller txtright">
+            <p>{% trans "powered by" %} <a href="http://inthepoche.com">poche</a></p>
+            {% if constant('DEBUG_POCHE') == 1 %}<p><strong>{% trans "debug mode is on so cache is off." %} {% trans "your poche version:" %}{{constant('POCHE_VERSION')}}. {% trans "storage:" %} {{constant('STORAGE')}}</strong></p>{% endif %}
+        </footer>
\ No newline at end of file
diff --git a/tpl/_head.twig b/tpl/_head.twig
new file mode 100644 (file)
index 0000000..f25f047
--- /dev/null
@@ -0,0 +1,9 @@
+        <link rel="shortcut icon" type="image/x-icon" href="./tpl/img/favicon.ico" />
+        <link rel="apple-touch-icon-precomposed" sizes="144x144" href="./tpl/img/apple-touch-icon-144x144-precomposed.png">
+        <link rel="apple-touch-icon-precomposed" sizes="72x72" href="./tpl/img/apple-touch-icon-72x72-precomposed.png">
+        <link rel="apple-touch-icon-precomposed" href="./tpl/img/apple-touch-icon-precomposed.png">
+        <link rel="stylesheet" href="./tpl/css/knacss.css" media="all">
+        <link rel="stylesheet" href="./tpl/css/style.css" media="all">
+        <link rel="stylesheet" href="./tpl/css/style-{{ constant('THEME') }}.css" media="all" title="{{ constant('THEME') }} theme">
+        <link rel="stylesheet" href="./tpl/css/messages.css" media="all">
+        <link href='http://fonts.googleapis.com/css?family=Roboto' rel='stylesheet' type='text/css'>
\ No newline at end of file
diff --git a/tpl/_menu.twig b/tpl/_menu.twig
new file mode 100644 (file)
index 0000000..699d6a0
--- /dev/null
@@ -0,0 +1,7 @@
+            <ul id="links">
+                <li><a href="./" {% if view == 'home' %}class="current"{% endif %}>{% trans "home" %}</a></li>
+                <li><a href="./?view=fav" {% if view == 'fav' %}class="current"{% endif %}>{% trans "favorites" %}</a></li>
+                <li><a href="./?view=archive" {% if view == 'archive' %}class="current"{% endif %}>{% trans "archive" %}</a></li>
+                <li><a href="./?view=config" {% if view == 'config' %}class="current"{% endif %}>{% trans "config" %}</a></li>
+                <li><a href="./?logout" title="{% trans "logout" %}">{% trans "logout" %}</a></li>
+            </ul>
\ No newline at end of file
diff --git a/tpl/_messages.twig b/tpl/_messages.twig
new file mode 100644 (file)
index 0000000..679aa09
--- /dev/null
@@ -0,0 +1 @@
+                {{ messages | raw }}
\ No newline at end of file
diff --git a/tpl/_top.twig b/tpl/_top.twig
new file mode 100644 (file)
index 0000000..ae01cc3
--- /dev/null
@@ -0,0 +1,3 @@
+        <header class="w600p center mbm">
+            <h1><a href="./" title="{% trans "back to home" %}" ><img src="./tpl/img/logo.png" alt="logo poche" /></a></h1>
+        </header>
\ No newline at end of file
diff --git a/tpl/config.html b/tpl/config.html
deleted file mode 100644 (file)
index 1100d45..0000000
+++ /dev/null
@@ -1,27 +0,0 @@
-            <div id="content">
-               <h2>Bookmarklet</h2>
-               <p>Thanks to the bookmarklet, you will be able to easily add a link to your poche. If you don't know how use a bookmarklet, <a href="http://support.mozilla.org/en-US/kb/bookmarklets-perform-common-web-page-tasks">have a look here</a>.</p>
-               <p>Drag & drop this link to your bookmarks bar and have fun with poche.</p>
-                <p><a style="cursor: move; border: 1px dashed grey; background: white;" title="i am a bookmarklet, use me !" href="javascript:(function(){var%20url%20=%20location.href%20||%20url;window.open('{$poche_url}?action=add&url='%20+%20encodeURIComponent(url),'_self');})();">poche it !</a></p>
-
-                <h2>Password</h2>
-                   <form method="post" action="?config" name="loginform">
-                       <fieldset class="w500p">
-                           <div class="row">
-                               <label class="col w150p" for="password">New password</label>
-                               <input class="col" type="password" id="password" name="password" placeholder="Password" tabindex="2">
-                           </div>
-                           <div class="row">
-                               <label class="col w150p" for="password_repeat">Repeat your new password</label>
-                               <input class="col" type="password" id="password_repeat" name="password_repeat" placeholder="Password" tabindex="3">
-                           </div>
-                           <div class="row mts txtcenter">
-                               <button class="bouton" type="submit" tabindex="4">Update</button>
-                           </div>
-                       </fieldset>
-                       <input type="hidden" name="returnurl" value="<?php echo htmlspecialchars($referer);?>">
-                       <input type="hidden" name="token" value="<?php echo Session::getToken(); ?>">
-                   </form>
-                <h2>Export</h2>
-                <p><a href="?view=export" target="_blank">Click here</a> to export your poche datas.</p>
-            </div>
\ No newline at end of file
diff --git a/tpl/config.twig b/tpl/config.twig
new file mode 100644 (file)
index 0000000..95d5d8c
--- /dev/null
@@ -0,0 +1,57 @@
+{% extends "layout.twig" %}
+
+{% block title %}{% trans "config" %}{% endblock %}
+{% block menu %}
+            <ul id="links">
+                <li><a href="./" {% if view == 'home' %}class="current"{% endif %}>{% trans "home" %}</a></li>
+                <li><a href="./?view=fav" {% if view == 'fav' %}class="current"{% endif %}>{% trans "favorites" %}</a></li>
+                <li><a href="./?view=archive" {% if view == 'archive' %}class="current"{% endif %}>{% trans "archive" %}</a></li>
+                <li><a href="./?view=config" {% if view == 'config' %}class="current"{% endif %}>{% trans "config" %}</a></li>
+                <li><a href="./?logout" title="{% trans "logout" %}">{% trans "logout" %}</a></li>
+            </ul>
+{% endblock %}
+{% block content %}
+            <h2>{% trans "Bookmarklet" %}</h2>
+            <p>{% trans "Thanks to the bookmarklet, you will be able to easily add a link to your poche." %} {% trans "Have a look to this documentation:" %} <a href="http://inthepoche.com/?pages/Documentation">inthepoche.com</a>.</p>
+            <p>{% trans "Drag & drop this link to your bookmarks bar and have fun with poche." %}</p>
+            <p class="txtcenter"><a ondragend="this.click();" style="cursor: move; border: 1px dashed grey; background: white; padding: 5px;" title="i am a bookmarklet, use me !" href="javascript:if(top['bookmarklet-url@inthepoche.com']){top['bookmarklet-url@inthepoche.com'];}else{(function(){var%20url%20=%20location.href%20||%20url;window.open('{{ poche_url }}?action=add&url='%20+%20btoa(url),'_self');})();void(0);}">{% trans "poche it!" %}</a></p>
+
+            <h2>{% trans "Updating poche" %}</h2>
+            <p><ul>
+                <li>{% trans "your version" %} : <strong>{{ constant('POCHE_VERSION') }}</strong></li>
+                <li>{% trans "latest stable version" %} : {{ prod }}. {% if compare_prod == -1 %}<strong><a href="http://inthepoche.com/?pages/T%C3%A9l%C3%A9charger-poche">{% trans "a more recent stable version is available." %}</a></strong>{% else %}{% trans "you are up to date." %}{% endif %}</li>
+                <li>{% trans "latest dev version" %} : {{ dev }}. {% if compare_dev == -1 %}<strong><a href="http://inthepoche.com/?pages/T%C3%A9l%C3%A9charger-poche">{% trans "a more recent development version is available." %}</a></strong>{% else %}{% trans "you are up to date." %}{% endif %}</li>
+            </ul>
+            </p>
+
+            <h2>{% trans "Change your password" %}</h2>
+            <form method="post" action="?config" name="loginform">
+                <fieldset class="w500p">
+                    <div class="row">
+                        <label class="col w150p" for="password">{% trans "New password:" %}</label>
+                        <input class="col" type="password" id="password" name="password" placeholder="{% trans "Password" %}" tabindex="2">
+                    </div>
+                    <div class="row">
+                        <label class="col w150p" for="password_repeat">{% trans "Repeat your new password:" %}</label>
+                        <input class="col" type="password" id="password_repeat" name="password_repeat" placeholder="{% trans "Password" %}" tabindex="3">
+                    </div>
+                    <div class="row mts txtcenter">
+                        <button class="bouton" type="submit" tabindex="4">{% trans "Update" %}</button>
+                    </div>
+                </fieldset>
+                <input type="hidden" name="returnurl" value="{{ referer }}">
+                <input type="hidden" name="token" value="{{ token }}">
+            </form>
+
+            <h2>{% trans "Import" %}</h2>
+            <p>{% trans "Please execute the import script locally, it can take a very long time." %}</p>
+            <p>{% trans "More infos in the official doc:" %} <a href="http://inthepoche.com/?pages/Documentation">inthepoche.com</a></p>
+            <p><ul>
+            <li><a href="./?import&from=pocket">{% trans "import from Pocket" %}</a> (you must have a "ril_export.html" file on your server)</li>
+            <li><a href="./?import&from=readability">{% trans "import from Readability" %}</a>  (you must have a "readability" file on your server)</li>
+            <li><a href="./?import&from=instapaper">{% trans "import from Instapaper" %}</a>  (you must have a "instapaper-export.html" file on your server)</li>
+            </ul></p>
+
+            <h2>{% trans "Export your poche datas" %}</h2>
+            <p><a href="./?export" target="_blank">{% trans "Click here" %}</a> {% trans "to export your poche datas." %}</p>
+{% endblock %}
\ No newline at end of file
similarity index 100%
rename from css/knacss.css
rename to tpl/css/knacss.css
diff --git a/tpl/css/messages.css b/tpl/css/messages.css
new file mode 100755 (executable)
index 0000000..9222bb8
--- /dev/null
@@ -0,0 +1,13 @@
+.messages { width: 400px; -moz-border-radius: 4px; border-radius: 4px; display: block; padding: 10px 0; margin: 10px auto 10px; clear: both; }\r
+.messages a.closeMessage { margin: -14px -8px 0 0; display:none; width: 16px; height: 16px; float: right; background: url(../img/messages/close.png) no-repeat; }\r
+/*.messages:hover a.closeMessage { visibility:visible; }*/\r
+.messages p { margin: 3px 0 3px 10px !important; padding: 0 10px 0 23px !important; font-size: 14px; line-height: 16px; }\r
+.messages.error { border: 1px solid #C42608; color: #c00 !important; background: #FFF0EF; }\r
+.messages.error p { background: url(../img/messages/cross.png ) no-repeat 0px 50%; color:#c00 !important; }\r
+.messages.success {background: #E0FBCC; border: 1px solid #6DC70C; } \r
+.messages.success p { background: url(../img/messages/tick.png) no-repeat 0px 50%; color: #2B6301 !important; }\r
+.messages.warning { background: #FFFCD3; border: 1px solid #EBCD41; color: #000; }\r
+.messages.warning p { background: url(../img/messages/warning.png ) no-repeat 0px 50%; color: #5F4E01; }\r
+.messages.information, .messages.info { background: #DFEBFB; border: 1px solid #82AEE7; }\r
+.messages.information p, .messages.info p { background: url(../img/messages/help.png ) no-repeat 0px 50%; color: #064393; }\r
+.messages.information a { text-decoration: underline; }
\ No newline at end of file
diff --git a/tpl/css/style-light.css b/tpl/css/style-light.css
new file mode 100644 (file)
index 0000000..9ea7955
--- /dev/null
@@ -0,0 +1,53 @@
+
+a.back span {
+    background: url('../img/light/left.png') no-repeat;
+}
+
+a.top span {
+    background: url('../img/light/top.png') no-repeat;
+}
+
+
+a.fav span {
+    background: url('../img/light/star-on.png') no-repeat;
+}
+
+a.fav span:hover {
+    background: url('../img/light/star-off.png') no-repeat;
+}
+
+a.fav-off span {
+    background: url('../img/light/star-off.png') no-repeat;
+}
+
+a.fav-off span:hover {
+    background: url('../img/light/star-on.png') no-repeat;
+}
+
+a.archive span {
+    background: url('../img/light/checkmark-on.png') no-repeat;
+}
+
+a.archive span:hover {
+    background: url('../img/light/checkmark-off.png') no-repeat;
+}
+
+a.archive-off span {
+    background: url('../img/light/checkmark-off.png') no-repeat;
+}
+
+a.archive-off span:hover {
+    background: url('../img/light/checkmark-on.png') no-repeat;
+}
+
+a.twitter span {
+    background: url('../img/light/twitter.png') no-repeat;
+}
+
+a.email span {
+    background: url('../img/light/envelop.png') no-repeat;
+}
+
+a.delete span {
+    background: url('../img/light/remove.png') no-repeat;
+}
\ No newline at end of file
diff --git a/tpl/css/style.css b/tpl/css/style.css
new file mode 100644 (file)
index 0000000..d23c189
--- /dev/null
@@ -0,0 +1,244 @@
+body {
+    font-size: 16px;
+    font-family: 'Roboto', sans-serif;
+    margin: 10px;
+}
+
+header {
+    text-align: center;
+}
+
+header h1 {
+    font-size: 1.3em;
+}
+
+.bouton {
+       border-radius: 2px;
+}
+
+#main {
+    margin: 0 auto;
+}
+
+#main ul#links {
+    padding: 0;
+    list-style-type: none;
+    text-align: center;
+    font-size: 0.9em;
+}
+
+#main ul#links li {
+    display: inline;
+}
+
+#main ul#links li a.current {
+    -webkit-border-radius: 2px;
+    border-radius: 2px;
+}
+
+#main ul#sort {
+    padding: 0;
+    list-style-type: none;
+    text-align: center;
+    opacity: 0.5;
+}
+
+#main ul#sort li {
+    display: inline;
+    font-size: 0.9em;
+}
+
+#main ul#sort img:hover {
+    cursor: pointer;
+}
+
+
+#links a{
+    text-decoration: none;
+    padding: 5px 10px;
+}
+#links a:hover{
+    -webkit-border-radius: 2px;
+    border-radius: 2px;
+}
+
+/*** ***/
+/*** LINKS DISPLAY ***/
+
+#main a.tool {
+    text-decoration: none;
+    cursor: pointer;
+}
+
+#main #content {
+    margin-top: 20px;
+}
+
+#main #content h2 {
+    font-size: 1.3em;
+    text-decoration: none;
+}
+
+#main #content .entrie {
+    border-bottom: 1px dashed #222222;
+}
+
+#main .entrie ul.tools {
+    list-style-type: none;
+}
+
+#main .entrie ul.tools li {
+    /*display: inline;*/
+}
+
+.tools {
+    float: right;
+    text-align: right;
+    opacity: 0.5;
+}
+
+.tools p {
+    font-size: 0.8em;}
+
+/*
+.tools ul {
+    padding: 0; margin: 0;
+    list-style-type: none;
+}
+
+.tools ul li {
+    line-height: 20px;
+}
+
+.tools a.tool {
+    cursor: pointer;
+}*/
+
+#main .entrie .tools a.tool span, #article .tools a.tool span {
+    display: inline-block;
+    width: 16px;
+    height: 16px;
+}
+
+#main .entrie .url {
+    font-size: 13px;
+}
+
+
+/*** ***/
+/*** ARTICLE PAGE ***/
+
+#article {
+    margin: 0 auto;
+}
+#article header {
+    text-align: left;
+}
+
+#article header a {
+    text-decoration: none;
+}
+
+.vieworiginal a, .vieworiginal a:hover, .vieworiginal a:visited {
+    text-decoration: none;
+    color: #888888;
+}
+
+.backhome {
+    display: inline;
+}
+
+#article .tools {
+    position: relative;
+    display: inline;
+    top: 0px;
+    right: 0px;
+    width: 100%;
+}
+
+#article .tools ul li{
+    display: inline;
+}
+
+
+/*** GENERAL ***/
+body {
+    color: #000;
+}
+
+a, a:hover, a:visited {
+    color: #000;
+}
+
+.bouton {
+    background-color: #000;
+    color: #fff;
+    border: none;
+}
+.bouton:hover {
+    background-color: #222222;
+    color: #F1F1F1;
+}
+
+#main ul#links li a.current {
+    background-color: #000;
+    color: #fff;
+}
+
+#links a:hover{
+    background-color: #040707;
+    color: #F1F1F1;
+}
+
+
+/*** ***/
+/*** ARTICLE PAGE ***/
+
+#article header, #article article {
+    border-bottom: 1px solid #222222;
+}
+
+
+/* Pagination */
+.pagination {
+    clear: both;
+    padding-bottom: 20px;
+    padding-top: 10px;
+    text-align: right;
+}
+.pagination a {
+    border: 1px solid #D5D5D5;
+    color: #333;
+    font-size: 11px;
+    font-weight: bold;
+    height: 25px;
+    padding: 4px 8px;
+    text-decoration: none;
+    margin:2px;
+}
+.pagination a:hover, .pagination a:active {
+    background:#efefef;
+}
+.pagination span.current {
+    background-color: #ccc;
+    border: 1px solid #D5D5D5;
+    color: #000;
+    font-size: 11px;
+    font-weight: bold;
+    height: 25px;
+    padding: 4px 8px;
+    text-decoration: none;
+    margin:2px;
+}
+.pagination span.disabled {
+    border: 1px solid #EEEEEE;
+    color: #DDDDDD;
+    margin:2px;
+    padding: 4px 8px;
+    font-size: 11px;
+    font-weight: bold;
+}
+
+footer {
+    clear: both;
+}
\ No newline at end of file
diff --git a/tpl/entries.html b/tpl/entries.html
deleted file mode 100644 (file)
index 8526a3c..0000000
+++ /dev/null
@@ -1,18 +0,0 @@
-            <div id="content">
-                {loop="entries"}
-                    <div id="entry-{$value.id}" class="entrie mb2">
-                        <span class="content">
-                            <h2 class="h6-like">
-                                <a href="index.php?&view=view&id={$value.id}">{$value.title}</a>
-                            </h2>
-                            <div class="tools">
-                                <ul>
-                                    <li><a title="toggle mark as read" class="tool archive {if="$value.is_read == '0'"}archive-off{/if}" onclick="toggle_archive(this, {$value.id})"><span></span></a></li>
-                                    <li><a title="toggle favorite" class="tool fav {if="$value.is_fav == '0'"}fav-off{/if}" onclick="toggle_favorite(this, {$value.id})"><span></span></a></li>
-                                    <li><form method="post" onsubmit="return confirm('Are you sure?')" style="display: inline;"><input type="hidden" name="token" id="token" value="<?php echo Session::getToken(); ?>" /><input type="hidden" id="action" name="action" value="delete" /><input type="hidden" id="view" name="view" value="{$view}" /><input type="hidden" id="id" name="id" value="{$value.id}" /><input type="submit" class="delete" title="toggle delete" /></form></li>
-                                </ul>
-                            </div>
-                        </span>
-                    </div>
-                {/loop}
-            </div>
\ No newline at end of file
diff --git a/tpl/export.html b/tpl/export.html
deleted file mode 100644 (file)
index d22d05f..0000000
+++ /dev/null
@@ -1 +0,0 @@
-export {$export}
\ No newline at end of file
diff --git a/tpl/export.twig b/tpl/export.twig
new file mode 100644 (file)
index 0000000..4adb954
--- /dev/null
@@ -0,0 +1 @@
+{{ export }}
\ No newline at end of file
diff --git a/tpl/footer.html b/tpl/footer.html
deleted file mode 100644 (file)
index b8bd755..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-               </div>
-        <footer class="mr2 mt3 smaller">
-            <p>powered by <a href="http://inthepoche.com">poche</a><br />follow us on <a href="https://twitter.com/getpoche" title="follow us on twitter">twitter</a></p>
-        </footer>
-
-    </body>
-</html>
\ No newline at end of file
diff --git a/tpl/head.html b/tpl/head.html
deleted file mode 100644 (file)
index e95f610..0000000
+++ /dev/null
@@ -1,22 +0,0 @@
-<!DOCTYPE html>
-<!--[if lte IE 6]> <html class="no-js ie6 ie67 ie678" lang="en"> <![endif]-->
-<!--[if lte IE 7]> <html class="no-js ie7 ie67 ie678" lang="en"> <![endif]-->
-<!--[if IE 8]> <html class="no-js ie8 ie678" lang="en"> <![endif]-->
-<!--[if gt IE 8]><!--> <html class="no-js" lang="en"> <!--<![endif]-->
-<html>
-    <head>
-        <meta name="viewport" content="width=device-width, minimum-scale=1.0, maximum-scale=1.0">
-        <meta charset="utf-8">
-        <meta http-equiv="X-UA-Compatible" content="IE=10">
-        <title>{$title}</title>
-        <link rel="shortcut icon" type="image/x-icon" href="./img/favicon.ico" />
-        <link rel="apple-touch-icon-precomposed" sizes="144x144" href="./img/apple-touch-icon-144x144-precomposed.png">
-        <link rel="apple-touch-icon-precomposed" sizes="72x72" href="./img/apple-touch-icon-72x72-precomposed.png">
-        <link rel="apple-touch-icon-precomposed" href="./img/apple-touch-icon-precomposed.png">
-        <link rel="stylesheet" href="./css/knacss.css" media="all">
-        <link rel="stylesheet" href="./css/style.css" media="all">
-        <!-- Light Theme -->
-        <link rel="stylesheet" href="./css/style-light.css" media="all" title="light-style">
-        <!-- Dark Theme -->
-        <link rel="alternate stylesheet" href="./css/style-dark.css" media="all" title="dark-style">
-    </head>
\ No newline at end of file
diff --git a/tpl/home.html b/tpl/home.html
deleted file mode 100644 (file)
index 90e247f..0000000
+++ /dev/null
@@ -1,19 +0,0 @@
-    <body class="light-style">
-        <header>
-            <h1><a href="index.php"><img src="./img/logo.png" alt="logo poche" /></a>poche</h1>
-        </header>
-        <div id="main">
-            <ul id="links">
-                <li><a href="index.php" {if="$view == 'index'"}class="current"{/if}>home</a></li>
-                <li><a href="?view=fav" {if="$view == 'fav'"}class="current"{/if}>favorites</a></li>
-                <li><a href="?view=archive" {if="$view == 'archive'"}class="current"{/if}>archive</a></li>
-                <li><a href="?view=config" {if="$view == 'config'"}class="current"{/if}>config</a></li>
-                <li><a href="?logout" title="Logout">logout</a></li>
-            </ul>
-            {if condition="isset($entries)"}
-            <ul id="sort">
-                <li><img src="img/up.png" onclick="sort_links('{$view}', 'ia');" title="by date asc" /> by date <img src="img/down.png" onclick="sort_links('{$view}', 'id');" title="by date desc" /></li>
-                <li><img src="img/up.png" onclick="sort_links('{$view}', 'ta');" title="by title asc" /> by title <img src="img/down.png" onclick="sort_links('{$view}', 'td');" title="by title desc" /></li>
-            </ul>
-            {/if}
-            {include="messages"}
\ No newline at end of file
diff --git a/tpl/home.twig b/tpl/home.twig
new file mode 100644 (file)
index 0000000..a6da641
--- /dev/null
@@ -0,0 +1,29 @@
+{% extends "layout.twig" %}
+{% block title %}{% trans "home" %}{% endblock %}
+{% block menu %}
+{% include '_menu.twig' %}
+{% endblock %}
+{% block precontent %}
+            <ul id="sort">
+                <li><a href="./?sort=ia&view={{ view }}"><img src="./tpl/img/{{ constant('THEME') }}/top.png" alt="{% trans "by date asc" %}" title="{% trans "by date asc" %}" /></a> {% trans "by date" %} <a href="./?sort=id&view={{ view }}"><img src="./tpl/img/{{ constant('THEME') }}/down.png" alt="{% trans "by date desc" %}" title="{% trans "by date desc" %}" /></a></li>
+                <li><a href="./?sort=ta&view={{ view }}"><img src="./tpl/img/{{ constant('THEME') }}/top.png" alt="{% trans "by title asc" %}" title="{% trans "by title asc" %}" /></a> {% trans "by title" %} <a href="./?sort=td&view={{ view }}"><img src="./tpl/img/{{ constant('THEME') }}/down.png" alt="{% trans "by title desc" %}" title="{% trans "by title desc" %}" /></a></li>
+            </ul>
+{% endblock %}
+{% block content %}
+            {{ page_links | raw }}
+            {% for entry in entries %}
+            <div id="entry-{{ entry.id|e }}" class="entrie">
+                <h2><a href="index.php?view=view&id={{ entry.id|e }}">{{ entry.title|e }}</a></h2>
+                <ul class="tools">
+                    <li>
+                        <a title="{% trans "toggle mark as read" %}" class="tool archive {% if entry.is_read == 0 %}archive-off{% endif %}" href="./?action=toggle_archive&id={{ entry.id|e }}"><span></span></a></li>
+                        <li><a title="{% trans "toggle favorite" %}" class="tool fav {% if entry.is_fav == 0 %}fav-off{% endif %}" href="./?action=toggle_fav&id={{ entry.id|e }}"><span></span></a></li>
+                        <li><a title="{% trans "delete" %}" class="tool delete" href="./?action=delete&id={{ entry.id|e }}"><span></span></a></li>
+                    </li>
+                </ul>
+                <p>{{ entry.content|striptags|slice(0, 300) }}...</p>
+                <p class="vieworiginal txtright small"><a href="{{ entry.url|e }}" target="_blank" title="{% trans "original" %} : {{ entry.title|e }}">{{ entry.url | e | getDomain }}</a></p>
+            </div>
+            {% endfor %}
+            {{ page_links | raw }}
+{% endblock %}
\ No newline at end of file
similarity index 100%
rename from img/favicon.ico
rename to tpl/img/favicon.ico
similarity index 100%
rename from img/down.png
rename to tpl/img/light/down.png
diff --git a/tpl/img/light/envelop.png b/tpl/img/light/envelop.png
new file mode 100755 (executable)
index 0000000..6be1c88
Binary files /dev/null and b/tpl/img/light/envelop.png differ
diff --git a/tpl/img/light/left.png b/tpl/img/light/left.png
new file mode 100755 (executable)
index 0000000..a0a5363
Binary files /dev/null and b/tpl/img/light/left.png differ
old mode 100644 (file)
new mode 100755 (executable)
similarity index 100%
rename from img/up.png
rename to tpl/img/light/top.png
diff --git a/tpl/img/light/twitter.png b/tpl/img/light/twitter.png
new file mode 100755 (executable)
index 0000000..cfcfe41
Binary files /dev/null and b/tpl/img/light/twitter.png differ
diff --git a/tpl/img/logo.png b/tpl/img/logo.png
new file mode 100644 (file)
index 0000000..549b846
Binary files /dev/null and b/tpl/img/logo.png differ
old mode 100644 (file)
new mode 100755 (executable)
similarity index 100%
rename from img/messages/close.png
rename to tpl/img/messages/close.png
old mode 100644 (file)
new mode 100755 (executable)
similarity index 100%
rename from img/messages/cross.png
rename to tpl/img/messages/cross.png
old mode 100644 (file)
new mode 100755 (executable)
similarity index 100%
rename from img/messages/help.png
rename to tpl/img/messages/help.png
old mode 100644 (file)
new mode 100755 (executable)
similarity index 100%
rename from img/messages/tick.png
rename to tpl/img/messages/tick.png
old mode 100644 (file)
new mode 100755 (executable)
similarity index 100%
rename from img/messages/warning.png
rename to tpl/img/messages/warning.png
diff --git a/tpl/install.html b/tpl/install.html
deleted file mode 100644 (file)
index d11a781..0000000
+++ /dev/null
@@ -1,30 +0,0 @@
-{include="head"}
-    <body class="light-style">
-        <header>
-            <h1><a href="index.php"><img src="./img/logo.png" alt="logo poche" /></a>poche</h1>
-        </header>
-        <div id="main">
-            <form method="post" action="?install" name="loginform">
-                <fieldset class="w500p center">
-                    <h2 class="mbs txtcenter">install your poche</h2>
-                    <div class="row">
-                        <label class="col w150p" for="login">Login</label>
-                        <input class="col" type="text" id="login" name="login" placeholder="Login" tabindex="1" autofocus />
-                    </div>
-                    <div class="row">
-                        <label class="col w150p" for="password">Password</label>
-                        <input class="col" type="password" id="password" name="password" placeholder="Password" tabindex="2">
-                    </div>
-                    <div class="row">
-                        <label class="col w150p" for="password_repeat">Repeat your password</label>
-                        <input class="col" type="password" id="password_repeat" name="password_repeat" placeholder="Password" tabindex="3">
-                    </div>
-                    <div class="row mts txtcenter">
-                        <button class="bouton" type="submit" tabindex="4">Install</button>
-                    </div>
-                </fieldset>
-                <input type="hidden" name="returnurl" value="<?php echo htmlspecialchars($referer);?>">
-                <input type="hidden" name="token" value="<?php echo Session::getToken(); ?>">
-            </form>
-
-{include="footer"}
diff --git a/tpl/install.twig b/tpl/install.twig
new file mode 100644 (file)
index 0000000..8bcede0
--- /dev/null
@@ -0,0 +1,28 @@
+{% extends "layout.twig" %}
+{% block title %}{% trans "installation" %}{% endblock %}
+{% block content %}
+        <form method="post" action="?install" name="loginform">
+            <fieldset class="w500p center">
+                <h2 class="mbs txtcenter">{% trans "install your poche" %}</h2>
+                <p>
+                    {% trans "poche is still not installed. Please fill the below form to install it. Don't hesitate to <a href='http://inthepoche.com/?pages/Documentation'>read the documentation on poche website</a>." %}
+                </p>
+                <div class="row">
+                    <label class="col w150p" for="login">{% trans "Login" %}</label>
+                    <input class="col" type="text" id="login" name="login" placeholder="Login" tabindex="1" autofocus />
+                </div>
+                <div class="row">
+                    <label class="col w150p" for="password">{% trans "Password" %}</label>
+                    <input class="col" type="password" id="password" name="password" placeholder="Password" tabindex="2">
+                </div>
+                <div class="row">
+                    <label class="col w150p" for="password_repeat">{% trans "Repeat your password" %}</label>
+                    <input class="col" type="password" id="password_repeat" name="password_repeat" placeholder="Password" tabindex="3">
+                </div>
+                <div class="row mts txtcenter">
+                    <button class="bouton" type="submit" tabindex="4">{% trans "Install" %}</button>
+                </div>
+            </fieldset>
+            <input type="hidden" name="token" value="{{ token }}">
+        </form>
+{% endblock %}
\ No newline at end of file
diff --git a/tpl/js.html b/tpl/js.html
deleted file mode 100644 (file)
index a02212b..0000000
+++ /dev/null
@@ -1,22 +0,0 @@
-        <script type="text/javascript" src="js/jquery-1.9.1.min.js"></script>
-        <script type="text/javascript" src="js/poche.js"></script>
-
-        {if="$load_all_js == '1'"}
-        <script type="text/javascript" src="js/jquery.masonry.min.js"></script>
-        <script type="text/javascript">
-            $( window ).load( function()
-            {
-                var columns    = 3,
-                    setColumns = function() { columns = $( window ).width() > 640 ? 3 : $( window ).width() > 320 ? 2 : 1; };
-
-                setColumns();
-                $( window ).resize( setColumns );
-
-                $( '#content' ).masonry(
-                {
-                    itemSelector: '.entrie',
-                    columnWidth:  function( containerWidth ) { return containerWidth / columns; }
-                });
-            });
-        </script>
-        {/if}
\ No newline at end of file
diff --git a/tpl/layout.twig b/tpl/layout.twig
new file mode 100644 (file)
index 0000000..c4bbe77
--- /dev/null
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<!--[if lte IE 6]> <html class="no-js ie6 ie67 ie678" lang="en"> <![endif]-->
+<!--[if lte IE 7]> <html class="no-js ie7 ie67 ie678" lang="en"> <![endif]-->
+<!--[if IE 8]> <html class="no-js ie8 ie678" lang="en"> <![endif]-->
+<!--[if gt IE 8]><!--> <html class="no-js" lang="en"> <!--<![endif]-->
+<html>
+    <head>
+        <meta name="viewport" content="width=device-width, minimum-scale=1.0, maximum-scale=1.0">
+        <meta charset="utf-8">
+        <meta http-equiv="X-UA-Compatible" content="IE=10">
+        <title>{% block title %}{% endblock %} - poche</title>
+        {% include '_head.twig' %}
+        {% include '_bookmarklet.twig' %}
+    </head>
+    <body>
+        {% include '_top.twig' %}
+        <div id="main">
+            {% block menu %}{% endblock %}
+            {% block precontent %}{% endblock %}
+            {% block messages %}
+            {% include '_messages.twig' %}
+            {% endblock %}
+            <div id="content" class="w600p center">
+            {% block content %}{% endblock %}
+            </div>
+        </div>
+        {% include '_footer.twig' %}
+    </body>
+</html>
\ No newline at end of file
diff --git a/tpl/login.html b/tpl/login.html
deleted file mode 100644 (file)
index 69c17a5..0000000
+++ /dev/null
@@ -1,33 +0,0 @@
-{include="head"}
-    <body class="light-style">
-        <header>
-            <h1><a href="index.php"><img src="./img/logo.png" alt="logo poche" /></a>poche</h1>
-        </header>
-        <div id="main">
-            <form method="post" action="?login" name="loginform">
-                <fieldset class="w500p center">
-                    <h2 class="mbs txtcenter">login to your poche</h2>
-                                       <div class="row">
-                                               <label class="col w150p" for="login">Login</label>
-                                               <input class="col" type="text" id="login" name="login" placeholder="Login" tabindex="1" autofocus />
-                                       </div>
-                                       <div class="row">
-                                               <label class="col w150p" for="password">Password</label>
-                                               <input class="col" type="password" id="password" name="password" placeholder="Password" tabindex="2">
-                                       </div>
-                                       <div class="row">
-                                               <label class="col w150p">Stay signed in</label>
-                                               <div class="col">
-                                                       <input type="checkbox" name="longlastingsession" tabindex="3">
-                                                       <small class="inbl">(Do not check on public computers)</small>
-                                               </div>
-                                       </div>
-                                       <div class="row mts txtcenter">
-                                               <button class="bouton" type="submit" tabindex="4">Sign in</button>
-                                       </div>
-                </fieldset>
-                <input type="hidden" name="returnurl" value="<?php echo htmlspecialchars($referer);?>">
-                <input type="hidden" name="token" value="<?php echo Session::getToken(); ?>">
-            </form>
-
-{include="footer"}
diff --git a/tpl/login.twig b/tpl/login.twig
new file mode 100644 (file)
index 0000000..0ae130b
--- /dev/null
@@ -0,0 +1,32 @@
+{% extends "layout.twig" %}
+
+{% block title %}{% trans "login to your poche" %}{% endblock %}
+{% block content %}
+            <form method="post" action="?login" name="loginform">
+                <fieldset class="w500p center">
+                    <h2 class="mbs txtcenter">{% trans "login to your poche" %}</h2>
+                    {% if constant('MODE_DEMO') == 1 %}<p>{% trans "you are in demo mode, some features may be disabled." %}</p>{% endif %}
+                                       <div class="row">
+                                               <label class="col w150p" for="login">{% trans "Login" %}</label>
+                                               <input class="col" type="text" id="login" name="login" placeholder="Login" tabindex="1" autofocus {% if constant('MODE_DEMO') == 1 %}value="poche"{% endif %} />
+                                       </div>
+
+                                       <div class="row">
+                                               <label class="col w150p" for="password">{% trans "Password" %}</label>
+                                               <input class="col" type="password" id="password" name="password" placeholder="Password" tabindex="2" {% if constant('MODE_DEMO') == 1 %}value="poche"{% endif %} />
+                                       </div>
+                                       <div class="row">
+                                               <label class="col w150p" for="longlastingsession">{% trans "Stay signed in" %}</label>
+                                               <div class="col">
+                                                       <input type="checkbox" id="longlastingsession" name="longlastingsession" tabindex="3">
+                                                       <small class="inbl">{% trans "(Do not check on public computers)" %}</small>
+                                               </div>
+                                       </div>
+                                       <div class="row mts txtcenter">
+                                               <button class="bouton" type="submit" tabindex="4">{% trans "Login" %}</button>
+                                       </div>
+                </fieldset>
+                <input type="hidden" name="returnurl" value="{{ referer }}">
+                <input type="hidden" name="token" value="{{ token }}">
+            </form>
+{% endblock %}
\ No newline at end of file
diff --git a/tpl/messages.html b/tpl/messages.html
deleted file mode 100644 (file)
index 87af259..0000000
+++ /dev/null
@@ -1 +0,0 @@
-<div id="messages"><?php echo $msg->display(); ?></div>
\ No newline at end of file
diff --git a/tpl/view.html b/tpl/view.html
deleted file mode 100644 (file)
index af94df2..0000000
+++ /dev/null
@@ -1,53 +0,0 @@
-<!DOCTYPE html>
-<!--[if lte IE 6]> <html class="no-js ie6 ie67 ie678" lang="en"> <![endif]-->
-<!--[if lte IE 7]> <html class="no-js ie7 ie67 ie678" lang="en"> <![endif]-->
-<!--[if IE 8]> <html class="no-js ie8 ie678" lang="en"> <![endif]-->
-<!--[if gt IE 8]><!--> <html class="no-js" lang="en"> <!--<![endif]-->
-<html>
-    <head>
-        <meta name="viewport" content="width=device-width, minimum-scale=1.0, maximum-scale=1.0">
-        <meta charset="utf-8">
-        <meta http-equiv="X-UA-Compatible" content="IE=10">
-        <title>{$title}</title>
-        <link rel="shortcut icon" type="image/x-icon" href="./img/favicon.ico" />
-        <link rel="apple-touch-icon-precomposed" sizes="144x144" href="./img/apple-touch-icon-144x144-precomposed.png">
-        <link rel="apple-touch-icon-precomposed" sizes="72x72" href="./img/apple-touch-icon-72x72-precomposed.png">
-        <link rel="apple-touch-icon-precomposed" href="./img/apple-touch-icon-precomposed.png">
-        <link rel="stylesheet" href="./css/knacss.css" media="all">
-        <link rel="stylesheet" href="./css/style.css" media="all">
-        <!-- Light Theme -->
-        <link rel="stylesheet" href="./css/style-light.css" media="all" title="light-style">
-        <!-- Dark Theme -->
-        <link rel="alternate stylesheet" href="./css/style-dark.css" media="all" title="dark-style">
-    </head>
-    <body class="article light-style">
-        <div id="article" class="w600p">
-               <div class="backhome">
-                       <a href="index.php" title="back to home">&larr;</a>
-               </div>
-            <div class="tools">
-                <ul>
-                    <li><a title="toggle mark as read" class="tool archive {if="$is_read == '0'"}archive-off{/if}" onclick="toggle_archive(this, {$id})"><span></span></a></li>
-                    <li><a href="#" id="themeswitch">dark</a></li>
-                    <li><a title="toggle favorite" class="tool fav {if="$is_fav == '0'"}fav-off{/if}" onclick="toggle_favorite(this, {$id})"><span></span></a></li>
-                    <li><form method="post" onsubmit="return confirm('Are you sure?')" style="display: inline;" action="index.php"><input type="hidden" name="token" id="token" value="<?php echo Session::getToken(); ?>" /><input type="hidden" id="view" name="view" value="index" /><input type="hidden" id="action" name="action" value="delete" /><input type="hidden" id="id" name="id" value="{$id}" /><input type="submit" class="delete" title="toggle delete" /></form></li>
-                    <li><a href="?logout" title="Logout">logout</a></li>
-                </ul>
-            </div>
-            <header class="mbm">
-                <h1><a href="{$url}">{$title}</a></h1>
-                <div class="vieworiginal txtright small"><a href="{$url}" target="_blank" title="original : {$title}">view original</a></div>
-            </header>
-            {include="messages"}
-            <article>
-                <div id="readityourselfcontent">
-                    {$content}
-                </div>
-            </article>
-            <div class="vieworiginal txtright small"><a href="{$url}" target="_blank" title="original : {$title}">view original</a></div>
-            <div class="backhome">
-                <a href="index.php" title="back to home">&larr;</a>
-            </div>
-
-        {include="js"}
-        {include="footer"}
\ No newline at end of file
diff --git a/tpl/view.twig b/tpl/view.twig
new file mode 100644 (file)
index 0000000..e999afa
--- /dev/null
@@ -0,0 +1,40 @@
+{% extends "layout.twig" %}
+{% block title %}{% trans "home" %}{% endblock %}
+{% block content %}
+        <div id="article">
+            <div class="tools">
+                <ul class="tools">
+                    <li>
+                        <li><a href="{{ referer }}" title="{% trans "back to home" %}" class="tool back"><span></span></a></li>
+                        <a title="{% trans "toggle mark as read" %}" class="tool archive {% if entry.is_read == 0 %}archive-off{% endif %}" href="./?action=toggle_archive&id={{ entry.id|e }}"><span></span></a></li>
+                        <li><a title="{% trans "toggle favorite" %}" class="tool fav {% if entry.is_fav == 0 %}fav-off{% endif %}" href="./?action=toggle_fav&id={{ entry.id|e }}"><span></span></a></li>
+                        <li><a title="{% trans "delete" %}" class="tool delete" href="./?action=delete&id={{ entry.id|e }}"><span></span></a></li>
+                        {% if constant('SHARE_TWITTER') == 1 %}<li><a href="https://twitter.com/home?status={{entry.title}}%20{{ entry.url|e }}%20via%20@getpoche" target="_blank" class="tool twitter" title="{% trans "tweet" %}"><span></span></a></li>{% endif %}
+                        {% if constant('SHARE_MAIL') == 1 %}<li><a href="mailto:?subject={{ entry.title|e }}&body={{ entry.url|e }} via @getpoche" class="tool email" title="{% trans "email" %}"><span></span></a></li>{% endif %}
+                    </li>
+                </ul>
+            </div>
+            <header class="mbm">
+                <h1>{{ entry.title|e }}</h1>
+                <div class="vieworiginal txtright small"><a href="{{ entry.url|e }}" target="_blank" title="{% trans "original" %} : {{ entry.title|e }}">{{ entry.url | e | getDomain }}</a></div>
+            </header>
+            <article>
+                {{ content | raw }}
+                <div class="vieworiginal txtright small"><a href="{{ entry.url|e }}" target="_blank" title="{% trans "original" %} : {{ entry.title|e }}">{{ entry.url | e | getDomain }}</a></div>
+            </article>
+            <div class="tools">
+                <ul class="tools">
+                    <li>
+                        <li><a href="{{ referer }}" title="{% trans "back to home" %}" class="tool back"><span></span></a></li>
+                        <li><a href="#" title="{% trans "back to top" %}" class="tool top"><span></span></a></li>
+                        <a title="{% trans "toggle mark as read" %}" class="tool archive {% if entry.is_read == 0 %}archive-off{% endif %}" href="./?action=toggle_archive&id={{ entry.id|e }}"><span></span></a></li>
+                        <li><a title="{% trans "toggle favorite" %}" class="tool fav {% if entry.is_fav == 0 %}fav-off{% endif %}" href="./?action=toggle_fav&id={{ entry.id|e }}"><span></span></a></li>
+                        <li><a title="{% trans "delete" %}" class="tool delete" href="./?action=delete&id={{ entry.id|e }}"><span></span></a></li>
+                        {% if constant('SHARE_TWITTER') == 1 %}<li><a href="https://twitter.com/home?status={{entry.title}}%20{{ entry.url|e }}%20via%20@getpoche" target="_blank" class="tool twitter" title="{% trans "tweet" %}"><span></span></a></li>{% endif %}
+                        {% if constant('SHARE_MAIL') == 1 %}<li><a href="mailto:?subject={{ entry.title|e }}&body={{ entry.url|e }} via @getpoche" class="tool email" title="{% trans "email" %}"><span></span></a></li>{% endif %}
+                    </li>
+                </ul>
+                <p>{% trans "this article appears wrong?" %} <a href="https://github.com/inthepoche/poche/issues/new">{% trans "create an issue" %}</a> {% trans "or" %} <a href="mailto:support@inthepoche.com?subject=Wrong display in poche&body={{ entry.url|e }}">{% trans "contact us by mail" %}</a></p>
+            </div>
+        </div>
+{% endblock %}
\ No newline at end of file