aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorNicolas Lœuillet <nicolas.loeuillet@gmail.com>2013-08-08 09:36:10 -0700
committerNicolas Lœuillet <nicolas.loeuillet@gmail.com>2013-08-08 09:36:10 -0700
commit9a8b4ff4edf84d7df60de1b6fd1e493b59f88273 (patch)
tree3c8ab8086fd8a2750270f8aeaee1f1ce016167cb
parent85ebc80c7eaf88e4d57a52adb8e4c32d8cc34b64 (diff)
parent572e758bf2e76308a3fa3eda9a8d9e9be8b53ecc (diff)
downloadwallabag-9a8b4ff4edf84d7df60de1b6fd1e493b59f88273.tar.gz
wallabag-9a8b4ff4edf84d7df60de1b6fd1e493b59f88273.tar.zst
wallabag-9a8b4ff4edf84d7df60de1b6fd1e493b59f88273.zip
Merge pull request #109 from inthepoche/dev
merge dev into master
-rw-r--r--.gitignore5
-rw-r--r--.travis.yml15
-rw-r--r--CREDITS12
-rw-r--r--INSTALL.md53
-rw-r--r--README.md30
-rw-r--r--TODO.md11
-rw-r--r--composer.json7
-rw-r--r--composer.lock744
-rw-r--r--css/style-dark.css90
-rw-r--r--css/style-light.css100
-rw-r--r--css/style.css215
-rw-r--r--img/dark/checkmark-off.pngbin267 -> 0 bytes
-rw-r--r--img/dark/checkmark-on.pngbin221 -> 0 bytes
-rw-r--r--img/dark/down.pngbin223 -> 0 bytes
-rw-r--r--img/dark/logo.pngbin786 -> 0 bytes
-rw-r--r--img/dark/remove.pngbin265 -> 0 bytes
-rw-r--r--img/dark/star-off.pngbin330 -> 0 bytes
-rw-r--r--img/dark/star-on.pngbin277 -> 0 bytes
-rw-r--r--img/dark/up.pngbin225 -> 0 bytes
-rw-r--r--img/logo.pngbin911 -> 0 bytes
-rw-r--r--import.php50
-rw-r--r--inc/3rdparty/Encoding.php (renamed from inc/Encoding.php)0
-rw-r--r--inc/3rdparty/JSLikeHTMLElement.php (renamed from inc/JSLikeHTMLElement.php)12
-rw-r--r--inc/3rdparty/Readability.php (renamed from inc/Readability.php)198
-rw-r--r--inc/3rdparty/Session.class.php (renamed from inc/Session.class.php)2
-rwxr-xr-x[-rw-r--r--]inc/3rdparty/class.messages.php (renamed from inc/class.messages.php)460
-rw-r--r--inc/3rdparty/paginator.php202
-rw-r--r--inc/3rdparty/simple_html_dom.php (renamed from inc/simple_html_dom.php)0
-rw-r--r--inc/MyTool.class.php265
-rw-r--r--inc/config.php66
-rw-r--r--inc/functions.php398
-rw-r--r--inc/poche/Database.class.php216
-rw-r--r--inc/poche/Poche.class.php485
-rw-r--r--inc/poche/Tools.class.php226
-rw-r--r--inc/poche/Url.class.php94
-rw-r--r--inc/poche/User.class.php50
-rw-r--r--inc/poche/config.inc.php61
-rw-r--r--inc/poche/pochePictures.php110
-rw-r--r--inc/rain.tpl.class.php1043
-rw-r--r--inc/store/file.class.php51
-rw-r--r--inc/store/sqlite.class.php202
-rw-r--r--inc/store/store.class.php63
-rw-r--r--index.php108
-rw-r--r--install/mysql.sql34
-rwxr-xr-xinstall/poche.sqlite (renamed from db/poche.sqlite)bin294912 -> 360448 bytes
-rw-r--r--install/postgres.sql30
-rw-r--r--install/update_sqlite_from_0_to_1.php72
-rw-r--r--js/jquery-1.9.1.min.js5
-rw-r--r--js/jquery.masonry.min.js10
-rw-r--r--js/poche.js57
-rw-r--r--locale/fr_FR.UTF8/LC_MESSAGES/fr_FR.UTF8.mobin0 -> 5699 bytes
-rw-r--r--locale/fr_FR.UTF8/LC_MESSAGES/fr_FR.UTF8.po376
-rw-r--r--phpunit.xml.dist0
-rw-r--r--tpl/_bookmarklet.twig3
-rw-r--r--tpl/_footer.twig4
-rw-r--r--tpl/_head.twig9
-rw-r--r--tpl/_menu.twig7
-rw-r--r--tpl/_messages.twig1
-rw-r--r--tpl/_top.twig3
-rw-r--r--tpl/config.html27
-rw-r--r--tpl/config.twig57
-rw-r--r--tpl/css/knacss.css (renamed from css/knacss.css)0
-rwxr-xr-xtpl/css/messages.css13
-rw-r--r--tpl/css/style-light.css53
-rw-r--r--tpl/css/style.css244
-rw-r--r--tpl/entries.html18
-rw-r--r--tpl/export.html1
-rw-r--r--tpl/export.twig1
-rw-r--r--tpl/footer.html7
-rw-r--r--tpl/head.html22
-rw-r--r--tpl/home.html19
-rw-r--r--tpl/home.twig29
-rw-r--r--tpl/img/apple-touch-icon-144x144-precomposed.png (renamed from img/apple-touch-icon-144x144-precomposed.png)bin7349 -> 7349 bytes
-rw-r--r--tpl/img/apple-touch-icon-72x72-precomposed.png (renamed from img/apple-touch-icon-72x72-precomposed.png)bin6168 -> 6168 bytes
-rw-r--r--tpl/img/apple-touch-icon.png (renamed from img/apple-touch-icon.png)bin5803 -> 5803 bytes
-rw-r--r--tpl/img/favicon.ico (renamed from img/favicon.ico)bin346 -> 346 bytes
-rw-r--r--tpl/img/light/checkmark-off.png (renamed from img/light/checkmark-off.png)bin277 -> 277 bytes
-rw-r--r--tpl/img/light/checkmark-on.png (renamed from img/light/checkmark-on.png)bin235 -> 235 bytes
-rw-r--r--tpl/img/light/down.png (renamed from img/down.png)bin216 -> 216 bytes
-rwxr-xr-xtpl/img/light/envelop.pngbin0 -> 285 bytes
-rwxr-xr-xtpl/img/light/left.pngbin0 -> 196 bytes
-rw-r--r--tpl/img/light/remove.png (renamed from img/light/remove.png)bin252 -> 252 bytes
-rw-r--r--tpl/img/light/star-off.png (renamed from img/light/star-off.png)bin314 -> 314 bytes
-rw-r--r--tpl/img/light/star-on.png (renamed from img/light/star-on.png)bin281 -> 281 bytes
-rwxr-xr-x[-rw-r--r--]tpl/img/light/top.png (renamed from img/up.png)bin212 -> 212 bytes
-rwxr-xr-xtpl/img/light/twitter.pngbin0 -> 297 bytes
-rw-r--r--tpl/img/logo.pngbin0 -> 454 bytes
-rwxr-xr-x[-rw-r--r--]tpl/img/messages/close.png (renamed from img/messages/close.png)bin662 -> 662 bytes
-rwxr-xr-x[-rw-r--r--]tpl/img/messages/cross.png (renamed from img/messages/cross.png)bin655 -> 655 bytes
-rwxr-xr-x[-rw-r--r--]tpl/img/messages/help.png (renamed from img/messages/help.png)bin786 -> 786 bytes
-rwxr-xr-x[-rw-r--r--]tpl/img/messages/tick.png (renamed from img/messages/tick.png)bin537 -> 537 bytes
-rwxr-xr-x[-rw-r--r--]tpl/img/messages/warning.png (renamed from img/messages/warning.png)bin666 -> 666 bytes
-rw-r--r--tpl/install.html30
-rw-r--r--tpl/install.twig28
-rw-r--r--tpl/js.html22
-rw-r--r--tpl/layout.twig29
-rw-r--r--tpl/login.html33
-rw-r--r--tpl/login.twig32
-rw-r--r--tpl/messages.html1
-rw-r--r--tpl/view.html53
-rw-r--r--tpl/view.twig40
101 files changed, 3733 insertions, 3281 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 00000000..5e992c2e
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,5 @@
1vendor
2composer.phar
3db/poche.sqlite
4output
5phpdoc* \ No newline at end of file
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 00000000..9d6ba132
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,15 @@
1language: php
2
3php:
4 - 5.4
5
6branches:
7 only:
8 - dev
9
10before_script:
11 - composer install
12
13notifications:
14 email:
15 - nicolas.loeuillet@gmail.com \ No newline at end of file
diff --git a/CREDITS b/CREDITS
index c917a012..a6dedce4 100644
--- a/CREDITS
+++ b/CREDITS
@@ -1,16 +1,14 @@
1poche is based on : 1poche is based on :
2* ReadItYourself http://www.memiks.fr/readityourself/ 2* PHP Readability https://bitbucket.org/fivefilters/php-readability
3* PHP Readability http://www.keyvan.net/2010/08/php-readability/
4* Encoding https://github.com/neitanod/forceutf8 3* Encoding https://github.com/neitanod/forceutf8
5* logo by Brightmix http://www.iconfinder.com/icondetails/43256/128/jeans_monotone_pocket_icon 4* logo by Brightmix http://www.iconfinder.com/icondetails/43256/128/jeans_monotone_pocket_icon
6* icons http://icomoon.io 5* icons http://icomoon.io
7* PHP Simple HTML DOM Parser (for Pocket import) http://simplehtmldom.sourceforge.net/ 6* PHP Simple HTML DOM Parser (for Pocket import) http://simplehtmldom.sourceforge.net/
8* Session https://github.com/tontof/kriss_feed/blob/master/src/class/Session.php 7* Session https://github.com/tontof/kriss_feed/blob/master/src/class/Session.php
8* Twig http://twig.sensiolabs.org
9* Flash messages https://github.com/plasticbrain/PHP-Flash-Messages
10* Pagination https://github.com/daveismyname/pagination
9 11
10poche is developed by Nicolas Lœuillet under the Do What the Fuck You Want to Public License 12poche is developed by Nicolas Lœuillet under the Do What the Fuck You Want to Public License
11 13
12Contributors : 14Contributors : https://github.com/inthepoche/poche/graphs/contributors \ No newline at end of file
13Nicolas Lœuillet aka nico_somb
14Tom.C. aka tmos
15PeaceCopathe
16Gregoire_M \ No newline at end of file
diff --git a/INSTALL.md b/INSTALL.md
new file mode 100644
index 00000000..cf027282
--- /dev/null
+++ b/INSTALL.md
@@ -0,0 +1,53 @@
1# Installing poche
2
3Get the [latest dev version](https://github.com/inthepoche/poche/archive/dev.zip) of poche on github. Unzip it and upload it on your server.
4
5your datas can be stored on sqlite, postgres or mysql databases.
6
7Edit /inc/poche/config.inc.php :
8
9```php
10define ('STORAGE','sqlite'); # postgres, mysql, sqlite
11define ('STORAGE_SERVER', 'localhost'); # leave blank for sqlite
12define ('STORAGE_DB', 'poche'); # only for postgres & mysql
13define ('STORAGE_SQLITE', './db/poche.sqlite');
14define ('STORAGE_USER', 'user'); # leave blank for sqlite
15define ('STORAGE_PASSWORD', 'pass'); # leave blank for sqlite
16```
17
18poche must have write access on assets, cache and db directories.
19
20[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.
21
22## twig
23poche now uses twig for templating. You have to install twig.
24
25Install composer in your project :
26```bash
27curl -s http://getcomposer.org/installer | php
28```
29Install via composer :
30```bash
31php composer.phar install
32```
33
34If 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).
35
36## storage in sqlite
37You have to install [sqlite for php](http://www.php.net/manual/en/book.sqlite.php) on your server.
38
39Copy /install/poche.sqlite in /db
40
41## storage in mysql
42Execute /install/mysql.sql file in your database.
43
44## storage in postgres
45Execute /install/postgres.sql file in your database.
46
47## upgrading from poche <= 0.3
48With poche <= 0.3, all your datas were stored in a sqlite file. The structure of this file changed.
49
50You have to execute http://yourpoche/install/update_sqlite_from_0_to_1.php before using this new version.
51
52## installing poche
53you can go on your poche http://yourpoche. You have to fill the fields and that's all ! \ No newline at end of file
diff --git a/README.md b/README.md
index b44e7d36..5bea7ca4 100644
--- a/README.md
+++ b/README.md
@@ -1,5 +1,5 @@
1# poche 1# poche
2Abandon Pocket, Instapaper and other Readability service : adopt poche. It is the same, but it is open source. 2Abandon Pocket, Instapaper and other Readability service : adopt poche. It is the same, but it is open source. Moreover, you can migrate from Pocket & Readability.
3 3
4![poche](http://inthepoche.com/img/logo.png) 4![poche](http://inthepoche.com/img/logo.png)
5 5
@@ -11,24 +11,6 @@ To get news from poche, [follow us on twitter](http://twitter.com/getpoche) or [
11 11
12[![flattr](http://api.flattr.com/button/flattr-badge-large.png)](http://flattr.com/thing/1265480/poche-a-read-it-later-open-source-system) 12[![flattr](http://api.flattr.com/button/flattr-badge-large.png)](http://flattr.com/thing/1265480/poche-a-read-it-later-open-source-system)
13 13
14## Usage
15You can easily add a "poched" page with the bookmarklet.
16
17poche save the entire content of a poched links : text and pictures are stored on your server.
18
19You can :
20* read a page in a comfortable reading view
21* archive a link
22* put a link in favorite
23* delete a link
24
25## Requirements & installation
26You have to install [sqlite for php](http://www.php.net/manual/en/book.sqlite.php) on your server.
27
28Get 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.
29
30That's all, **poche works** !
31
32## Security 14## Security
33You **have** to protect your db/poche.sqlite file. Modify the virtual host of your website to add this condition : 15You **have** to protect your db/poche.sqlite file. Modify the virtual host of your website to add this condition :
34```apache 16```apache
@@ -46,12 +28,14 @@ location ~ /(db) {
46} 28}
47``` 29```
48 30
49## Import from Pocket 31## Usage
32See the documentation on our website : [inthepoche.com](http://inthepoche.com).
50 33
51If 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. 34## Travis
35[![Build Status](https://api.travis-ci.org/inthepoche/poche.png?branch=dev)](http://travis-ci.org/#!/inthepoche/poche)
52 36
53## License 37## License
54Copyright © 2010-2013 Nicolas Lœuillet <nicolas@loeuillet.org> 38Copyright © 2010-2013 Nicolas Lœuillet <nicolas.loeuillet@gmail.com>
55This work is free. You can redistribute it and/or modify it under the 39This work is free. You can redistribute it and/or modify it under the
56terms of the Do What The Fuck You Want To Public License, Version 2, 40terms of the Do What The Fuck You Want To Public License, Version 2,
57as published by Sam Hocevar. See the COPYING file for more details. 41as 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
index 00000000..ac3c0e98
--- /dev/null
+++ b/TODO.md
@@ -0,0 +1,11 @@
1# TODO
2
3pouvoir annuler la suppression
4conventions codage ? phing ? vérifier error_log qui trainent
5phpDocumentor
6minifier css
7revoir tous les css
8barre fixe d'admin sur la page d'un billet ?
9revoir export (export vers pocket &cie ? )
10raccourcis clavier
11date d'ajout d'un lien \ No newline at end of file
diff --git a/composer.json b/composer.json
new file mode 100644
index 00000000..6c69e48d
--- /dev/null
+++ b/composer.json
@@ -0,0 +1,7 @@
1{
2 "require": {
3 "twig/twig": "1.*",
4 "twig/extensions": "1.0.*",
5 "umpirsky/twig-gettext-extractor": "1.1.*"
6 }
7} \ No newline at end of file
diff --git a/composer.lock b/composer.lock
new file mode 100644
index 00000000..9e17fca9
--- /dev/null
+++ b/composer.lock
@@ -0,0 +1,744 @@
1{
2 "_readme": [
3 "This file locks the dependencies of your project to a known state",
4 "Read more about it at http://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file"
5 ],
6 "hash": "1c8badb14d91f4f3ef1cfae23252a2c4",
7 "packages": [
8 {
9 "name": "symfony/event-dispatcher",
10 "version": "v2.3.2",
11 "target-dir": "Symfony/Component/EventDispatcher",
12 "source": {
13 "type": "git",
14 "url": "https://github.com/symfony/EventDispatcher.git",
15 "reference": "v2.3.2"
16 },
17 "dist": {
18 "type": "zip",
19 "url": "https://api.github.com/repos/symfony/EventDispatcher/zipball/v2.3.2",
20 "reference": "v2.3.2",
21 "shasum": ""
22 },
23 "require": {
24 "php": ">=5.3.3"
25 },
26 "require-dev": {
27 "symfony/dependency-injection": "~2.0"
28 },
29 "suggest": {
30 "symfony/dependency-injection": "",
31 "symfony/http-kernel": ""
32 },
33 "type": "library",
34 "extra": {
35 "branch-alias": {
36 "dev-master": "2.3-dev"
37 }
38 },
39 "autoload": {
40 "psr-0": {
41 "Symfony\\Component\\EventDispatcher\\": ""
42 }
43 },
44 "notification-url": "https://packagist.org/downloads/",
45 "license": [
46 "MIT"
47 ],
48 "authors": [
49 {
50 "name": "Fabien Potencier",
51 "email": "fabien@symfony.com"
52 },
53 {
54 "name": "Symfony Community",
55 "homepage": "http://symfony.com/contributors"
56 }
57 ],
58 "description": "Symfony EventDispatcher Component",
59 "homepage": "http://symfony.com",
60 "time": "2013-05-13 14:36:40"
61 },
62 {
63 "name": "symfony/filesystem",
64 "version": "v2.3.2",
65 "target-dir": "Symfony/Component/Filesystem",
66 "source": {
67 "type": "git",
68 "url": "https://github.com/symfony/Filesystem.git",
69 "reference": "v2.3.2"
70 },
71 "dist": {
72 "type": "zip",
73 "url": "https://api.github.com/repos/symfony/Filesystem/zipball/v2.3.2",
74 "reference": "v2.3.2",
75 "shasum": ""
76 },
77 "require": {
78 "php": ">=5.3.3"
79 },
80 "type": "library",
81 "extra": {
82 "branch-alias": {
83 "dev-master": "2.3-dev"
84 }
85 },
86 "autoload": {
87 "psr-0": {
88 "Symfony\\Component\\Filesystem\\": ""
89 }
90 },
91 "notification-url": "https://packagist.org/downloads/",
92 "license": [
93 "MIT"
94 ],
95 "authors": [
96 {
97 "name": "Fabien Potencier",
98 "email": "fabien@symfony.com"
99 },
100 {
101 "name": "Symfony Community",
102 "homepage": "http://symfony.com/contributors"
103 }
104 ],
105 "description": "Symfony Filesystem Component",
106 "homepage": "http://symfony.com",
107 "time": "2013-06-04 15:02:05"
108 },
109 {
110 "name": "symfony/form",
111 "version": "v2.3.2",
112 "target-dir": "Symfony/Component/Form",
113 "source": {
114 "type": "git",
115 "url": "https://github.com/symfony/Form.git",
116 "reference": "v2.3.2"
117 },
118 "dist": {
119 "type": "zip",
120 "url": "https://api.github.com/repos/symfony/Form/zipball/v2.3.2",
121 "reference": "v2.3.2",
122 "shasum": ""
123 },
124 "require": {
125 "php": ">=5.3.3",
126 "symfony/event-dispatcher": "~2.1",
127 "symfony/intl": "~2.3",
128 "symfony/options-resolver": "~2.1",
129 "symfony/property-access": "~2.2"
130 },
131 "require-dev": {
132 "symfony/http-foundation": "~2.2",
133 "symfony/validator": "~2.2"
134 },
135 "suggest": {
136 "symfony/http-foundation": "",
137 "symfony/validator": ""
138 },
139 "type": "library",
140 "extra": {
141 "branch-alias": {
142 "dev-master": "2.3-dev"
143 }
144 },
145 "autoload": {
146 "psr-0": {
147 "Symfony\\Component\\Form\\": ""
148 }
149 },
150 "notification-url": "https://packagist.org/downloads/",
151 "license": [
152 "MIT"
153 ],
154 "authors": [
155 {
156 "name": "Fabien Potencier",
157 "email": "fabien@symfony.com"
158 },
159 {
160 "name": "Symfony Community",
161 "homepage": "http://symfony.com/contributors"
162 }
163 ],
164 "description": "Symfony Form Component",
165 "homepage": "http://symfony.com",
166 "time": "2013-07-01 12:24:43"
167 },
168 {
169 "name": "symfony/icu",
170 "version": "v1.0.0",
171 "target-dir": "Symfony/Component/Icu",
172 "source": {
173 "type": "git",
174 "url": "https://github.com/symfony/Icu.git",
175 "reference": "v1.0.0"
176 },
177 "dist": {
178 "type": "zip",
179 "url": "https://api.github.com/repos/symfony/Icu/zipball/v1.0.0",
180 "reference": "v1.0.0",
181 "shasum": ""
182 },
183 "require": {
184 "php": ">=5.3.3",
185 "symfony/intl": ">=2.3,<3.0"
186 },
187 "type": "library",
188 "autoload": {
189 "psr-0": {
190 "Symfony\\Component\\Icu\\": ""
191 }
192 },
193 "notification-url": "https://packagist.org/downloads/",
194 "license": [
195 "MIT"
196 ],
197 "authors": [
198 {
199 "name": "Symfony Community",
200 "homepage": "http://symfony.com/contributors"
201 },
202 {
203 "name": "Bernhard Schussek",
204 "email": "bschussek@gmail.com"
205 }
206 ],
207 "description": "Contains an excerpt of the ICU data and classes to load it.",
208 "homepage": "http://symfony.com",
209 "keywords": [
210 "icu",
211 "intl"
212 ],
213 "time": "2013-06-03 18:32:07"
214 },
215 {
216 "name": "symfony/intl",
217 "version": "v2.3.2",
218 "target-dir": "Symfony/Component/Intl",
219 "source": {
220 "type": "git",
221 "url": "https://github.com/symfony/Intl.git",
222 "reference": "v2.3.2"
223 },
224 "dist": {
225 "type": "zip",
226 "url": "https://api.github.com/repos/symfony/Intl/zipball/v2.3.2",
227 "reference": "v2.3.2",
228 "shasum": ""
229 },
230 "require": {
231 "php": ">=5.3.3",
232 "symfony/icu": "~1.0-RC"
233 },
234 "require-dev": {
235 "symfony/filesystem": ">=2.1"
236 },
237 "suggest": {
238 "ext-intl": "to use the component with locales other than \"en\""
239 },
240 "type": "library",
241 "extra": {
242 "branch-alias": {
243 "dev-master": "2.3-dev"
244 }
245 },
246 "autoload": {
247 "psr-0": {
248 "Symfony\\Component\\Intl\\": ""
249 },
250 "classmap": [
251 "Symfony/Component/Intl/Resources/stubs"
252 ],
253 "files": [
254 "Symfony/Component/Intl/Resources/stubs/functions.php"
255 ]
256 },
257 "notification-url": "https://packagist.org/downloads/",
258 "license": [
259 "MIT"
260 ],
261 "authors": [
262 {
263 "name": "Symfony Community",
264 "homepage": "http://symfony.com/contributors"
265 },
266 {
267 "name": "Igor Wiedler",
268 "email": "igor@wiedler.ch",
269 "homepage": "http://wiedler.ch/igor/"
270 },
271 {
272 "name": "Bernhard Schussek",
273 "email": "bschussek@gmail.com"
274 },
275 {
276 "name": "Eriksen Costa",
277 "email": "eriksen.costa@infranology.com.br"
278 }
279 ],
280 "description": "A PHP replacement layer for the C intl extension that includes additional data from the ICU library.",
281 "homepage": "http://symfony.com",
282 "keywords": [
283 "i18n",
284 "icu",
285 "internationalization",
286 "intl",
287 "l10n",
288 "localization"
289 ],
290 "time": "2013-07-08 13:00:35"
291 },
292 {
293 "name": "symfony/options-resolver",
294 "version": "v2.3.2",
295 "target-dir": "Symfony/Component/OptionsResolver",
296 "source": {
297 "type": "git",
298 "url": "https://github.com/symfony/OptionsResolver.git",
299 "reference": "v2.3.2"
300 },
301 "dist": {
302 "type": "zip",
303 "url": "https://api.github.com/repos/symfony/OptionsResolver/zipball/v2.3.2",
304 "reference": "v2.3.2",
305 "shasum": ""
306 },
307 "require": {
308 "php": ">=5.3.3"
309 },
310 "type": "library",
311 "extra": {
312 "branch-alias": {
313 "dev-master": "2.3-dev"
314 }
315 },
316 "autoload": {
317 "psr-0": {
318 "Symfony\\Component\\OptionsResolver\\": ""
319 }
320 },
321 "notification-url": "https://packagist.org/downloads/",
322 "license": [
323 "MIT"
324 ],
325 "authors": [
326 {
327 "name": "Fabien Potencier",
328 "email": "fabien@symfony.com"
329 },
330 {
331 "name": "Symfony Community",
332 "homepage": "http://symfony.com/contributors"
333 }
334 ],
335 "description": "Symfony OptionsResolver Component",
336 "homepage": "http://symfony.com",
337 "keywords": [
338 "config",
339 "configuration",
340 "options"
341 ],
342 "time": "2013-04-11 06:50:46"
343 },
344 {
345 "name": "symfony/property-access",
346 "version": "v2.3.2",
347 "target-dir": "Symfony/Component/PropertyAccess",
348 "source": {
349 "type": "git",
350 "url": "https://github.com/symfony/PropertyAccess.git",
351 "reference": "v2.3.2"
352 },
353 "dist": {
354 "type": "zip",
355 "url": "https://api.github.com/repos/symfony/PropertyAccess/zipball/v2.3.2",
356 "reference": "v2.3.2",
357 "shasum": ""
358 },
359 "require": {
360 "php": ">=5.3.3"
361 },
362 "type": "library",
363 "extra": {
364 "branch-alias": {
365 "dev-master": "2.3-dev"
366 }
367 },
368 "autoload": {
369 "psr-0": {
370 "Symfony\\Component\\PropertyAccess\\": ""
371 }
372 },
373 "notification-url": "https://packagist.org/downloads/",
374 "license": [
375 "MIT"
376 ],
377 "authors": [
378 {
379 "name": "Fabien Potencier",
380 "email": "fabien@symfony.com"
381 },
382 {
383 "name": "Symfony Community",
384 "homepage": "http://symfony.com/contributors"
385 }
386 ],
387 "description": "Symfony PropertyAccess Component",
388 "homepage": "http://symfony.com",
389 "keywords": [
390 "access",
391 "array",
392 "extraction",
393 "index",
394 "injection",
395 "object",
396 "property",
397 "property path",
398 "reflection"
399 ],
400 "time": "2013-07-01 12:24:43"
401 },
402 {
403 "name": "symfony/routing",
404 "version": "v2.3.2",
405 "target-dir": "Symfony/Component/Routing",
406 "source": {
407 "type": "git",
408 "url": "https://github.com/symfony/Routing.git",
409 "reference": "v2.3.2"
410 },
411 "dist": {
412 "type": "zip",
413 "url": "https://api.github.com/repos/symfony/Routing/zipball/v2.3.2",
414 "reference": "v2.3.2",
415 "shasum": ""
416 },
417 "require": {
418 "php": ">=5.3.3"
419 },
420 "require-dev": {
421 "doctrine/common": "~2.2",
422 "psr/log": "~1.0",
423 "symfony/config": "~2.2",
424 "symfony/yaml": "~2.0"
425 },
426 "suggest": {
427 "doctrine/common": "",
428 "symfony/config": "",
429 "symfony/yaml": ""
430 },
431 "type": "library",
432 "extra": {
433 "branch-alias": {
434 "dev-master": "2.3-dev"
435 }
436 },
437 "autoload": {
438 "psr-0": {
439 "Symfony\\Component\\Routing\\": ""
440 }
441 },
442 "notification-url": "https://packagist.org/downloads/",
443 "license": [
444 "MIT"
445 ],
446 "authors": [
447 {
448 "name": "Fabien Potencier",
449 "email": "fabien@symfony.com"
450 },
451 {
452 "name": "Symfony Community",
453 "homepage": "http://symfony.com/contributors"
454 }
455 ],
456 "description": "Symfony Routing Component",
457 "homepage": "http://symfony.com",
458 "time": "2013-06-23 08:16:02"
459 },
460 {
461 "name": "symfony/translation",
462 "version": "v2.3.2",
463 "target-dir": "Symfony/Component/Translation",
464 "source": {
465 "type": "git",
466 "url": "https://github.com/symfony/Translation.git",
467 "reference": "v2.3.2"
468 },
469 "dist": {
470 "type": "zip",
471 "url": "https://api.github.com/repos/symfony/Translation/zipball/v2.3.2",
472 "reference": "v2.3.2",
473 "shasum": ""
474 },
475 "require": {
476 "php": ">=5.3.3"
477 },
478 "require-dev": {
479 "symfony/config": "~2.0",
480 "symfony/yaml": "~2.2"
481 },
482 "suggest": {
483 "symfony/config": "",
484 "symfony/yaml": ""
485 },
486 "type": "library",
487 "extra": {
488 "branch-alias": {
489 "dev-master": "2.3-dev"
490 }
491 },
492 "autoload": {
493 "psr-0": {
494 "Symfony\\Component\\Translation\\": ""
495 }
496 },
497 "notification-url": "https://packagist.org/downloads/",
498 "license": [
499 "MIT"
500 ],
501 "authors": [
502 {
503 "name": "Fabien Potencier",
504 "email": "fabien@symfony.com"
505 },
506 {
507 "name": "Symfony Community",
508 "homepage": "http://symfony.com/contributors"
509 }
510 ],
511 "description": "Symfony Translation Component",
512 "homepage": "http://symfony.com",
513 "time": "2013-05-13 14:36:40"
514 },
515 {
516 "name": "symfony/twig-bridge",
517 "version": "v2.3.2",
518 "target-dir": "Symfony/Bridge/Twig",
519 "source": {
520 "type": "git",
521 "url": "https://github.com/symfony/TwigBridge.git",
522 "reference": "v2.3.2"
523 },
524 "dist": {
525 "type": "zip",
526 "url": "https://api.github.com/repos/symfony/TwigBridge/zipball/v2.3.2",
527 "reference": "v2.3.2",
528 "shasum": ""
529 },
530 "require": {
531 "php": ">=5.3.3",
532 "twig/twig": "~1.11"
533 },
534 "require-dev": {
535 "symfony/form": "2.2.*",
536 "symfony/http-kernel": "~2.2",
537 "symfony/routing": "~2.2",
538 "symfony/security": "~2.0",
539 "symfony/templating": "~2.1",
540 "symfony/translation": "~2.2",
541 "symfony/yaml": "~2.0"
542 },
543 "suggest": {
544 "symfony/form": "",
545 "symfony/http-kernel": "",
546 "symfony/routing": "",
547 "symfony/security": "",
548 "symfony/templating": "",
549 "symfony/translation": "",
550 "symfony/yaml": ""
551 },
552 "type": "symfony-bridge",
553 "extra": {
554 "branch-alias": {
555 "dev-master": "2.3-dev"
556 }
557 },
558 "autoload": {
559 "psr-0": {
560 "Symfony\\Bridge\\Twig\\": ""
561 }
562 },
563 "notification-url": "https://packagist.org/downloads/",
564 "license": [
565 "MIT"
566 ],
567 "authors": [
568 {
569 "name": "Fabien Potencier",
570 "email": "fabien@symfony.com"
571 },
572 {
573 "name": "Symfony Community",
574 "homepage": "http://symfony.com/contributors"
575 }
576 ],
577 "description": "Symfony Twig Bridge",
578 "homepage": "http://symfony.com",
579 "time": "2013-05-16 10:19:58"
580 },
581 {
582 "name": "twig/extensions",
583 "version": "v1.0.0",
584 "source": {
585 "type": "git",
586 "url": "https://github.com/fabpot/Twig-extensions.git",
587 "reference": "v1.0.0"
588 },
589 "dist": {
590 "type": "zip",
591 "url": "https://api.github.com/repos/fabpot/Twig-extensions/zipball/v1.0.0",
592 "reference": "v1.0.0",
593 "shasum": ""
594 },
595 "require": {
596 "twig/twig": "1.*"
597 },
598 "type": "library",
599 "extra": {
600 "branch-alias": {
601 "dev-master": "1.0.x-dev"
602 }
603 },
604 "autoload": {
605 "psr-0": {
606 "Twig_Extensions_": "lib/"
607 }
608 },
609 "notification-url": "https://packagist.org/downloads/",
610 "license": [
611 "MIT"
612 ],
613 "authors": [
614 {
615 "name": "Fabien Potencier",
616 "email": "fabien@symfony.com"
617 }
618 ],
619 "description": "Common additional features for Twig that do not directly belong in core",
620 "homepage": "https://github.com/fabpot/Twig-extensions",
621 "keywords": [
622 "debug",
623 "i18n",
624 "text"
625 ],
626 "time": "2013-02-28 14:21:30"
627 },
628 {
629 "name": "twig/twig",
630 "version": "v1.13.2",
631 "source": {
632 "type": "git",
633 "url": "https://github.com/fabpot/Twig.git",
634 "reference": "v1.13.2"
635 },
636 "dist": {
637 "type": "zip",
638 "url": "https://api.github.com/repos/fabpot/Twig/zipball/v1.13.2",
639 "reference": "v1.13.2",
640 "shasum": ""
641 },
642 "require": {
643 "php": ">=5.2.4"
644 },
645 "type": "library",
646 "extra": {
647 "branch-alias": {
648 "dev-master": "1.13-dev"
649 }
650 },
651 "autoload": {
652 "psr-0": {
653 "Twig_": "lib/"
654 }
655 },
656 "notification-url": "https://packagist.org/downloads/",
657 "license": [
658 "BSD-3-Clause"
659 ],
660 "authors": [
661 {
662 "name": "Fabien Potencier",
663 "email": "fabien@symfony.com"
664 },
665 {
666 "name": "Armin Ronacher",
667 "email": "armin.ronacher@active-4.com"
668 }
669 ],
670 "description": "Twig, the flexible, fast, and secure template language for PHP",
671 "homepage": "http://twig.sensiolabs.org",
672 "keywords": [
673 "templating"
674 ],
675 "time": "2013-08-03 15:35:31"
676 },
677 {
678 "name": "umpirsky/twig-gettext-extractor",
679 "version": "1.1.3",
680 "source": {
681 "type": "git",
682 "url": "https://github.com/umpirsky/Twig-Gettext-Extractor.git",
683 "reference": "1.1.3"
684 },
685 "dist": {
686 "type": "zip",
687 "url": "https://api.github.com/repos/umpirsky/Twig-Gettext-Extractor/zipball/1.1.3",
688 "reference": "1.1.3",
689 "shasum": ""
690 },
691 "require": {
692 "php": ">=5.3.3",
693 "symfony/filesystem": ">=2.0,<3.0",
694 "symfony/form": ">=2.0,<3.0",
695 "symfony/routing": ">=2.0,<3.0",
696 "symfony/translation": ">=2.0,<3.0",
697 "symfony/twig-bridge": ">=2.0,<3.0",
698 "twig/extensions": "1.0.*",
699 "twig/twig": ">=1.2.0,<2.0-dev"
700 },
701 "require-dev": {
702 "symfony/config": "2.1.*"
703 },
704 "bin": [
705 "twig-gettext-extractor"
706 ],
707 "type": "application",
708 "autoload": {
709 "psr-0": {
710 "Twig\\Gettext": "."
711 }
712 },
713 "notification-url": "https://packagist.org/downloads/",
714 "license": [
715 "MIT"
716 ],
717 "authors": [
718 {
719 "name": "Саша Стаменковић",
720 "email": "umpirsky@gmail.com",
721 "homepage": "http://umpirsky.com"
722 }
723 ],
724 "description": "The Twig Gettext Extractor is Poedit friendly tool which extracts translations from twig templates.",
725 "time": "2013-02-14 16:41:48"
726 }
727 ],
728 "packages-dev": [
729
730 ],
731 "aliases": [
732
733 ],
734 "minimum-stability": "stable",
735 "stability-flags": [
736
737 ],
738 "platform": [
739
740 ],
741 "platform-dev": [
742
743 ]
744}
diff --git a/css/style-dark.css b/css/style-dark.css
deleted file mode 100644
index 813c291d..00000000
--- a/css/style-dark.css
+++ /dev/null
@@ -1,90 +0,0 @@
1/*** GENERAL ***/
2body {
3 color: #fff;
4 background-color: #0d0d0d;
5}
6
7a, a:hover, a:visited {
8 color: #fff;
9}
10
11#main ul#links li a.current {
12 background-color: #000;
13 color: #fff;
14}
15
16#links a:hover, .backhome a:hover{
17 background-color: #fff;
18 color: #000;
19}
20
21input[type=submit].delete {
22 background : url('../img/dark/remove.png') no-repeat center center;
23 color : transparent;
24}
25
26#main .entrie {
27 color: #fff;
28 background-color: #000;
29 border: 1px solid #fff;
30}
31
32#main .entrie h2 a:hover {
33 color: #29B1E3;
34}
35
36a.fav span {
37 background: url('../img/dark/star-on.png') no-repeat;
38}
39
40a.fav span:hover {
41 background: url('../img/dark/star-off.png') no-repeat;
42}
43
44a.fav-off span {
45 background: url('../img/dark/star-off.png') no-repeat;
46}
47
48a.fav-off span:hover {
49 background: url('../img/dark/star-on.png') no-repeat;
50}
51
52a.archive span {
53 background: url('../img/dark/checkmark-on.png') no-repeat;
54}
55
56a.archive span:hover {
57 background: url('../img/dark/checkmark-off.png') no-repeat;
58}
59
60a.archive-off span {
61 background: url('../img/dark/checkmark-off.png') no-repeat;
62}
63
64a.archive-off span:hover {
65 background: url('../img/dark/checkmark-on.png') no-repeat;
66}
67
68/*** ***/
69/*** ARTICLE PAGE ***/
70
71body.article {
72 color: #fff;
73 background-color: #0d0d0d;
74}
75
76#article header {
77 border-bottom: 1px solid #222222;
78}
79
80#article article {
81 border-bottom: 1px solid #222222;
82}
83
84.vieworiginal a {
85 color: #888888;
86}
87
88.entrie {
89 background-color: #fff;
90}
diff --git a/css/style-light.css b/css/style-light.css
deleted file mode 100644
index cd2384c3..00000000
--- a/css/style-light.css
+++ /dev/null
@@ -1,100 +0,0 @@
1/*** GENERAL ***/
2body {
3 color: #222222;
4 background-color: #F1F1F1;
5}
6
7a, a:hover, a:visited {
8 color: #000;
9}
10
11.bouton {
12 background-color: #000;
13 color: #fff;
14 border: none;
15}
16.bouton:hover {
17 background-color: #222222;
18 color: #F1F1F1;
19}
20
21#main ul#links li a.current {
22 background-color: #000;
23 color: #fff;
24}
25
26#links a:hover, .backhome a:hover{
27 background-color: #040707;
28 color: #F1F1F1;
29}
30
31input[type=submit].delete {
32 background : url('../img/light/remove.png') no-repeat center center;
33 color : transparent;
34}
35
36#main .entrie {
37 color: #2e2e2e;
38 background-color: #ffffff;
39 border: 1px solid #000;
40}
41
42#main .entrie h2 a:hover {
43 color: #F5BE00;
44}
45
46a.fav span {
47 background: url('../img/light/star-on.png') no-repeat;
48}
49
50a.fav span:hover {
51 background: url('../img/light/star-off.png') no-repeat;
52}
53
54a.fav-off span {
55 background: url('../img/light/star-off.png') no-repeat;
56}
57
58a.fav-off span:hover {
59 background: url('../img/light/star-on.png') no-repeat;
60}
61
62a.archive span {
63 background: url('../img/light/checkmark-on.png') no-repeat;
64}
65
66a.archive span:hover {
67 background: url('../img/light/checkmark-off.png') no-repeat;
68}
69
70a.archive-off span {
71 background: url('../img/light/checkmark-off.png') no-repeat;
72}
73
74a.archive-off span:hover {
75 background: url('../img/light/checkmark-on.png') no-repeat;
76}
77
78/*** ***/
79/*** ARTICLE PAGE ***/
80
81body.article {
82 color: #222222;
83 background-color: #F1F1F1;
84}
85
86#article header {
87 border-bottom: 1px solid #222222;
88}
89
90#article article {
91 border-bottom: 1px solid #222222;
92}
93
94.vieworiginal a {
95 color: #888888;
96}
97
98.entrie {
99 background-color: #fff;
100}
diff --git a/css/style.css b/css/style.css
deleted file mode 100644
index 9fadfa96..00000000
--- a/css/style.css
+++ /dev/null
@@ -1,215 +0,0 @@
1/*** GENERAL ***/
2body {
3 font: 20px/1.3em Palatino,Georgia,serif;
4 margin: 10px;
5}
6
7header {
8 text-align: center;
9}
10
11.bouton {
12 border-radius: 2px;
13}
14
15#main ul#links {
16 padding: 0;
17 list-style-type: none;
18 text-align: center;
19}
20
21#main ul#links li {
22 display: inline;
23}
24
25#main ul#links li a.current {
26 -webkit-border-radius: 2px;
27 border-radius: 2px;
28}
29
30#main ul#sort {
31 padding: 0;
32 list-style-type: none;
33 text-align: center;
34}
35
36#main ul#sort li {
37 display: inline;
38 font-size: 0.9em;
39}
40
41#main ul#sort img:hover {
42 cursor: pointer;
43}
44
45#main, #article {
46 margin: 0 auto;
47}
48
49#links a, .backhome a{
50 text-decoration: none;
51 padding: 5px 10px;
52}
53#links a:hover, .backhome a:hover{
54 -webkit-border-radius: 2px;
55 border-radius: 2px;
56}
57
58footer {
59 text-align: right;
60}
61
62/*** ***/
63/*** LINKS DISPLAY ***/
64
65#main a.tool {
66 text-decoration: none;
67 cursor: pointer;
68}
69
70input[type=submit].delete {
71 width : 16px;
72 height :16px;
73 border : none;
74 cursor: pointer;
75 font-size : 0;
76}
77
78#main #content {
79 margin-top: 20px;
80}
81
82#main .entrie {
83 padding: 15px;
84 min-height: 8em;
85 border: 1px solid;
86}
87
88#main .entrie h2 a {
89 text-decoration: none;
90}
91
92.tools {
93 text-align: right;
94}
95
96.tools ul {
97 padding: 0; margin: 0;
98 list-style-type: none;
99}
100
101.tools ul li {
102 line-height: 20px;
103}
104
105.tools a.tool {
106 cursor: pointer;
107}
108
109#article .tools {
110 position: relative;
111 display: inline;
112 top: 0px;
113 right: 0px;
114 width: 100%;
115 text-align: left;
116}
117
118#article .tools ul li{
119 display: inline;
120}
121
122#main .entrie .tools a.tool span, #article .tools a.tool span {
123 display: inline-block;
124 width: 16px;
125 height: 16px;
126}
127
128
129/*** ***/
130/*** ARTICLE PAGE ***/
131
132body.article {
133 font: 20px/1.3em Palatino,Georgia,serif;
134}
135
136#article header {
137 text-align: left;
138}
139
140#article header a {
141 text-decoration: none;
142}
143
144.vieworiginal a {
145 text-decoration: none;
146}
147
148.backhome {
149 display: inline;
150}
151
152/*** ***/
153
154#main
155{
156 max-width: 60em; /* 960 px */
157 margin: 0 auto;
158}
159#content
160{
161 width: 103.125%; /* 990px */
162 overflow: hidden;
163 margin-left: -1.562%; /* 15px */
164 margin-bottom: -1.875em; /* 30px */
165}
166
167.entrie
168{
169 width: 30.303%; /* 300px */
170 background-color: #fff;
171 float: left;
172 margin: 0 1.515% 1.875em; /* 15px 30px */
173}
174
175@media only screen and ( max-width: 40em ) /* 640px */
176{
177 .entrie
178 {
179 width: 46.876%; /* 305px */
180 margin-bottom: 0.938em; /* 15px */
181 }
182}
183
184@media only screen and ( max-width: 20em ) /* 320px */
185{
186 #content
187 {
188 width: 100%;
189 margin-left: 0;
190 }
191
192 .entrie
193 {
194 width: 100%;
195 margin-left: 0;
196 margin-right: 0;
197 }
198}
199
200/*** ***/
201/*** MESSAGES ***/
202
203.messages { width: 100%; -moz-border-radius: 4px; border-radius: 4px; display: block; padding: 10px 0; margin: 10px auto 10px; clear: both; }
204.messages a.closeMessage { margin: -14px -8px 0 0; display:none; width: 16px; height: 16px; float: right; background: url(../img/messages/close.png) no-repeat; }
205/*.messages:hover a.closeMessage { visibility:visible; }*/
206.messages p { margin: 3px 0 3px 10px !important; padding: 0 10px 0 23px !important; font-size: 14px; line-height: 16px; }
207.messages.error { border: 1px solid #C42608; color: #c00 !important; background: #FFF0EF; }
208.messages.error p { background: url(../img/messages/cross.png ) no-repeat 0px 50%; color:#c00 !important; }
209.messages.success {background: #E0FBCC; border: 1px solid #6DC70C; }
210.messages.success p { background: url(../img/messages/tick.png) no-repeat 0px 50%; color: #2B6301 !important; }
211.messages.warning { background: #FFFCD3; border: 1px solid #EBCD41; color: #000; }
212.messages.warning p { background: url(../img/messages/warning.png ) no-repeat 0px 50%; color: #5F4E01; }
213.messages.information, .messages.info { background: #DFEBFB; border: 1px solid #82AEE7; }
214.messages.information p, .messages.info p { background: url(../img/messages/help.png ) no-repeat 0px 50%; color: #064393; }
215.messages.information a { text-decoration: underline; }
diff --git a/img/dark/checkmark-off.png b/img/dark/checkmark-off.png
deleted file mode 100644
index efc3439f..00000000
--- a/img/dark/checkmark-off.png
+++ /dev/null
Binary files differ
diff --git a/img/dark/checkmark-on.png b/img/dark/checkmark-on.png
deleted file mode 100644
index 24391c2e..00000000
--- a/img/dark/checkmark-on.png
+++ /dev/null
Binary files differ
diff --git a/img/dark/down.png b/img/dark/down.png
deleted file mode 100644
index 41ea9604..00000000
--- a/img/dark/down.png
+++ /dev/null
Binary files differ
diff --git a/img/dark/logo.png b/img/dark/logo.png
deleted file mode 100644
index 9fba0642..00000000
--- a/img/dark/logo.png
+++ /dev/null
Binary files differ
diff --git a/img/dark/remove.png b/img/dark/remove.png
deleted file mode 100644
index 41786fd7..00000000
--- a/img/dark/remove.png
+++ /dev/null
Binary files differ
diff --git a/img/dark/star-off.png b/img/dark/star-off.png
deleted file mode 100644
index 90651b54..00000000
--- a/img/dark/star-off.png
+++ /dev/null
Binary files differ
diff --git a/img/dark/star-on.png b/img/dark/star-on.png
deleted file mode 100644
index 7fc14477..00000000
--- a/img/dark/star-on.png
+++ /dev/null
Binary files differ
diff --git a/img/dark/up.png b/img/dark/up.png
deleted file mode 100644
index 1679e18f..00000000
--- a/img/dark/up.png
+++ /dev/null
Binary files differ
diff --git a/img/logo.png b/img/logo.png
deleted file mode 100644
index f917857f..00000000
--- a/img/logo.png
+++ /dev/null
Binary files differ
diff --git a/import.php b/import.php
deleted file mode 100644
index 72e3eac7..00000000
--- a/import.php
+++ /dev/null
@@ -1,50 +0,0 @@
1<?php
2/**
3 * poche, a read it later open source system
4 *
5 * @category poche
6 * @author Nicolas Lœuillet <support@inthepoche.com>
7 * @copyright 2013
8 * @license http://www.wtfpl.net/ see COPYING file
9 */
10
11set_time_limit(0);
12
13include dirname(__FILE__).'/inc/config.php';
14include dirname(__FILE__).'/inc/simple_html_dom.php';
15
16if (!isset($_GET['start'])) {
17 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>';
18}
19else {
20 $html = new simple_html_dom();
21 $html->load_file('ril_export.html');
22
23 $read = 0;
24 $errors = array();
25 foreach($html->find('ul') as $ul)
26 {
27 foreach($ul->find('li') as $li)
28 {
29 $a = $li->find('a');
30 $url = $a[0]->href;
31
32
33 action_to_do('add', $url);
34 if ($read == '1') {
35 $last_id = $db->getHandle()->lastInsertId();
36 $sql_update = "UPDATE entries SET is_read=~is_read WHERE id=?";
37 $params_update = array($last_id);
38 $query_update = $db->getHandle()->prepare($sql_update);
39 $query_update->execute($params_update);
40 }
41 }
42 # Pocket génère un fichier HTML avec deux <ul>
43 # Le premier concerne les éléments non lus
44 # Le second concerne les éléments archivés
45 $read = 1;
46 }
47
48 echo 'Import from Pocket completed. <a href="index.php">Welcome to #poche !</a>';
49 logm('import from pocket completed');
50} \ No newline at end of file
diff --git a/inc/Encoding.php b/inc/3rdparty/Encoding.php
index 577763b4..577763b4 100644
--- a/inc/Encoding.php
+++ b/inc/3rdparty/Encoding.php
diff --git a/inc/JSLikeHTMLElement.php b/inc/3rdparty/JSLikeHTMLElement.php
index dfcc1be5..238ba8a8 100644
--- a/inc/JSLikeHTMLElement.php
+++ b/inc/3rdparty/JSLikeHTMLElement.php
@@ -4,7 +4,7 @@
4* 4*
5* This class extends PHP's DOMElement to allow 5* This class extends PHP's DOMElement to allow
6* users to get and set the innerHTML property of 6* users to get and set the innerHTML property of
7* HTML elements in the same way it's done in 7* HTML elements in the same way it's done in
8* JavaScript. 8* JavaScript.
9* 9*
10* Example usage: 10* Example usage:
@@ -15,16 +15,16 @@
15* $doc->registerNodeClass('DOMElement', 'JSLikeHTMLElement'); 15* $doc->registerNodeClass('DOMElement', 'JSLikeHTMLElement');
16* $doc->loadHTML('<div><p>Para 1</p><p>Para 2</p></div>'); 16* $doc->loadHTML('<div><p>Para 1</p><p>Para 2</p></div>');
17* $elem = $doc->getElementsByTagName('div')->item(0); 17* $elem = $doc->getElementsByTagName('div')->item(0);
18* 18*
19* // print innerHTML 19* // print innerHTML
20* echo $elem->innerHTML; // prints '<p>Para 1</p><p>Para 2</p>' 20* echo $elem->innerHTML; // prints '<p>Para 1</p><p>Para 2</p>'
21* echo "\n\n"; 21* echo "\n\n";
22* 22*
23* // set innerHTML 23* // set innerHTML
24* $elem->innerHTML = '<a href="http://fivefilters.org">FiveFilters.org</a>'; 24* $elem->innerHTML = '<a href="http://fivefilters.org">FiveFilters.org</a>';
25* echo $elem->innerHTML; // prints '<a href="http://fivefilters.org">FiveFilters.org</a>' 25* echo $elem->innerHTML; // prints '<a href="http://fivefilters.org">FiveFilters.org</a>'
26* echo "\n\n"; 26* echo "\n\n";
27* 27*
28* // print document (with our changes) 28* // print document (with our changes)
29* echo $doc->saveXML(); 29* echo $doc->saveXML();
30* @endcode 30* @endcode
@@ -59,7 +59,7 @@ class JSLikeHTMLElement extends DOMElement
59 $value = mb_convert_encoding($value, 'HTML-ENTITIES', 'UTF-8'); 59 $value = mb_convert_encoding($value, 'HTML-ENTITIES', 'UTF-8');
60 // Using <htmlfragment> will generate a warning, but so will bad HTML 60 // Using <htmlfragment> will generate a warning, but so will bad HTML
61 // (and by this point, bad HTML is what we've got). 61 // (and by this point, bad HTML is what we've got).
62 // We use it (and suppress the warning) because an HTML fragment will 62 // We use it (and suppress the warning) because an HTML fragment will
63 // be wrapped around <html><body> tags which we don't really want to keep. 63 // be wrapped around <html><body> tags which we don't really want to keep.
64 // Note: despite the warning, if loadHTML succeeds it will return true. 64 // Note: despite the warning, if loadHTML succeeds it will return true.
65 $result = @$f->loadHTML('<htmlfragment>'.$value.'</htmlfragment>'); 65 $result = @$f->loadHTML('<htmlfragment>'.$value.'</htmlfragment>');
@@ -86,7 +86,7 @@ class JSLikeHTMLElement extends DOMElement
86 * @code 86 * @code
87 * $string = $div->innerHTML; 87 * $string = $div->innerHTML;
88 * @endcode 88 * @endcode
89 */ 89 */
90 public function __get($name) 90 public function __get($name)
91 { 91 {
92 if ($name == 'innerHTML') { 92 if ($name == 'innerHTML') {
diff --git a/inc/Readability.php b/inc/3rdparty/Readability.php
index d28d28f9..e1e8738b 100644
--- a/inc/Readability.php
+++ b/inc/3rdparty/Readability.php
@@ -1,5 +1,5 @@
1<?php 1<?php
2/** 2/**
3* Arc90's Readability ported to PHP for FiveFilters.org 3* Arc90's Readability ported to PHP for FiveFilters.org
4* Based on readability.js version 1.7.1 (without multi-page support) 4* Based on readability.js version 1.7.1 (without multi-page support)
5* Updated to allow HTML5 parsing with html5lib 5* Updated to allow HTML5 parsing with html5lib
@@ -13,34 +13,34 @@
13* License: Apache License, Version 2.0 13* License: Apache License, Version 2.0
14* Requires: PHP5 14* Requires: PHP5
15* Date: 2012-09-19 15* Date: 2012-09-19
16* 16*
17* Differences between the PHP port and the original 17* Differences between the PHP port and the original
18* ------------------------------------------------------ 18* ------------------------------------------------------
19* Arc90's Readability is designed to run in the browser. It works on the DOM 19* Arc90's Readability is designed to run in the browser. It works on the DOM
20* tree (the parsed HTML) after the page's CSS styles have been applied and 20* tree (the parsed HTML) after the page's CSS styles have been applied and
21* Javascript code executed. This PHP port does not run inside a browser. 21* Javascript code executed. This PHP port does not run inside a browser.
22* We use PHP's ability to parse HTML to build our DOM tree, but we cannot 22* We use PHP's ability to parse HTML to build our DOM tree, but we cannot
23* rely on CSS or Javascript support. As such, the results will not always 23* rely on CSS or Javascript support. As such, the results will not always
24* match Arc90's Readability. (For example, if a web page contains CSS style 24* match Arc90's Readability. (For example, if a web page contains CSS style
25* rules or Javascript code which hide certain HTML elements from display, 25* rules or Javascript code which hide certain HTML elements from display,
26* Arc90's Readability will dismiss those from consideration but our PHP port, 26* Arc90's Readability will dismiss those from consideration but our PHP port,
27* unable to understand CSS or Javascript, will not know any better.) 27* unable to understand CSS or Javascript, will not know any better.)
28* 28*
29* Another significant difference is that the aim of Arc90's Readability is 29* Another significant difference is that the aim of Arc90's Readability is
30* to re-present the main content block of a given web page so users can 30* to re-present the main content block of a given web page so users can
31* read it more easily in their browsers. Correct identification, clean up, 31* read it more easily in their browsers. Correct identification, clean up,
32* and separation of the content block is only a part of this process. 32* and separation of the content block is only a part of this process.
33* This PHP port is only concerned with this part, it does not include code 33* This PHP port is only concerned with this part, it does not include code
34* that relates to presentation in the browser - Arc90 already do 34* that relates to presentation in the browser - Arc90 already do
35* that extremely well, and for PDF output there's FiveFilters.org's 35* that extremely well, and for PDF output there's FiveFilters.org's
36* PDF Newspaper: http://fivefilters.org/pdf-newspaper/. 36* PDF Newspaper: http://fivefilters.org/pdf-newspaper/.
37* 37*
38* Finally, this class contains methods that might be useful for developers 38* Finally, this class contains methods that might be useful for developers
39* working on HTML document fragments. So without deviating too much from 39* working on HTML document fragments. So without deviating too much from
40* the original code (which I don't want to do because it makes debugging 40* the original code (which I don't want to do because it makes debugging
41* and updating more difficult), I've tried to make it a little more 41* and updating more difficult), I've tried to make it a little more
42* developer friendly. You should be able to use the methods here on 42* developer friendly. You should be able to use the methods here on
43* existing DOMElement objects without passing an entire HTML document to 43* existing DOMElement objects without passing an entire HTML document to
44* be parsed. 44* be parsed.
45*/ 45*/
46 46
@@ -48,7 +48,7 @@
48require_once(dirname(__FILE__).'/JSLikeHTMLElement.php'); 48require_once(dirname(__FILE__).'/JSLikeHTMLElement.php');
49 49
50// Alternative usage (for testing only!) 50// Alternative usage (for testing only!)
51// uncomment the lines below and call Readability.php in your browser 51// uncomment the lines below and call Readability.php in your browser
52// passing it the URL of the page you'd like content from, e.g.: 52// passing it the URL of the page you'd like content from, e.g.:
53// Readability.php?url=http://medialens.org/alerts/09/090615_the_guardian_climate.php 53// Readability.php?url=http://medialens.org/alerts/09/090615_the_guardian_climate.php
54 54
@@ -75,11 +75,11 @@ class Readability
75 public $url = null; // optional - URL where HTML was retrieved 75 public $url = null; // optional - URL where HTML was retrieved
76 public $debug = false; 76 public $debug = false;
77 public $lightClean = true; // preserves more content (experimental) added 2012-09-19 77 public $lightClean = true; // preserves more content (experimental) added 2012-09-19
78 protected $body = null; // 78 protected $body = null; //
79 protected $bodyCache = null; // Cache the body HTML in case we need to re-use it later 79 protected $bodyCache = null; // Cache the body HTML in case we need to re-use it later
80 protected $flags = 7; // 1 | 2 | 4; // Start with all flags set. 80 protected $flags = 7; // 1 | 2 | 4; // Start with all flags set.
81 protected $success = false; // indicates whether we were able to extract or not 81 protected $success = false; // indicates whether we were able to extract or not
82 82
83 /** 83 /**
84 * All of the regular expressions in use within readability. 84 * All of the regular expressions in use within readability.
85 * Defined up here so we don't instantiate them repeatedly in loops. 85 * Defined up here so we don't instantiate them repeatedly in loops.
@@ -97,19 +97,19 @@ class Readability
97 'killBreaks' => '/(<br\s*\/?>(\s|&nbsp;?)*){1,}/', 97 'killBreaks' => '/(<br\s*\/?>(\s|&nbsp;?)*){1,}/',
98 'video' => '!//(player\.|www\.)?(youtube|vimeo|viddler)\.com!i', 98 'video' => '!//(player\.|www\.)?(youtube|vimeo|viddler)\.com!i',
99 'skipFootnoteLink' => '/^\s*(\[?[a-z0-9]{1,2}\]?|^|edit|citation needed)\s*$/i' 99 'skipFootnoteLink' => '/^\s*(\[?[a-z0-9]{1,2}\]?|^|edit|citation needed)\s*$/i'
100 ); 100 );
101 101
102 /* constants */ 102 /* constants */
103 const FLAG_STRIP_UNLIKELYS = 1; 103 const FLAG_STRIP_UNLIKELYS = 1;
104 const FLAG_WEIGHT_CLASSES = 2; 104 const FLAG_WEIGHT_CLASSES = 2;
105 const FLAG_CLEAN_CONDITIONALLY = 4; 105 const FLAG_CLEAN_CONDITIONALLY = 4;
106 106
107 /** 107 /**
108 * Create instance of Readability 108 * Create instance of Readability
109 * @param string UTF-8 encoded string 109 * @param string UTF-8 encoded string
110 * @param string (optional) URL associated with HTML (used for footnotes) 110 * @param string (optional) URL associated with HTML (used for footnotes)
111 * @param string which parser to use for turning raw HTML into a DOMDocument (either 'libxml' or 'html5lib') 111 * @param string which parser to use for turning raw HTML into a DOMDocument (either 'libxml' or 'html5lib')
112 */ 112 */
113 function __construct($html, $url=null, $parser='libxml') 113 function __construct($html, $url=null, $parser='libxml')
114 { 114 {
115 $this->url = $url; 115 $this->url = $url;
@@ -135,18 +135,18 @@ class Readability
135 public function getTitle() { 135 public function getTitle() {
136 return $this->articleTitle; 136 return $this->articleTitle;
137 } 137 }
138 138
139 /** 139 /**
140 * Get article content element 140 * Get article content element
141 * @return DOMElement 141 * @return DOMElement
142 */ 142 */
143 public function getContent() { 143 public function getContent() {
144 return $this->articleContent; 144 return $this->articleContent;
145 } 145 }
146 146
147 /** 147 /**
148 * Runs readability. 148 * Runs readability.
149 * 149 *
150 * Workflow: 150 * Workflow:
151 * 1. Prep the document by removing script tags, css, etc. 151 * 1. Prep the document by removing script tags, css, etc.
152 * 2. Build readability's DOM tree. 152 * 2. Build readability's DOM tree.
@@ -161,7 +161,7 @@ class Readability
161 if (!isset($this->dom->documentElement)) return false; 161 if (!isset($this->dom->documentElement)) return false;
162 $this->removeScripts($this->dom); 162 $this->removeScripts($this->dom);
163 //die($this->getInnerHTML($this->dom->documentElement)); 163 //die($this->getInnerHTML($this->dom->documentElement));
164 164
165 // Assume successful outcome 165 // Assume successful outcome
166 $this->success = true; 166 $this->success = true;
167 167
@@ -176,7 +176,7 @@ class Readability
176 } 176 }
177 177
178 $this->prepDocument(); 178 $this->prepDocument();
179 179
180 //die($this->dom->documentElement->parentNode->nodeType); 180 //die($this->dom->documentElement->parentNode->nodeType);
181 //$this->setInnerHTML($this->dom->documentElement, $this->getInnerHTML($this->dom->documentElement)); 181 //$this->setInnerHTML($this->dom->documentElement, $this->getInnerHTML($this->dom->documentElement));
182 //die($this->getInnerHTML($this->dom->documentElement)); 182 //die($this->getInnerHTML($this->dom->documentElement));
@@ -191,9 +191,9 @@ class Readability
191 $this->success = false; 191 $this->success = false;
192 $articleContent = $this->dom->createElement('div'); 192 $articleContent = $this->dom->createElement('div');
193 $articleContent->setAttribute('id', 'readability-content'); 193 $articleContent->setAttribute('id', 'readability-content');
194 $articleContent->innerHTML = '<p>Sorry, Readability was unable to parse this page for content.</p>'; 194 $articleContent->innerHTML = '<p>Sorry, Readability was unable to parse this page for content.</p>';
195 } 195 }
196 196
197 $overlay->setAttribute('id', 'readOverlay'); 197 $overlay->setAttribute('id', 'readOverlay');
198 $innerDiv->setAttribute('id', 'readInner'); 198 $innerDiv->setAttribute('id', 'readInner');
199 199
@@ -201,7 +201,7 @@ class Readability
201 $innerDiv->appendChild($articleTitle); 201 $innerDiv->appendChild($articleTitle);
202 $innerDiv->appendChild($articleContent); 202 $innerDiv->appendChild($articleContent);
203 $overlay->appendChild($innerDiv); 203 $overlay->appendChild($innerDiv);
204 204
205 /* Clear the old HTML, insert the new content. */ 205 /* Clear the old HTML, insert the new content. */
206 $this->body->innerHTML = ''; 206 $this->body->innerHTML = '';
207 $this->body->appendChild($overlay); 207 $this->body->appendChild($overlay);
@@ -209,21 +209,21 @@ class Readability
209 $this->body->removeAttribute('style'); 209 $this->body->removeAttribute('style');
210 210
211 $this->postProcessContent($articleContent); 211 $this->postProcessContent($articleContent);
212 212
213 // Set title and content instance variables 213 // Set title and content instance variables
214 $this->articleTitle = $articleTitle; 214 $this->articleTitle = $articleTitle;
215 $this->articleContent = $articleContent; 215 $this->articleContent = $articleContent;
216 216
217 return $this->success; 217 return $this->success;
218 } 218 }
219 219
220 /** 220 /**
221 * Debug 221 * Debug
222 */ 222 */
223 protected function dbg($msg) { 223 protected function dbg($msg) {
224 if ($this->debug) echo '* ',$msg, "\n"; 224 if ($this->debug) echo '* ',$msg, "\n";
225 } 225 }
226 226
227 /** 227 /**
228 * Run any post-process modifications to article content as necessary. 228 * Run any post-process modifications to article content as necessary.
229 * 229 *
@@ -231,11 +231,11 @@ class Readability
231 * @return void 231 * @return void
232 */ 232 */
233 public function postProcessContent($articleContent) { 233 public function postProcessContent($articleContent) {
234 if ($this->convertLinksToFootnotes && !preg_match('/wikipedia\.org/', @$this->url)) { 234 if ($this->convertLinksToFootnotes && !preg_match('/wikipedia\.org/', @$this->url)) {
235 $this->addFootnotes($articleContent); 235 $this->addFootnotes($articleContent);
236 } 236 }
237 } 237 }
238 238
239 /** 239 /**
240 * Get the article title as an H1. 240 * Get the article title as an H1.
241 * 241 *
@@ -248,11 +248,11 @@ class Readability
248 try { 248 try {
249 $curTitle = $origTitle = $this->getInnerText($this->dom->getElementsByTagName('title')->item(0)); 249 $curTitle = $origTitle = $this->getInnerText($this->dom->getElementsByTagName('title')->item(0));
250 } catch(Exception $e) {} 250 } catch(Exception $e) {}
251 251
252 if (preg_match('/ [\|\-] /', $curTitle)) 252 if (preg_match('/ [\|\-] /', $curTitle))
253 { 253 {
254 $curTitle = preg_replace('/(.*)[\|\-] .*/i', '$1', $origTitle); 254 $curTitle = preg_replace('/(.*)[\|\-] .*/i', '$1', $origTitle);
255 255
256 if (count(explode(' ', $curTitle)) < 3) { 256 if (count(explode(' ', $curTitle)) < 3) {
257 $curTitle = preg_replace('/[^\|\-]*[\|\-](.*)/i', '$1', $origTitle); 257 $curTitle = preg_replace('/[^\|\-]*[\|\-](.*)/i', '$1', $origTitle);
258 } 258 }
@@ -279,17 +279,17 @@ class Readability
279 if (count(explode(' ', $curTitle)) <= 4) { 279 if (count(explode(' ', $curTitle)) <= 4) {
280 $curTitle = $origTitle; 280 $curTitle = $origTitle;
281 } 281 }
282 282
283 $articleTitle = $this->dom->createElement('h1'); 283 $articleTitle = $this->dom->createElement('h1');
284 $articleTitle->innerHTML = $curTitle; 284 $articleTitle->innerHTML = $curTitle;
285 285
286 return $articleTitle; 286 return $articleTitle;
287 } 287 }
288 288
289 /** 289 /**
290 * Prepare the HTML document for readability to scrape it. 290 * Prepare the HTML document for readability to scrape it.
291 * This includes things like stripping javascript, CSS, and handling terrible markup. 291 * This includes things like stripping javascript, CSS, and handling terrible markup.
292 * 292 *
293 * @return void 293 * @return void
294 **/ 294 **/
295 protected function prepDocument() { 295 protected function prepDocument() {
@@ -328,13 +328,13 @@ class Readability
328 $footnotesWrapper = $this->dom->createElement('div'); 328 $footnotesWrapper = $this->dom->createElement('div');
329 $footnotesWrapper->setAttribute('id', 'readability-footnotes'); 329 $footnotesWrapper->setAttribute('id', 'readability-footnotes');
330 $footnotesWrapper->innerHTML = '<h3>References</h3>'; 330 $footnotesWrapper->innerHTML = '<h3>References</h3>';
331 331
332 $articleFootnotes = $this->dom->createElement('ol'); 332 $articleFootnotes = $this->dom->createElement('ol');
333 $articleFootnotes->setAttribute('id', 'readability-footnotes-list'); 333 $articleFootnotes->setAttribute('id', 'readability-footnotes-list');
334 $footnotesWrapper->appendChild($articleFootnotes); 334 $footnotesWrapper->appendChild($articleFootnotes);
335 335
336 $articleLinks = $articleContent->getElementsByTagName('a'); 336 $articleLinks = $articleContent->getElementsByTagName('a');
337 337
338 $linkCount = 0; 338 $linkCount = 0;
339 for ($i = 0; $i < $articleLinks->length; $i++) 339 for ($i = 0; $i < $articleLinks->length; $i++)
340 { 340 {
@@ -346,11 +346,11 @@ class Readability
346 if (!$linkDomain && isset($this->url)) $linkDomain = @parse_url($this->url, PHP_URL_HOST); 346 if (!$linkDomain && isset($this->url)) $linkDomain = @parse_url($this->url, PHP_URL_HOST);
347 //linkDomain = footnoteLink.host ? footnoteLink.host : document.location.host, 347 //linkDomain = footnoteLink.host ? footnoteLink.host : document.location.host,
348 $linkText = $this->getInnerText($articleLink); 348 $linkText = $this->getInnerText($articleLink);
349 349
350 if ((strpos($articleLink->getAttribute('class'), 'readability-DoNotFootnote') !== false) || preg_match($this->regexps['skipFootnoteLink'], $linkText)) { 350 if ((strpos($articleLink->getAttribute('class'), 'readability-DoNotFootnote') !== false) || preg_match($this->regexps['skipFootnoteLink'], $linkText)) {
351 continue; 351 continue;
352 } 352 }
353 353
354 $linkCount++; 354 $linkCount++;
355 355
356 /** Add a superscript reference after the article link */ 356 /** Add a superscript reference after the article link */
@@ -358,7 +358,7 @@ class Readability
358 $refLink->innerHTML = '<small><sup>[' . $linkCount . ']</sup></small>'; 358 $refLink->innerHTML = '<small><sup>[' . $linkCount . ']</sup></small>';
359 $refLink->setAttribute('class', 'readability-DoNotFootnote'); 359 $refLink->setAttribute('class', 'readability-DoNotFootnote');
360 $refLink->setAttribute('style', 'color: inherit;'); 360 $refLink->setAttribute('style', 'color: inherit;');
361 361
362 //TODO: does this work or should we use DOMNode.isSameNode()? 362 //TODO: does this work or should we use DOMNode.isSameNode()?
363 if ($articleLink->parentNode->lastChild == $articleLink) { 363 if ($articleLink->parentNode->lastChild == $articleLink) {
364 $articleLink->parentNode->appendChild($refLink); 364 $articleLink->parentNode->appendChild($refLink);
@@ -373,15 +373,15 @@ class Readability
373 373
374 $footnoteLink->innerHTML = ($footnoteLink->getAttribute('title') != '' ? $footnoteLink->getAttribute('title') : $linkText); 374 $footnoteLink->innerHTML = ($footnoteLink->getAttribute('title') != '' ? $footnoteLink->getAttribute('title') : $linkText);
375 $footnoteLink->setAttribute('name', 'readabilityFootnoteLink-' . $linkCount); 375 $footnoteLink->setAttribute('name', 'readabilityFootnoteLink-' . $linkCount);
376 376
377 $footnote->appendChild($footnoteLink); 377 $footnote->appendChild($footnoteLink);
378 if ($linkDomain) $footnote->innerHTML = $footnote->innerHTML . '<small> (' . $linkDomain . ')</small>'; 378 if ($linkDomain) $footnote->innerHTML = $footnote->innerHTML . '<small> (' . $linkDomain . ')</small>';
379 379
380 $articleFootnotes->appendChild($footnote); 380 $articleFootnotes->appendChild($footnote);
381 } 381 }
382 382
383 if ($linkCount > 0) { 383 if ($linkCount > 0) {
384 $articleContent->appendChild($footnotesWrapper); 384 $articleContent->appendChild($footnotesWrapper);
385 } 385 }
386 } 386 }
387 387
@@ -404,7 +404,7 @@ class Readability
404 //} 404 //}
405 } 405 }
406 } 406 }
407 407
408 /** 408 /**
409 * Prepare the article node for display. Clean out any inline styles, 409 * Prepare the article node for display. Clean out any inline styles,
410 * iframes, forms, strip extraneous <p> tags, etc. 410 * iframes, forms, strip extraneous <p> tags, etc.
@@ -429,7 +429,7 @@ class Readability
429 * as a header and not a subheader, so remove it since we already have a header. 429 * as a header and not a subheader, so remove it since we already have a header.
430 ***/ 430 ***/
431 if (!$this->lightClean && ($articleContent->getElementsByTagName('h2')->length == 1)) { 431 if (!$this->lightClean && ($articleContent->getElementsByTagName('h2')->length == 1)) {
432 $this->clean($articleContent, 'h2'); 432 $this->clean($articleContent, 'h2');
433 } 433 }
434 $this->clean($articleContent, 'iframe'); 434 $this->clean($articleContent, 'iframe');
435 435
@@ -448,7 +448,7 @@ class Readability
448 $embedCount = $articleParagraphs->item($i)->getElementsByTagName('embed')->length; 448 $embedCount = $articleParagraphs->item($i)->getElementsByTagName('embed')->length;
449 $objectCount = $articleParagraphs->item($i)->getElementsByTagName('object')->length; 449 $objectCount = $articleParagraphs->item($i)->getElementsByTagName('object')->length;
450 $iframeCount = $articleParagraphs->item($i)->getElementsByTagName('iframe')->length; 450 $iframeCount = $articleParagraphs->item($i)->getElementsByTagName('iframe')->length;
451 451
452 if ($imgCount === 0 && $embedCount === 0 && $objectCount === 0 && $iframeCount === 0 && $this->getInnerText($articleParagraphs->item($i), false) == '') 452 if ($imgCount === 0 && $embedCount === 0 && $objectCount === 0 && $iframeCount === 0 && $this->getInnerText($articleParagraphs->item($i), false) == '')
453 { 453 {
454 $articleParagraphs->item($i)->parentNode->removeChild($articleParagraphs->item($i)); 454 $articleParagraphs->item($i)->parentNode->removeChild($articleParagraphs->item($i));
@@ -457,13 +457,13 @@ class Readability
457 457
458 try { 458 try {
459 $articleContent->innerHTML = preg_replace('/<br[^>]*>\s*<p/i', '<p', $articleContent->innerHTML); 459 $articleContent->innerHTML = preg_replace('/<br[^>]*>\s*<p/i', '<p', $articleContent->innerHTML);
460 //articleContent.innerHTML = articleContent.innerHTML.replace(/<br[^>]*>\s*<p/gi, '<p'); 460 //articleContent.innerHTML = articleContent.innerHTML.replace(/<br[^>]*>\s*<p/gi, '<p');
461 } 461 }
462 catch (Exception $e) { 462 catch (Exception $e) {
463 $this->dbg("Cleaning innerHTML of breaks failed. This is an IE strict-block-elements bug. Ignoring.: " . $e); 463 $this->dbg("Cleaning innerHTML of breaks failed. This is an IE strict-block-elements bug. Ignoring.: " . $e);
464 } 464 }
465 } 465 }
466 466
467 /** 467 /**
468 * Initialize a node with the readability object. Also checks the 468 * Initialize a node with the readability object. Also checks the
469 * className/id for special names to add to its score. 469 * className/id for special names to add to its score.
@@ -474,7 +474,7 @@ class Readability
474 protected function initializeNode($node) { 474 protected function initializeNode($node) {
475 $readability = $this->dom->createAttribute('readability'); 475 $readability = $this->dom->createAttribute('readability');
476 $readability->value = 0; // this is our contentScore 476 $readability->value = 0; // this is our contentScore
477 $node->setAttributeNode($readability); 477 $node->setAttributeNode($readability);
478 478
479 switch (strtoupper($node->tagName)) { // unsure if strtoupper is needed, but using it just in case 479 switch (strtoupper($node->tagName)) { // unsure if strtoupper is needed, but using it just in case
480 case 'DIV': 480 case 'DIV':
@@ -486,7 +486,7 @@ class Readability
486 case 'BLOCKQUOTE': 486 case 'BLOCKQUOTE':
487 $readability->value += 3; 487 $readability->value += 3;
488 break; 488 break;
489 489
490 case 'ADDRESS': 490 case 'ADDRESS':
491 case 'OL': 491 case 'OL':
492 case 'UL': 492 case 'UL':
@@ -510,7 +510,7 @@ class Readability
510 } 510 }
511 $readability->value += $this->getClassWeight($node); 511 $readability->value += $this->getClassWeight($node);
512 } 512 }
513 513
514 /*** 514 /***
515 * grabArticle - Using a variety of metrics (content score, classname, element types), find the content that is 515 * grabArticle - Using a variety of metrics (content score, classname, element types), find the content that is
516 * most likely to be the stuff a user wants to read. Then return it wrapped up in a div. 516 * 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
548 $node->parentNode->removeChild($node); 548 $node->parentNode->removeChild($node);
549 $nodeIndex--; 549 $nodeIndex--;
550 continue; 550 continue;
551 } 551 }
552 } 552 }
553 553
554 if ($tagName == 'P' || $tagName == 'TD' || $tagName == 'PRE') { 554 if ($tagName == 'P' || $tagName == 'TD' || $tagName == 'PRE') {
@@ -589,7 +589,7 @@ class Readability
589 } 589 }
590 } 590 }
591 } 591 }
592 592
593 /** 593 /**
594 * Loop through all paragraphs, and assign a score to them based on how content-y they look. 594 * Loop through all paragraphs, and assign a score to them based on how content-y they look.
595 * Then add their score to their parent node. 595 * Then add their score to their parent node.
@@ -613,7 +613,7 @@ class Readability
613 } 613 }
614 614
615 /* Initialize readability data for the parent. */ 615 /* Initialize readability data for the parent. */
616 if (!$parentNode->hasAttribute('readability')) 616 if (!$parentNode->hasAttribute('readability'))
617 { 617 {
618 $this->initializeNode($parentNode); 618 $this->initializeNode($parentNode);
619 $candidates[] = $parentNode; 619 $candidates[] = $parentNode;
@@ -633,15 +633,15 @@ class Readability
633 633
634 /* Add points for any commas within this paragraph */ 634 /* Add points for any commas within this paragraph */
635 $contentScore += count(explode(',', $innerText)); 635 $contentScore += count(explode(',', $innerText));
636 636
637 /* For every 100 characters in this paragraph, add another point. Up to 3 points. */ 637 /* For every 100 characters in this paragraph, add another point. Up to 3 points. */
638 $contentScore += min(floor(strlen($innerText) / 100), 3); 638 $contentScore += min(floor(strlen($innerText) / 100), 3);
639 639
640 /* Add the score to the parent. The grandparent gets half. */ 640 /* Add the score to the parent. The grandparent gets half. */
641 $parentNode->getAttributeNode('readability')->value += $contentScore; 641 $parentNode->getAttributeNode('readability')->value += $contentScore;
642 642
643 if ($grandParentNode) { 643 if ($grandParentNode) {
644 $grandParentNode->getAttributeNode('readability')->value += $contentScore/2; 644 $grandParentNode->getAttributeNode('readability')->value += $contentScore/2;
645 } 645 }
646 } 646 }
647 647
@@ -727,12 +727,12 @@ class Readability
727 { 727 {
728 $append = true; 728 $append = true;
729 } 729 }
730 730
731 if (strtoupper($siblingNode->nodeName) == 'P') { 731 if (strtoupper($siblingNode->nodeName) == 'P') {
732 $linkDensity = $this->getLinkDensity($siblingNode); 732 $linkDensity = $this->getLinkDensity($siblingNode);
733 $nodeContent = $this->getInnerText($siblingNode); 733 $nodeContent = $this->getInnerText($siblingNode);
734 $nodeLength = strlen($nodeContent); 734 $nodeLength = strlen($nodeContent);
735 735
736 if ($nodeLength > 80 && $linkDensity < 0.25) 736 if ($nodeLength > 80 && $linkDensity < 0.25)
737 { 737 {
738 $append = true; 738 $append = true;
@@ -751,7 +751,7 @@ class Readability
751 $sibNodeName = strtoupper($siblingNode->nodeName); 751 $sibNodeName = strtoupper($siblingNode->nodeName);
752 if ($sibNodeName != 'DIV' && $sibNodeName != 'P') { 752 if ($sibNodeName != 'DIV' && $sibNodeName != 'P') {
753 /* 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. */ 753 /* 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. */
754 754
755 $this->dbg('Altering siblingNode of ' . $sibNodeName . ' to div.'); 755 $this->dbg('Altering siblingNode of ' . $sibNodeName . ' to div.');
756 $nodeToAppend = $this->dom->createElement('div'); 756 $nodeToAppend = $this->dom->createElement('div');
757 try { 757 try {
@@ -770,7 +770,7 @@ class Readability
770 $s--; 770 $s--;
771 $sl--; 771 $sl--;
772 } 772 }
773 773
774 /* To ensure a node does not interfere with readability styles, remove its classnames */ 774 /* To ensure a node does not interfere with readability styles, remove its classnames */
775 $nodeToAppend->removeAttribute('class'); 775 $nodeToAppend->removeAttribute('class');
776 776
@@ -796,14 +796,14 @@ class Readability
796 // in the meantime, we check and create an empty element if it's not there. 796 // in the meantime, we check and create an empty element if it's not there.
797 if (!isset($this->body->childNodes)) $this->body = $this->dom->createElement('body'); 797 if (!isset($this->body->childNodes)) $this->body = $this->dom->createElement('body');
798 $this->body->innerHTML = $this->bodyCache; 798 $this->body->innerHTML = $this->bodyCache;
799 799
800 if ($this->flagIsActive(self::FLAG_STRIP_UNLIKELYS)) { 800 if ($this->flagIsActive(self::FLAG_STRIP_UNLIKELYS)) {
801 $this->removeFlag(self::FLAG_STRIP_UNLIKELYS); 801 $this->removeFlag(self::FLAG_STRIP_UNLIKELYS);
802 return $this->grabArticle($this->body); 802 return $this->grabArticle($this->body);
803 } 803 }
804 else if ($this->flagIsActive(self::FLAG_WEIGHT_CLASSES)) { 804 else if ($this->flagIsActive(self::FLAG_WEIGHT_CLASSES)) {
805 $this->removeFlag(self::FLAG_WEIGHT_CLASSES); 805 $this->removeFlag(self::FLAG_WEIGHT_CLASSES);
806 return $this->grabArticle($this->body); 806 return $this->grabArticle($this->body);
807 } 807 }
808 else if ($this->flagIsActive(self::FLAG_CLEAN_CONDITIONALLY)) { 808 else if ($this->flagIsActive(self::FLAG_CLEAN_CONDITIONALLY)) {
809 $this->removeFlag(self::FLAG_CLEAN_CONDITIONALLY); 809 $this->removeFlag(self::FLAG_CLEAN_CONDITIONALLY);
@@ -815,7 +815,7 @@ class Readability
815 } 815 }
816 return $articleContent; 816 return $articleContent;
817 } 817 }
818 818
819 /** 819 /**
820 * Remove script tags from document 820 * Remove script tags from document
821 * 821 *
@@ -829,7 +829,7 @@ class Readability
829 $scripts->item($i)->parentNode->removeChild($scripts->item($i)); 829 $scripts->item($i)->parentNode->removeChild($scripts->item($i));
830 } 830 }
831 } 831 }
832 832
833 /** 833 /**
834 * Get the inner text of a node. 834 * Get the inner text of a node.
835 * This also strips out any excess whitespace to be found. 835 * This also strips out any excess whitespace to be found.
@@ -878,11 +878,11 @@ class Readability
878 $elem->removeAttribute('style'); 878 $elem->removeAttribute('style');
879 } 879 }
880 } 880 }
881 881
882 /** 882 /**
883 * Get the density of links as a percentage of the content 883 * Get the density of links as a percentage of the content
884 * This is the amount of text that is inside a link divided by the total text in the node. 884 * This is the amount of text that is inside a link divided by the total text in the node.
885 * 885 *
886 * @param DOMElement $e 886 * @param DOMElement $e
887 * @return number (float) 887 * @return number (float)
888 */ 888 */
@@ -900,9 +900,9 @@ class Readability
900 return 0; 900 return 0;
901 } 901 }
902 } 902 }
903 903
904 /** 904 /**
905 * Get an elements class/id weight. Uses regular expressions to tell if this 905 * Get an elements class/id weight. Uses regular expressions to tell if this
906 * element looks good or bad. 906 * element looks good or bad.
907 * 907 *
908 * @param DOMElement $e 908 * @param DOMElement $e
@@ -964,7 +964,7 @@ class Readability
964 public function clean($e, $tag) { 964 public function clean($e, $tag) {
965 $targetList = $e->getElementsByTagName($tag); 965 $targetList = $e->getElementsByTagName($tag);
966 $isEmbed = ($tag == 'iframe' || $tag == 'object' || $tag == 'embed'); 966 $isEmbed = ($tag == 'iframe' || $tag == 'object' || $tag == 'embed');
967 967
968 for ($y=$targetList->length-1; $y >= 0; $y--) { 968 for ($y=$targetList->length-1; $y >= 0; $y--) {
969 /* Allow youtube and vimeo videos through as people usually want to see those. */ 969 /* Allow youtube and vimeo videos through as people usually want to see those. */
970 if ($isEmbed) { 970 if ($isEmbed) {
@@ -972,7 +972,7 @@ class Readability
972 for ($i=0, $il=$targetList->item($y)->attributes->length; $i < $il; $i++) { 972 for ($i=0, $il=$targetList->item($y)->attributes->length; $i < $il; $i++) {
973 $attributeValues .= $targetList->item($y)->attributes->item($i)->value . '|'; // DOMAttr? (TODO: test) 973 $attributeValues .= $targetList->item($y)->attributes->item($i)->value . '|'; // DOMAttr? (TODO: test)
974 } 974 }
975 975
976 /* First, check the elements attributes to see if any of them contain youtube or vimeo */ 976 /* First, check the elements attributes to see if any of them contain youtube or vimeo */
977 if (preg_match($this->regexps['video'], $attributeValues)) { 977 if (preg_match($this->regexps['video'], $attributeValues)) {
978 continue; 978 continue;
@@ -986,10 +986,10 @@ class Readability
986 $targetList->item($y)->parentNode->removeChild($targetList->item($y)); 986 $targetList->item($y)->parentNode->removeChild($targetList->item($y));
987 } 987 }
988 } 988 }
989 989
990 /** 990 /**
991 * Clean an element of all tags of type "tag" if they look fishy. 991 * Clean an element of all tags of type "tag" if they look fishy.
992 * "Fishy" is an algorithm based on content length, classnames, 992 * "Fishy" is an algorithm based on content length, classnames,
993 * link density, number of images & embeds, etc. 993 * link density, number of images & embeds, etc.
994 * 994 *
995 * @param DOMElement $e 995 * @param DOMElement $e
@@ -1013,7 +1013,7 @@ class Readability
1013 for ($i=$curTagsLength-1; $i >= 0; $i--) { 1013 for ($i=$curTagsLength-1; $i >= 0; $i--) {
1014 $weight = $this->getClassWeight($tagsList->item($i)); 1014 $weight = $this->getClassWeight($tagsList->item($i));
1015 $contentScore = ($tagsList->item($i)->hasAttribute('readability')) ? (int)$tagsList->item($i)->getAttribute('readability') : 0; 1015 $contentScore = ($tagsList->item($i)->hasAttribute('readability')) ? (int)$tagsList->item($i)->getAttribute('readability') : 0;
1016 1016
1017 $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')) : '')); 1017 $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')) : ''));
1018 1018
1019 if ($weight + $contentScore < 0) { 1019 if ($weight + $contentScore < 0) {
@@ -1034,13 +1034,13 @@ class Readability
1034 $embeds = $tagsList->item($i)->getElementsByTagName('embed'); 1034 $embeds = $tagsList->item($i)->getElementsByTagName('embed');
1035 for ($ei=0, $il=$embeds->length; $ei < $il; $ei++) { 1035 for ($ei=0, $il=$embeds->length; $ei < $il; $ei++) {
1036 if (preg_match($this->regexps['video'], $embeds->item($ei)->getAttribute('src'))) { 1036 if (preg_match($this->regexps['video'], $embeds->item($ei)->getAttribute('src'))) {
1037 $embedCount++; 1037 $embedCount++;
1038 } 1038 }
1039 } 1039 }
1040 $embeds = $tagsList->item($i)->getElementsByTagName('iframe'); 1040 $embeds = $tagsList->item($i)->getElementsByTagName('iframe');
1041 for ($ei=0, $il=$embeds->length; $ei < $il; $ei++) { 1041 for ($ei=0, $il=$embeds->length; $ei < $il; $ei++) {
1042 if (preg_match($this->regexps['video'], $embeds->item($ei)->getAttribute('src'))) { 1042 if (preg_match($this->regexps['video'], $embeds->item($ei)->getAttribute('src'))) {
1043 $embedCount++; 1043 $embedCount++;
1044 } 1044 }
1045 } 1045 }
1046 1046
@@ -1058,7 +1058,7 @@ class Readability
1058 $toRemove = true; 1058 $toRemove = true;
1059 } else if ( $input > floor($p/3) ) { 1059 } else if ( $input > floor($p/3) ) {
1060 $this->dbg(' too many <input> elements'); 1060 $this->dbg(' too many <input> elements');
1061 $toRemove = true; 1061 $toRemove = true;
1062 } else if ($contentLength < 25 && ($embedCount === 0 && ($img === 0 || $img > 2))) { 1062 } else if ($contentLength < 25 && ($embedCount === 0 && ($img === 0 || $img > 2))) {
1063 $this->dbg(' content length less than 25 chars, 0 embeds and either 0 images or more than 2 images'); 1063 $this->dbg(' content length less than 25 chars, 0 embeds and either 0 images or more than 2 images');
1064 $toRemove = true; 1064 $toRemove = true;
@@ -1082,7 +1082,7 @@ class Readability
1082 $toRemove = true; 1082 $toRemove = true;
1083 } else if ( $input > floor($p/3) ) { 1083 } else if ( $input > floor($p/3) ) {
1084 $this->dbg(' too many <input> elements'); 1084 $this->dbg(' too many <input> elements');
1085 $toRemove = true; 1085 $toRemove = true;
1086 } else if ($contentLength < 25 && ($img === 0 || $img > 2) ) { 1086 } else if ($contentLength < 25 && ($img === 0 || $img > 2) ) {
1087 $this->dbg(' content length less than 25 chars and 0 images, or more than 2 images'); 1087 $this->dbg(' content length less than 25 chars and 0 images, or more than 2 images');
1088 $toRemove = true; 1088 $toRemove = true;
@@ -1126,11 +1126,11 @@ class Readability
1126 public function flagIsActive($flag) { 1126 public function flagIsActive($flag) {
1127 return ($this->flags & $flag) > 0; 1127 return ($this->flags & $flag) > 0;
1128 } 1128 }
1129 1129
1130 public function addFlag($flag) { 1130 public function addFlag($flag) {
1131 $this->flags = $this->flags | $flag; 1131 $this->flags = $this->flags | $flag;
1132 } 1132 }
1133 1133
1134 public function removeFlag($flag) { 1134 public function removeFlag($flag) {
1135 $this->flags = $this->flags & ~$flag; 1135 $this->flags = $this->flags & ~$flag;
1136 } 1136 }
diff --git a/inc/Session.class.php b/inc/3rdparty/Session.class.php
index eff924cc..3162f507 100644
--- a/inc/Session.class.php
+++ b/inc/3rdparty/Session.class.php
@@ -93,7 +93,7 @@ class Session
93 // Force logout 93 // Force logout
94 public static function logout() 94 public static function logout()
95 { 95 {
96 unset($_SESSION['uid'],$_SESSION['info'],$_SESSION['expires_on'],$_SESSION['tokens'], $_SESSION['login'], $_SESSION['pass']); 96 unset($_SESSION['uid'],$_SESSION['info'],$_SESSION['expires_on'],$_SESSION['tokens'], $_SESSION['login'], $_SESSION['pass'], $_SESSION['poche_user']);
97 } 97 }
98 98
99 // Make sure user is logged in. 99 // Make sure user is logged in.
diff --git a/inc/class.messages.php b/inc/3rdparty/class.messages.php
index 6d515bf6..e60bd3a1 100644..100755
--- a/inc/class.messages.php
+++ b/inc/3rdparty/class.messages.php
@@ -1,231 +1,231 @@
1<?php 1<?php
2//-------------------------------------------------------------------------------------------------- 2//--------------------------------------------------------------------------------------------------
3// Session-Based Flash Messages v1.0 3// Session-Based Flash Messages v1.0
4// Copyright 2012 Mike Everhart (http://mikeeverhart.net) 4// Copyright 2012 Mike Everhart (http://mikeeverhart.net)
5// 5//
6// Licensed under the Apache License, Version 2.0 (the "License"); 6// Licensed under the Apache License, Version 2.0 (the "License");
7// you may not use this file except in compliance with the License. 7// you may not use this file except in compliance with the License.
8// You may obtain a copy of the License at 8// You may obtain a copy of the License at
9// 9//
10// http://www.apache.org/licenses/LICENSE-2.0 10// http://www.apache.org/licenses/LICENSE-2.0
11// 11//
12// Unless required by applicable law or agreed to in writing, software 12// Unless required by applicable law or agreed to in writing, software
13// distributed under the License is distributed on an "AS IS" BASIS, 13// distributed under the License is distributed on an "AS IS" BASIS,
14// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15// See the License for the specific language governing permissions and 15// See the License for the specific language governing permissions and
16// limitations under the License. 16// limitations under the License.
17// 17//
18//------------------------------------------------------------------------------ 18//------------------------------------------------------------------------------
19// Description: 19// Description:
20//------------------------------------------------------------------------------ 20//------------------------------------------------------------------------------
21// 21//
22// Stores messages in Session data to be easily retrieved later on. 22// Stores messages in Session data to be easily retrieved later on.
23// This class includes four different types of messages: 23// This class includes four different types of messages:
24// - Success 24// - Success
25// - Error 25// - Error
26// - Warning 26// - Warning
27// - Information 27// - Information
28// 28//
29// See README for basic usage instructions, or see samples/index.php for more advanced samples 29// See README for basic usage instructions, or see samples/index.php for more advanced samples
30// 30//
31//-------------------------------------------------------------------------------------------------- 31//--------------------------------------------------------------------------------------------------
32// Changelog 32// Changelog
33//-------------------------------------------------------------------------------------------------- 33//--------------------------------------------------------------------------------------------------
34// 34//
35// 2011-05-15 - v1.0 - Initial Version 35// 2011-05-15 - v1.0 - Initial Version
36// 36//
37//-------------------------------------------------------------------------------------------------- 37//--------------------------------------------------------------------------------------------------
38 38
39class Messages { 39class Messages {
40 40
41 //----------------------------------------------------------------------------------------------- 41 //-----------------------------------------------------------------------------------------------
42 // Class Variables 42 // Class Variables
43 //----------------------------------------------------------------------------------------------- 43 //-----------------------------------------------------------------------------------------------
44 var $msgId; 44 var $msgId;
45 var $msgTypes = array( 'help', 'info', 'warning', 'success', 'error' ); 45 var $msgTypes = array( 'help', 'info', 'warning', 'success', 'error' );
46 var $msgClass = 'messages'; 46 var $msgClass = 'messages';
47 var $msgWrapper = "<div class='%s %s'><a href='#' class='closeMessage'></a>\n%s</div>\n"; 47 var $msgWrapper = "<div class='%s %s'><a href='#' class='closeMessage'>X</a>\n%s</div>\n";
48 var $msgBefore = '<p>'; 48 var $msgBefore = '<p>';
49 var $msgAfter = "</p>\n"; 49 var $msgAfter = "</p>\n";
50 50
51 51
52 /** 52 /**
53 * Constructor 53 * Constructor
54 * @author Mike Everhart 54 * @author Mike Everhart
55 */ 55 */
56 public function __construct() { 56 public function __construct() {
57 57
58 // Generate a unique ID for this user and session 58 // Generate a unique ID for this user and session
59 $this->msgId = md5(uniqid()); 59 $this->msgId = md5(uniqid());
60 60
61 // Create the session array if it doesnt already exist 61 // Create the session array if it doesnt already exist
62 if( !array_key_exists('flash_messages', $_SESSION) ) $_SESSION['flash_messages'] = array(); 62 if( !array_key_exists('flash_messages', $_SESSION) ) $_SESSION['flash_messages'] = array();
63 63
64 } 64 }
65 65
66 /** 66 /**
67 * Add a message to the queue 67 * Add a message to the queue
68 * 68 *
69 * @author Mike Everhart 69 * @author Mike Everhart
70 * 70 *
71 * @param string $type The type of message to add 71 * @param string $type The type of message to add
72 * @param string $message The message 72 * @param string $message The message
73 * @param string $redirect_to (optional) If set, the user will be redirected to this URL 73 * @param string $redirect_to (optional) If set, the user will be redirected to this URL
74 * @return bool 74 * @return bool
75 * 75 *
76 */ 76 */
77 public function add($type, $message, $redirect_to=null) { 77 public function add($type, $message, $redirect_to=null) {
78 78
79 if( !isset($_SESSION['flash_messages']) ) return false; 79 if( !isset($_SESSION['flash_messages']) ) return false;
80 80
81 if( !isset($type) || !isset($message[0]) ) return false; 81 if( !isset($type) || !isset($message[0]) ) return false;
82 82
83 // Replace any shorthand codes with their full version 83 // Replace any shorthand codes with their full version
84 if( strlen(trim($type)) == 1 ) { 84 if( strlen(trim($type)) == 1 ) {
85 $type = str_replace( array('h', 'i', 'w', 'e', 's'), array('help', 'info', 'warning', 'error', 'success'), $type ); 85 $type = str_replace( array('h', 'i', 'w', 'e', 's'), array('help', 'info', 'warning', 'error', 'success'), $type );
86 86
87 // Backwards compatibility... 87 // Backwards compatibility...
88 } elseif( $type == 'information' ) { 88 } elseif( $type == 'information' ) {
89 $type = 'info'; 89 $type = 'info';
90 } 90 }
91 91
92 // Make sure it's a valid message type 92 // Make sure it's a valid message type
93 if( !in_array($type, $this->msgTypes) ) die('"' . strip_tags($type) . '" is not a valid message type!' ); 93 if( !in_array($type, $this->msgTypes) ) die('"' . strip_tags($type) . '" is not a valid message type!' );
94 94
95 // If the session array doesn't exist, create it 95 // If the session array doesn't exist, create it
96 if( !array_key_exists( $type, $_SESSION['flash_messages'] ) ) $_SESSION['flash_messages'][$type] = array(); 96 if( !array_key_exists( $type, $_SESSION['flash_messages'] ) ) $_SESSION['flash_messages'][$type] = array();
97 97
98 $_SESSION['flash_messages'][$type][] = $message; 98 $_SESSION['flash_messages'][$type][] = $message;
99 99
100 if( !is_null($redirect_to) ) { 100 if( !is_null($redirect_to) ) {
101 header("Location: $redirect_to"); 101 header("Location: $redirect_to");
102 exit(); 102 exit();
103 } 103 }
104 104
105 return true; 105 return true;
106 106
107 } 107 }
108 108
109 //----------------------------------------------------------------------------------------------- 109 //-----------------------------------------------------------------------------------------------
110 // display() 110 // display()
111 // print queued messages to the screen 111 // print queued messages to the screen
112 //----------------------------------------------------------------------------------------------- 112 //-----------------------------------------------------------------------------------------------
113 /** 113 /**
114 * Display the queued messages 114 * Display the queued messages
115 * 115 *
116 * @author Mike Everhart 116 * @author Mike Everhart
117 * 117 *
118 * @param string $type Which messages to display 118 * @param string $type Which messages to display
119 * @param bool $print True = print the messages on the screen 119 * @param bool $print True = print the messages on the screen
120 * @return mixed 120 * @return mixed
121 * 121 *
122 */ 122 */
123 public function display($type='all', $print=true) { 123 public function display($type='all', $print=true) {
124 $messages = ''; 124 $messages = '';
125 $data = ''; 125 $data = '';
126 126
127 if( !isset($_SESSION['flash_messages']) ) return false; 127 if( !isset($_SESSION['flash_messages']) ) return false;
128 128
129 if( $type == 'g' || $type == 'growl' ) { 129 if( $type == 'g' || $type == 'growl' ) {
130 $this->displayGrowlMessages(); 130 $this->displayGrowlMessages();
131 return true; 131 return true;
132 } 132 }
133 133
134 // Print a certain type of message? 134 // Print a certain type of message?
135 if( in_array($type, $this->msgTypes) ) { 135 if( in_array($type, $this->msgTypes) ) {
136 foreach( $_SESSION['flash_messages'][$type] as $msg ) { 136 foreach( $_SESSION['flash_messages'][$type] as $msg ) {
137 $messages .= $this->msgBefore . $msg . $this->msgAfter; 137 $messages .= $this->msgBefore . $msg . $this->msgAfter;
138 } 138 }
139 139
140 $data .= sprintf($this->msgWrapper, $this->msgClass, $type, $messages); 140 $data .= sprintf($this->msgWrapper, $this->msgClass, $type, $messages);
141 141
142 // Clear the viewed messages 142 // Clear the viewed messages
143 $this->clear($type); 143 $this->clear($type);
144 144
145 // Print ALL queued messages 145 // Print ALL queued messages
146 } elseif( $type == 'all' ) { 146 } elseif( $type == 'all' ) {
147 foreach( $_SESSION['flash_messages'] as $type => $msgArray ) { 147 foreach( $_SESSION['flash_messages'] as $type => $msgArray ) {
148 $messages = ''; 148 $messages = '';
149 foreach( $msgArray as $msg ) { 149 foreach( $msgArray as $msg ) {
150 $messages .= $this->msgBefore . $msg . $this->msgAfter; 150 $messages .= $this->msgBefore . $msg . $this->msgAfter;
151 } 151 }
152 $data .= sprintf($this->msgWrapper, $this->msgClass, $type, $messages); 152 $data .= sprintf($this->msgWrapper, $this->msgClass, $type, $messages);
153 } 153 }
154 154
155 // Clear ALL of the messages 155 // Clear ALL of the messages
156 $this->clear(); 156 $this->clear();
157 157
158 // Invalid Message Type? 158 // Invalid Message Type?
159 } else { 159 } else {
160 return false; 160 return false;
161 } 161 }
162 162
163 // Print everything to the screen or return the data 163 // Print everything to the screen or return the data
164 if( $print ) { 164 if( $print ) {
165 echo $data; 165 echo $data;
166 } else { 166 } else {
167 return $data; 167 return $data;
168 } 168 }
169 } 169 }
170 170
171 171
172 /** 172 /**
173 * Check to see if there are any queued error messages 173 * Check to see if there are any queued error messages
174 * 174 *
175 * @author Mike Everhart 175 * @author Mike Everhart
176 * 176 *
177 * @return bool true = There ARE error messages 177 * @return bool true = There ARE error messages
178 * false = There are NOT any error messages 178 * false = There are NOT any error messages
179 * 179 *
180 */ 180 */
181 public function hasErrors() { 181 public function hasErrors() {
182 return empty($_SESSION['flash_messages']['error']) ? false : true; 182 return empty($_SESSION['flash_messages']['error']) ? false : true;
183 } 183 }
184 184
185 /** 185 /**
186 * Check to see if there are any ($type) messages queued 186 * Check to see if there are any ($type) messages queued
187 * 187 *
188 * @author Mike Everhart 188 * @author Mike Everhart
189 * 189 *
190 * @param string $type The type of messages to check for 190 * @param string $type The type of messages to check for
191 * @return bool 191 * @return bool
192 * 192 *
193 */ 193 */
194 public function hasMessages($type=null) { 194 public function hasMessages($type=null) {
195 if( !is_null($type) ) { 195 if( !is_null($type) ) {
196 if( !empty($_SESSION['flash_messages'][$type]) ) return $_SESSION['flash_messages'][$type]; 196 if( !empty($_SESSION['flash_messages'][$type]) ) return $_SESSION['flash_messages'][$type];
197 } else { 197 } else {
198 foreach( $this->msgTypes as $type ) { 198 foreach( $this->msgTypes as $type ) {
199 if( !empty($_SESSION['flash_messages']) ) return true; 199 if( !empty($_SESSION['flash_messages']) ) return true;
200 } 200 }
201 } 201 }
202 return false; 202 return false;
203 } 203 }
204 204
205 /** 205 /**
206 * Clear messages from the session data 206 * Clear messages from the session data
207 * 207 *
208 * @author Mike Everhart 208 * @author Mike Everhart
209 * 209 *
210 * @param string $type The type of messages to clear 210 * @param string $type The type of messages to clear
211 * @return bool 211 * @return bool
212 * 212 *
213 */ 213 */
214 public function clear($type='all') { 214 public function clear($type='all') {
215 if( $type == 'all' ) { 215 if( $type == 'all' ) {
216 unset($_SESSION['flash_messages']); 216 unset($_SESSION['flash_messages']);
217 } else { 217 } else {
218 unset($_SESSION['flash_messages'][$type]); 218 unset($_SESSION['flash_messages'][$type]);
219 } 219 }
220 return true; 220 return true;
221 } 221 }
222 222
223 public function __toString() { return $this->hasMessages(); } 223 public function __toString() { return $this->hasMessages(); }
224 224
225 public function __destruct() { 225 public function __destruct() {
226 //$this->clear(); 226 //$this->clear();
227 } 227 }
228 228
229 229
230} // end class 230} // end class
231?> \ No newline at end of file 231?> \ No newline at end of file
diff --git a/inc/3rdparty/paginator.php b/inc/3rdparty/paginator.php
new file mode 100644
index 00000000..306756c0
--- /dev/null
+++ b/inc/3rdparty/paginator.php
@@ -0,0 +1,202 @@
1<?php
2/*
3 * PHP Pagination Class
4 *
5 * @author David Carr - dave@daveismyname.com - http://www.daveismyname.com
6 * @version 1.0
7 * @date October 20, 2013
8 */
9class Paginator{
10
11 /**
12 * set the number of items per page.
13 *
14 * @var numeric
15 */
16 private $_perPage;
17
18 /**
19 * set get parameter for fetching the page number
20 *
21 * @var string
22 */
23 private $_instance;
24
25 /**
26 * sets the page number.
27 *
28 * @var numeric
29 */
30 private $_page;
31
32 /**
33 * set the limit for the data source
34 *
35 * @var string
36 */
37 private $_limit;
38
39 /**
40 * set the total number of records/items.
41 *
42 * @var numeric
43 */
44 private $_totalRows = 0;
45
46
47
48 /**
49 * __construct
50 *
51 * pass values when class is istantiated
52 *
53 * @param numeric $_perPage sets the number of iteems per page
54 * @param numeric $_instance sets the instance for the GET parameter
55 */
56 public function __construct($perPage,$instance){
57 $this->_instance = $instance;
58 $this->_perPage = $perPage;
59 $this->set_instance();
60 }
61
62 /**
63 * get_start
64 *
65 * creates the starting point for limiting the dataset
66 * @return numeric
67 */
68 private function get_start(){
69 return ($this->_page * $this->_perPage) - $this->_perPage;
70 }
71
72 /**
73 * set_instance
74 *
75 * sets the instance parameter, if numeric value is 0 then set to 1
76 *
77 * @var numeric
78 */
79 private function set_instance(){
80 $this->_page = (int) (!isset($_GET[$this->_instance]) ? 1 : $_GET[$this->_instance]);
81 $this->_page = ($this->_page == 0 ? 1 : $this->_page);
82 }
83
84 /**
85 * set_total
86 *
87 * collect a numberic value and assigns it to the totalRows
88 *
89 * @var numeric
90 */
91 public function set_total($_totalRows){
92 $this->_totalRows = $_totalRows;
93 }
94
95 /**
96 * get_limit
97 *
98 * returns the limit for the data source, calling the get_start method and passing in the number of items perp page
99 *
100 * @return string
101 */
102 public function get_limit(){
103 if (STORAGE == 'postgres') {
104 return "LIMIT ".$this->_perPage." OFFSET ".$this->get_start();
105 } else {
106 return "LIMIT ".$this->get_start().",".$this->_perPage;
107 }
108 }
109
110 /**
111 * page_links
112 *
113 * create the html links for navigating through the dataset
114 *
115 * @var sting $path optionally set the path for the link
116 * @var sting $ext optionally pass in extra parameters to the GET
117 * @return string returns the html menu
118 */
119 public function page_links($path='?',$ext=null)
120 {
121 $adjacents = "2";
122 $prev = $this->_page - 1;
123 $next = $this->_page + 1;
124 $lastpage = ceil($this->_totalRows/$this->_perPage);
125 $lpm1 = $lastpage - 1;
126
127 $pagination = "";
128 if($lastpage > 1)
129 {
130 $pagination .= "<div class='pagination'>";
131 if ($this->_page > 1)
132 $pagination.= "<a href='".$path."$this->_instance=$prev"."$ext'>« previous</a>";
133 else
134 $pagination.= "<span class='disabled'>« previous</span>";
135
136 if ($lastpage < 7 + ($adjacents * 2))
137 {
138 for ($counter = 1; $counter <= $lastpage; $counter++)
139 {
140 if ($counter == $this->_page)
141 $pagination.= "<span class='current'>$counter</span>";
142 else
143 $pagination.= "<a href='".$path."$this->_instance=$counter"."$ext'>$counter</a>";
144 }
145 }
146 elseif($lastpage > 5 + ($adjacents * 2))
147 {
148 if($this->_page < 1 + ($adjacents * 2))
149 {
150 for ($counter = 1; $counter < 4 + ($adjacents * 2); $counter++)
151 {
152 if ($counter == $this->_page)
153 $pagination.= "<span class='current'>$counter</span>";
154 else
155 $pagination.= "<a href='".$path."$this->_instance=$counter"."$ext'>$counter</a>";
156 }
157 $pagination.= "...";
158 $pagination.= "<a href='".$path."$this->_instance=$lpm1"."$ext'>$lpm1</a>";
159 $pagination.= "<a href='".$path."$this->_instance=$lastpage"."$ext'>$lastpage</a>";
160 }
161 elseif($lastpage - ($adjacents * 2) > $this->_page && $this->_page > ($adjacents * 2))
162 {
163 $pagination.= "<a href='".$path."$this->_instance=1"."$ext'>1</a>";
164 $pagination.= "<a href='".$path."$this->_instance=2"."$ext'>2</a>";
165 $pagination.= "...";
166 for ($counter = $this->_page - $adjacents; $counter <= $this->_page + $adjacents; $counter++)
167 {
168 if ($counter == $this->_page)
169 $pagination.= "<span class='current'>$counter</span>";
170 else
171 $pagination.= "<a href='".$path."$this->_instance=$counter"."$ext'>$counter</a>";
172 }
173 $pagination.= "..";
174 $pagination.= "<a href='".$path."$this->_instance=$lpm1"."$ext'>$lpm1</a>";
175 $pagination.= "<a href='".$path."$this->_instance=$lastpage"."$ext'>$lastpage</a>";
176 }
177 else
178 {
179 $pagination.= "<a href='".$path."$this->_instance=1"."$ext'>1</a>";
180 $pagination.= "<a href='".$path."$this->_instance=2"."$ext'>2</a>";
181 $pagination.= "..";
182 for ($counter = $lastpage - (2 + ($adjacents * 2)); $counter <= $lastpage; $counter++)
183 {
184 if ($counter == $this->_page)
185 $pagination.= "<span class='current'>$counter</span>";
186 else
187 $pagination.= "<a href='".$path."$this->_instance=$counter"."$ext'>$counter</a>";
188 }
189 }
190 }
191
192 if ($this->_page < $counter - 1)
193 $pagination.= "<a href='".$path."$this->_instance=$next"."$ext'>next »</a>";
194 else
195 $pagination.= "<span class='disabled'>next »</span>";
196 $pagination.= "</div>\n";
197 }
198
199
200 return $pagination;
201 }
202}
diff --git a/inc/simple_html_dom.php b/inc/3rdparty/simple_html_dom.php
index 43b94e57..43b94e57 100644
--- a/inc/simple_html_dom.php
+++ b/inc/3rdparty/simple_html_dom.php
diff --git a/inc/MyTool.class.php b/inc/MyTool.class.php
deleted file mode 100644
index 1f5051a4..00000000
--- a/inc/MyTool.class.php
+++ /dev/null
@@ -1,265 +0,0 @@
1<?php
2/**
3 * poche, a read it later open source system
4 *
5 * @category poche
6 * @author Nicolas Lœuillet <support@inthepoche.com>
7 * @copyright 2013
8 * @license http://www.wtfpl.net/ see COPYING file
9 */
10
11class MyTool
12{
13 public static function initPhp()
14 {
15 define('START_TIME', microtime(true));
16
17 if (phpversion() < 5) {
18 die("Argh you don't have PHP 5 !");
19 }
20
21 error_reporting(E_ALL);
22
23 function stripslashesDeep($value) {
24 return is_array($value)
25 ? array_map('stripslashesDeep', $value)
26 : stripslashes($value);
27 }
28
29 if (get_magic_quotes_gpc()) {
30 $_POST = array_map('stripslashesDeep', $_POST);
31 $_GET = array_map('stripslashesDeep', $_GET);
32 $_COOKIE = array_map('stripslashesDeep', $_COOKIE);
33 }
34
35 ob_start();
36 register_shutdown_function('ob_end_flush');
37 }
38
39 public static function isUrl($url)
40 {
41 // http://neo22s.com/check-if-url-exists-and-is-online-php/
42 $pattern='|^http(s)?://[a-z0-9-]+(.[a-z0-9-]+)*(:[0-9]+)?(/.*)?$|i';
43
44 return preg_match($pattern, $url);
45 }
46
47 public static function isEmail($email)
48 {
49 $pattern = "/^[A-Z0-9._%-]+@[A-Z0-9.-]+\.[A-Z]{2, 4}$/i";
50
51 return (preg_match($pattern, $email));
52 }
53
54 public static function formatBBCode($text)
55 {
56 $replace = array(
57 '/\[m\](.+?)\[\/m\]/is'
58 => '/* moderate */',
59 '/\[b\](.+?)\[\/b\]/is'
60 => '<strong>$1</strong>',
61 '/\[i\](.+?)\[\/i\]/is'
62 => '<em>$1</em>',
63 '/\[s\](.+?)\[\/s\]/is'
64 => '<del>$1</del>',
65 '/\[u\](.+?)\[\/u\]/is'
66 => '<span style="text-decoration: underline;">$1</span>',
67 '/\[url\](.+?)\[\/url]/is'
68 => '<a href="$1">$1</a>',
69 '/\[url=(\w+:\/\/[^\]]+)\](.+?)\[\/url]/is'
70 => '<a href="$1">$2</a>',
71 '/\[quote\](.+?)\[\/quote\]/is'
72 => '<blockquote>$1</blockquote>',
73 '/\[code\](.+?)\[\/code\]/is'
74 => '<code>$1</code>',
75 '/\[([^[]+)\|([^[]+)\]/is'
76 => '<a href="$2">$1</a>'
77 );
78 $text = preg_replace(
79 array_keys($replace),
80 array_values($replace),
81 $text
82 );
83
84 return $text;
85 }
86
87 public static function formatText($text)
88 {
89 $text = preg_replace_callback(
90 '/<code_html>(.*?)<\/code_html>/is',
91 create_function(
92 '$matches',
93 'return htmlspecialchars($matches[1]);'
94 ),
95 $text
96 );
97 $text = preg_replace_callback(
98 '/<code_php>(.*?)<\/code_php>/is',
99 create_function(
100 '$matches',
101 'return highlight_string("<?php $matches[1] ?>", true);'
102 ),
103 $text
104 );
105 $text = preg_replace('/<br \/>/is', '', $text);
106
107 $text = preg_replace(
108 '#(^|\s)([a-z]+://([^\s\w/]?[\w/])*)(\s|$)#im',
109 '\\1<a href="\\2">\\2</a>\\4',
110 $text
111 );
112 $text = preg_replace(
113 '#(^|\s)wp:?([a-z]{2}|):([\w]+)#im',
114 '\\1<a href="http://\\2.wikipedia.org/wiki/\\3">\\3</a>',
115 $text
116 );
117 $text = str_replace(
118 'http://.wikipedia.org/wiki/',
119 'http://www.wikipedia.org/wiki/',
120 $text
121 );
122 $text = str_replace('\wp:', 'wp:', $text);
123 $text = str_replace('\http:', 'http:', $text);
124 $text = MyTool::formatBBCode($text);
125 $text = nl2br($text);
126
127 return $text;
128 }
129
130 public static function getUrl()
131 {
132 $https = (!empty($_SERVER['HTTPS'])
133 && (strtolower($_SERVER['HTTPS']) == 'on'))
134 || (isset($_SERVER["SERVER_PORT"])
135 && $_SERVER["SERVER_PORT"] == '443'); // HTTPS detection.
136 $serverport = (!isset($_SERVER["SERVER_PORT"])
137 || $_SERVER["SERVER_PORT"] == '80'
138 || ($https && $_SERVER["SERVER_PORT"] == '443')
139 ? ''
140 : ':' . $_SERVER["SERVER_PORT"]);
141
142 $scriptname = str_replace('/index.php', '/', $_SERVER["SCRIPT_NAME"]);
143
144 if (!isset($_SERVER["SERVER_NAME"])) {
145 return $scriptname;
146 }
147
148 return 'http' . ($https ? 's' : '') . '://'
149 . $_SERVER["SERVER_NAME"] . $serverport . $scriptname;
150 }
151
152 public static function rrmdir($dir)
153 {
154 if (is_dir($dir) && ($d = @opendir($dir))) {
155 while (($file = @readdir($d)) !== false) {
156 if ( $file == '.' || $file == '..' ) {
157 continue;
158 } else {
159 unlink($dir . '/' . $file);
160 }
161 }
162 }
163 }
164
165 public static function humanBytes($bytes)
166 {
167 $siPrefix = array( 'bytes', 'KB', 'MB', 'GB', 'TB', 'EB', 'ZB', 'YB' );
168 $base = 1024;
169 $class = min((int) log($bytes, $base), count($siPrefix) - 1);
170 $val = sprintf('%1.2f', $bytes / pow($base, $class));
171
172 return $val . ' ' . $siPrefix[$class];
173 }
174
175 public static function returnBytes($val)
176 {
177 $val = trim($val);
178 $last = strtolower($val[strlen($val)-1]);
179 switch($last)
180 {
181 case 'g': $val *= 1024;
182 case 'm': $val *= 1024;
183 case 'k': $val *= 1024;
184 }
185
186 return $val;
187 }
188
189 public static function getMaxFileSize()
190 {
191 $sizePostMax = MyTool::returnBytes(ini_get('post_max_size'));
192 $sizeUploadMax = MyTool::returnBytes(ini_get('upload_max_filesize'));
193
194 // Return the smaller of two:
195 return min($sizePostMax, $sizeUploadMax);
196 }
197
198 public static function smallHash($text)
199 {
200 $t = rtrim(base64_encode(hash('crc32', $text, true)), '=');
201 // Get rid of characters which need encoding in URLs.
202 $t = str_replace('+', '-', $t);
203 $t = str_replace('/', '_', $t);
204 $t = str_replace('=', '@', $t);
205
206 return $t;
207 }
208
209 public static function renderJson($data)
210 {
211 header('Cache-Control: no-cache, must-revalidate');
212 header('Expires: Sat, 26 Jul 1997 05:00:00 GMT');
213 header('Content-type: application/json; charset=UTF-8');
214
215 echo json_encode($data);
216 exit();
217 }
218
219 public static function grabToLocal($url, $file, $force = false)
220 {
221 if ((!file_exists($file) || $force) && in_array('curl', get_loaded_extensions())){
222 $ch = curl_init ($url);
223 curl_setopt($ch, CURLOPT_HEADER, false);
224 curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
225 curl_setopt($ch, CURLOPT_BINARYTRANSFER, true);
226 $raw = curl_exec($ch);
227 if (curl_getinfo($ch, CURLINFO_HTTP_CODE) == 200) {
228 $fp = fopen($file, 'x');
229 fwrite($fp, $raw);
230 fclose($fp);
231 }
232 curl_close ($ch);
233 }
234 }
235
236 public static function redirect($rurl = '')
237 {
238 if ($rurl === '') {
239 // if (!empty($_SERVER['HTTP_REFERER']) && strcmp(parse_url($_SERVER['HTTP_REFERER'],PHP_URL_HOST),$_SERVER['SERVER_NAME'])==0)
240 $rurl = (empty($_SERVER['HTTP_REFERER'])?'?':$_SERVER['HTTP_REFERER']);
241 if (isset($_POST['returnurl'])) {
242 $rurl = $_POST['returnurl'];
243 }
244 }
245
246 // prevent loop
247 if (empty($rurl) || parse_url($rurl, PHP_URL_QUERY) === $_SERVER['QUERY_STRING']) {
248 $rurl = MyTool::getUrl();
249 }
250
251 if (substr($rurl, 0, 1) !== '?') {
252 $ref = MyTool::getUrl();
253 if (substr($rurl, 0, strlen($ref)) !== $ref) {
254 $rurl = $ref;
255 }
256 }
257 header('Location: '.$rurl);
258 exit();
259 }
260
261 public static function silence_errors($num, $str)
262 {
263// No-op
264 }
265} \ No newline at end of file
diff --git a/inc/config.php b/inc/config.php
deleted file mode 100644
index bd9287fe..00000000
--- a/inc/config.php
+++ /dev/null
@@ -1,66 +0,0 @@
1<?php
2/**
3 * poche, a read it later open source system
4 *
5 * @category poche
6 * @author Nicolas Lœuillet <nicolas@loeuillet.org>
7 * @copyright 2013
8 * @license http://www.wtfpl.net/ see COPYING file
9 */
10
11define ('POCHE_VERSION', '0.2.1');
12
13if (!is_dir('db/')) {
14 @mkdir('db/',0705);
15}
16
17define ('MODE_DEMO', FALSE);
18define ('ABS_PATH', 'assets/');
19define ('CONVERT_LINKS_FOOTNOTES', TRUE);
20define ('REVERT_FORCED_PARAGRAPH_ELEMENTS',FALSE);
21define ('DOWNLOAD_PICTURES', TRUE);
22define ('SALT', '464v54gLLw928uz4zUBqkRJeiPY68zCX');
23$storage_type = 'sqlite'; # sqlite or file
24
25include 'functions.php';
26require_once 'Readability.php';
27require_once 'Encoding.php';
28require_once 'rain.tpl.class.php';
29require_once 'MyTool.class.php';
30require_once 'Session.class.php';
31require_once 'store/store.class.php';
32require_once 'store/sqlite.class.php';
33require_once 'store/file.class.php';
34require_once 'class.messages.php';
35
36Session::init();
37
38$store = new $storage_type();
39# initialisation de RainTPL
40raintpl::$tpl_dir = './tpl/';
41raintpl::$cache_dir = './cache/';
42raintpl::$base_url = get_poche_url();
43raintpl::configure('path_replace', false);
44raintpl::configure('debug', false);
45$tpl = new raintpl();
46
47if(!$store->isInstalled())
48{
49 logm('poche still not installed');
50 $tpl->draw('install');
51 if (isset($_GET['install'])) {
52 if (($_POST['password'] == $_POST['password_repeat'])
53 && $_POST['password'] != "" && $_POST['login'] != "") {
54 $store->install($_POST['login'], encode_string($_POST['password'] . $_POST['login']));
55 Session::logout();
56 MyTool::redirect();
57 }
58 }
59 exit();
60}
61
62$_SESSION['login'] = (isset ($_SESSION['login'])) ? $_SESSION['login'] : $store->getLogin();
63$_SESSION['pass'] = (isset ($_SESSION['pass'])) ? $_SESSION['pass'] : $store->getPassword();
64
65$msg = new Messages();
66$tpl->assign('msg', $msg); \ No newline at end of file
diff --git a/inc/functions.php b/inc/functions.php
deleted file mode 100644
index 73e591c5..00000000
--- a/inc/functions.php
+++ /dev/null
@@ -1,398 +0,0 @@
1<?php
2/**
3 * poche, a read it later open source system
4 *
5 * @category poche
6 * @author Nicolas Lœuillet <support@inthepoche.com>
7 * @copyright 2013
8 * @license http://www.wtfpl.net/ see COPYING file
9 */
10
11/**
12 * Permet de générer l'URL de poche pour le bookmarklet
13 */
14function get_poche_url()
15{
16 $protocol = "http";
17 if(isset($_SERVER['HTTPS'])) {
18 if($_SERVER['HTTPS'] != "off" && $_SERVER['HTTPS'] != "") {
19 $protocol = "https";
20 }
21 }
22
23 return $protocol . "://" . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'];
24}
25
26function encode_string($string)
27{
28 return sha1($string . SALT);
29}
30
31// function define to retrieve url content
32function get_external_file($url)
33{
34 $timeout = 15;
35 // spoofing FireFox 18.0
36 $useragent="Mozilla/5.0 (Windows NT 5.1; rv:18.0) Gecko/20100101 Firefox/18.0";
37
38 if (in_array ('curl', get_loaded_extensions())) {
39 // Fetch feed from URL
40 $curl = curl_init();
41 curl_setopt($curl, CURLOPT_URL, $url);
42 curl_setopt($curl, CURLOPT_TIMEOUT, $timeout);
43 curl_setopt($curl, CURLOPT_FOLLOWLOCATION, true);
44 curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
45 curl_setopt($curl, CURLOPT_HEADER, false);
46
47 // FOR SSL do not verified certificate
48 curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, FALSE);
49 curl_setopt($curl, CURLOPT_AUTOREFERER, TRUE );
50
51 // FeedBurner requires a proper USER-AGENT...
52 curl_setopt($curl, CURL_HTTP_VERSION_1_1, true);
53 curl_setopt($curl, CURLOPT_ENCODING, "gzip, deflate");
54 curl_setopt($curl, CURLOPT_USERAGENT, $useragent);
55
56 $data = curl_exec($curl);
57
58 $httpcode = curl_getinfo($curl, CURLINFO_HTTP_CODE);
59
60 $httpcodeOK = isset($httpcode) and ($httpcode == 200 or $httpcode == 301);
61
62 curl_close($curl);
63 } else {
64
65 // create http context and add timeout and user-agent
66 $context = stream_context_create(array(
67 'http'=>array('timeout' => $timeout,
68 'header'=> "User-Agent: ".$useragent, /*spoot Mozilla Firefox*/
69 'follow_location' => true),
70 // FOR SSL do not verified certificate
71 'ssl' => array('verify_peer' => false,
72 'allow_self_signed' => true)
73 )
74 );
75
76 // only download page lesser than 4MB
77 $data = @file_get_contents($url, false, $context, -1, 4000000); // We download at most 4 MB from source.
78
79 if(isset($http_response_header) and isset($http_response_header[0])) {
80 $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));
81 }
82 }
83
84 // if response is not empty and response is OK
85 if (isset($data) and isset($httpcodeOK) and $httpcodeOK ) {
86
87 // take charset of page and get it
88 preg_match('#<meta .*charset=.*>#Usi', $data, $meta);
89
90 // if meta tag is found
91 if (!empty($meta[0])) {
92 // retrieve encoding in $enc
93 preg_match('#charset="?(.*)"#si', $meta[0], $enc);
94
95 // if charset is found set it otherwise, set it to utf-8
96 $html_charset = (!empty($enc[1])) ? strtolower($enc[1]) : 'utf-8';
97
98 } else {
99 $html_charset = 'utf-8';
100 $enc[1] = '';
101 }
102
103 // replace charset of url to charset of page
104 $data = str_replace('charset='.$enc[1], 'charset='.$html_charset, $data);
105
106 return $data;
107 }
108 else {
109 return FALSE;
110 }
111}
112
113/**
114 * Préparation de l'URL avec récupération du contenu avant insertion en base
115 */
116function prepare_url($url)
117{
118 $parametres = array();
119 $url = html_entity_decode(trim($url));
120
121 // We remove the annoying parameters added by FeedBurner and GoogleFeedProxy (?utm_source=...)
122 // from shaarli, by sebsauvage
123 $i=strpos($url,'&utm_source='); if ($i!==false) $url=substr($url,0,$i);
124 $i=strpos($url,'?utm_source='); if ($i!==false) $url=substr($url,0,$i);
125 $i=strpos($url,'#xtor=RSS-'); if ($i!==false) $url=substr($url,0,$i);
126
127 $title = $url;
128 $html = Encoding::toUTF8(get_external_file($url,15));
129 // If get_external_file if not able to retrieve HTTPS content try the same URL with HTTP protocol
130 if (!preg_match('!^https?://!i', $url) && (!isset($html) || strlen($html) <= 0)) {
131 $url = 'http://' . $url;
132 $html = Encoding::toUTF8(get_external_file($url,15));
133 }
134
135 if (function_exists('tidy_parse_string')) {
136 $tidy = tidy_parse_string($html, array(), 'UTF8');
137 $tidy->cleanRepair();
138 $html = $tidy->value;
139 }
140
141 if (isset($html) and strlen($html) > 0)
142 {
143 $r = new Readability($html, $url);
144
145 $r->convertLinksToFootnotes = CONVERT_LINKS_FOOTNOTES;
146 $r->revertForcedParagraphElements = REVERT_FORCED_PARAGRAPH_ELEMENTS;
147
148 if($r->init())
149 {
150 $content = $r->articleContent->innerHTML;
151 $parametres['title'] = $r->articleTitle->innerHTML;
152 $parametres['content'] = $content;
153 return $parametres;
154 }
155 }
156
157 return FALSE;
158}
159
160/**
161 * On modifie les URLS des images dans le corps de l'article
162 */
163function filtre_picture($content, $url, $id)
164{
165 $matches = array();
166 preg_match_all('#<\s*(img)[^>]+src="([^"]*)"[^>]*>#Si', $content, $matches, PREG_SET_ORDER);
167 foreach($matches as $i => $link)
168 {
169 $link[1] = trim($link[1]);
170 if (!preg_match('#^(([a-z]+://)|(\#))#', $link[1]) )
171 {
172 $absolute_path = get_absolute_link($link[2],$url);
173 $filename = basename(parse_url($absolute_path, PHP_URL_PATH));
174 $directory = create_assets_directory($id);
175 $fullpath = $directory . '/' . $filename;
176 download_pictures($absolute_path, $fullpath);
177 $content = str_replace($matches[$i][2], $fullpath, $content);
178 }
179
180 }
181
182 return $content;
183}
184
185/**
186 * Retourne le lien absolu
187 */
188function get_absolute_link($relative_link, $url)
189{
190 /* return if already absolute URL */
191 if (parse_url($relative_link, PHP_URL_SCHEME) != '') return $relative_link;
192
193 /* queries and anchors */
194 if ($relative_link[0]=='#' || $relative_link[0]=='?') return $url . $relative_link;
195
196 /* parse base URL and convert to local variables:
197 $scheme, $host, $path */
198 extract(parse_url($url));
199
200 /* remove non-directory element from path */
201 $path = preg_replace('#/[^/]*$#', '', $path);
202
203 /* destroy path if relative url points to root */
204 if ($relative_link[0] == '/') $path = '';
205
206 /* dirty absolute URL */
207 $abs = $host . $path . '/' . $relative_link;
208
209 /* replace '//' or '/./' or '/foo/../' with '/' */
210 $re = array('#(/\.?/)#', '#/(?!\.\.)[^/]+/\.\./#');
211 for($n=1; $n>0; $abs=preg_replace($re, '/', $abs, -1, $n)) {}
212
213 /* absolute URL is ready! */
214 return $scheme.'://'.$abs;
215}
216
217/**
218 * Téléchargement des images
219 */
220
221function download_pictures($absolute_path, $fullpath)
222{
223 $rawdata = get_external_file($absolute_path);
224
225 if(file_exists($fullpath)) {
226 unlink($fullpath);
227 }
228 $fp = fopen($fullpath, 'x');
229 fwrite($fp, $rawdata);
230 fclose($fp);
231}
232
233/**
234 * Crée un répertoire de médias pour l'article
235 */
236function create_assets_directory($id)
237{
238 $assets_path = ABS_PATH;
239 if(!is_dir($assets_path)) {
240 mkdir($assets_path, 0705);
241 }
242
243 $article_directory = $assets_path . $id;
244 if(!is_dir($article_directory)) {
245 mkdir($article_directory, 0705);
246 }
247
248 return $article_directory;
249}
250
251/**
252 * Suppression du répertoire d'images
253 */
254function remove_directory($directory)
255{
256 if(is_dir($directory)) {
257 $files = array_diff(scandir($directory), array('.','..'));
258 foreach ($files as $file) {
259 (is_dir("$directory/$file")) ? remove_directory("$directory/$file") : unlink("$directory/$file");
260 }
261 return rmdir($directory);
262 }
263}
264
265function display_view($view, $id = 0, $full_head = 'yes')
266{
267 global $tpl, $store, $msg;
268
269 switch ($view)
270 {
271 case 'export':
272 $entries = $store->retrieveAll();
273 $tpl->assign('export', myTool::renderJson($entries));
274 $tpl->draw('export');
275 logm('export view');
276 break;
277 case 'config':
278 $tpl->assign('load_all_js', 0);
279 $tpl->draw('head');
280 $tpl->draw('home');
281 $tpl->draw('config');
282 $tpl->draw('js');
283 $tpl->draw('footer');
284 logm('config view');
285 break;
286 case 'view':
287 $entry = $store->retrieveOneById($id);
288
289 if ($entry != NULL) {
290 $tpl->assign('id', $entry['id']);
291 $tpl->assign('url', $entry['url']);
292 $tpl->assign('title', $entry['title']);
293 $content = $entry['content'];
294 if (function_exists('tidy_parse_string')) {
295 $tidy = tidy_parse_string($content, array('indent'=>true, 'show-body-only' => true), 'UTF8');
296 $tidy->cleanRepair();
297 $content = $tidy->value;
298 }
299 $tpl->assign('content', $content);
300 $tpl->assign('is_fav', $entry['is_fav']);
301 $tpl->assign('is_read', $entry['is_read']);
302 $tpl->assign('load_all_js', 0);
303 $tpl->draw('view');
304 }
305 else {
306 logm('error in view call : entry is NULL');
307 }
308
309 logm('view link #' . $id);
310 break;
311 default: # home view
312 $entries = $store->getEntriesByView($view);
313
314 $tpl->assign('entries', $entries);
315
316 if ($full_head == 'yes') {
317 $tpl->assign('load_all_js', 1);
318 $tpl->draw('head');
319 $tpl->draw('home');
320 }
321
322 $tpl->draw('entries');
323
324 if ($full_head == 'yes') {
325 $tpl->draw('js');
326 $tpl->draw('footer');
327 }
328 break;
329 }
330}
331
332/**
333 * Appel d'une action (mark as fav, archive, delete)
334 */
335function action_to_do($action, $url, $id = 0)
336{
337 global $store, $msg;
338
339 switch ($action)
340 {
341 case 'add':
342 if ($url == '')
343 continue;
344
345 if (MyTool::isUrl($url)) {
346 if($parametres_url = prepare_url($url)) {
347 if ($store->add($url, $parametres_url['title'], $parametres_url['content'])) {
348 $last_id = $store->getLastId();
349 if (DOWNLOAD_PICTURES) {
350 $content = filtre_picture($parametres_url['content'], $url, $last_id);
351 }
352 $msg->add('s', 'the link has been added successfully');
353 }
354 else {
355 $msg->add('e', 'error during insertion : the link wasn\'t added');
356 }
357 }
358 else {
359 $msg->add('e', 'error during url preparation : the link wasn\'t added');
360 logm('error during url preparation');
361 }
362 }
363 else {
364 $msg->add('e', 'error during url preparation : the link is not valid');
365 logm($url . ' is not a valid url');
366 }
367
368 logm('add link ' . $url);
369 break;
370 case 'delete':
371 if ($store->deleteById($id)) {
372 remove_directory(ABS_PATH . $id);
373 $msg->add('s', 'the link has been deleted successfully');
374 logm('delete link #' . $id);
375 }
376 else {
377 $msg->add('e', 'the link wasn\'t deleted');
378 logm('error : can\'t delete link #' . $id);
379 }
380 break;
381 case 'toggle_fav' :
382 $store->favoriteById($id);
383 logm('mark as favorite link #' . $id);
384 break;
385 case 'toggle_archive' :
386 $store->archiveById($id);
387 logm('archive link #' . $id);
388 break;
389 default:
390 break;
391 }
392}
393
394function logm($message)
395{
396 $t = strval(date('Y/m/d_H:i:s')).' - '.$_SERVER["REMOTE_ADDR"].' - '.strval($message)."\n";
397 file_put_contents('./log.txt',$t,FILE_APPEND);
398}
diff --git a/inc/poche/Database.class.php b/inc/poche/Database.class.php
new file mode 100644
index 00000000..cd5a9a31
--- /dev/null
+++ b/inc/poche/Database.class.php
@@ -0,0 +1,216 @@
1<?php
2/**
3 * poche, a read it later open source system
4 *
5 * @category poche
6 * @author Nicolas Lœuillet <support@inthepoche.com>
7 * @copyright 2013
8 * @license http://www.wtfpl.net/ see COPYING file
9 */
10
11class Database {
12 var $handle;
13
14 function __construct()
15 {
16 switch (STORAGE) {
17 case 'sqlite':
18 $db_path = 'sqlite:' . STORAGE_SQLITE;
19 $this->handle = new PDO($db_path);
20 break;
21 case 'mysql':
22 $db_path = 'mysql:host=' . STORAGE_SERVER . ';dbname=' . STORAGE_DB;
23 $this->handle = new PDO($db_path, STORAGE_USER, STORAGE_PASSWORD);
24 break;
25 case 'postgres':
26 $db_path = 'pgsql:host=' . STORAGE_SERVER . ';dbname=' . STORAGE_DB;
27 $this->handle = new PDO($db_path, STORAGE_USER, STORAGE_PASSWORD);
28 break;
29 }
30
31 $this->handle->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
32 Tools::logm('storage type ' . STORAGE);
33 }
34
35 private function getHandle() {
36 return $this->handle;
37 }
38
39 public function isInstalled() {
40 $sql = "SELECT username FROM users";
41 $query = $this->executeQuery($sql, array());
42 $hasAdmin = count($query->fetchAll());
43
44 if ($hasAdmin == 0)
45 return FALSE;
46
47 return TRUE;
48 }
49
50 public function install($login, $password) {
51 $sql = 'INSERT INTO users ( username, password, name, email) VALUES (?, ?, ?, ?)';
52 $params = array($login, $password, $login, ' ');
53 $query = $this->executeQuery($sql, $params);
54
55 $sequence = '';
56 if (STORAGE == 'postgres') {
57 $sequence = 'users_id_seq';
58 }
59
60 $id_user = intval($this->getLastId($sequence));
61
62 $sql = 'INSERT INTO users_config ( user_id, name, value ) VALUES (?, ?, ?)';
63 $params = array($id_user, 'pager', '10');
64 $query = $this->executeQuery($sql, $params);
65
66 $sql = 'INSERT INTO users_config ( user_id, name, value ) VALUES (?, ?, ?)';
67 $params = array($id_user, 'language', 'en_EN.UTF8');
68 $query = $this->executeQuery($sql, $params);
69
70 return TRUE;
71 }
72
73 private function getConfigUser($id) {
74 $sql = "SELECT * FROM users_config WHERE user_id = ?";
75 $query = $this->executeQuery($sql, array($id));
76 $result = $query->fetchAll();
77 $user_config = array();
78
79 foreach ($result as $key => $value) {
80 $user_config[$value['name']] = $value['value'];
81 }
82
83 return $user_config;
84 }
85
86 public function login($username, $password) {
87 $sql = "SELECT * FROM users WHERE username=? AND password=?";
88 $query = $this->executeQuery($sql, array($username, $password));
89 $login = $query->fetchAll();
90
91 $user = array();
92 if (isset($login[0])) {
93 $user['id'] = $login[0]['id'];
94 $user['username'] = $login[0]['username'];
95 $user['password'] = $login[0]['password'];
96 $user['name'] = $login[0]['name'];
97 $user['email'] = $login[0]['email'];
98 $user['config'] = $this->getConfigUser($login[0]['id']);
99 }
100
101 return $user;
102 }
103
104 public function updatePassword($id, $password)
105 {
106 $sql_update = "UPDATE users SET password=? WHERE id=?";
107 $params_update = array($password, $id);
108 $query = $this->executeQuery($sql_update, $params_update);
109 }
110
111 private function executeQuery($sql, $params) {
112 try
113 {
114 $query = $this->getHandle()->prepare($sql);
115 $query->execute($params);
116 return $query;
117 }
118 catch (Exception $e)
119 {
120 Tools::logm('execute query error : '.$e->getMessage());
121 return FALSE;
122 }
123 }
124
125 public function retrieveAll($user_id) {
126 $sql = "SELECT * FROM entries WHERE user_id=? ORDER BY id";
127 $query = $this->executeQuery($sql, array($user_id));
128 $entries = $query->fetchAll();
129
130 return $entries;
131 }
132
133 public function retrieveOneById($id, $user_id) {
134 $entry = NULL;
135 $sql = "SELECT * FROM entries WHERE id=? AND user_id=?";
136 $params = array(intval($id), $user_id);
137 $query = $this->executeQuery($sql, $params);
138 $entry = $query->fetchAll();
139
140 return $entry[0];
141 }
142
143 public function getEntriesByView($view, $user_id, $limit = '') {
144 switch ($_SESSION['sort'])
145 {
146 case 'ia':
147 $order = 'ORDER BY id';
148 break;
149 case 'id':
150 $order = 'ORDER BY id DESC';
151 break;
152 case 'ta':
153 $order = 'ORDER BY lower(title)';
154 break;
155 case 'td':
156 $order = 'ORDER BY lower(title) DESC';
157 break;
158 default:
159 $order = 'ORDER BY id';
160 break;
161 }
162
163 switch ($view)
164 {
165 case 'archive':
166 $sql = "SELECT * FROM entries WHERE user_id=? AND is_read=? " . $order;
167 $params = array($user_id, 1);
168 break;
169 case 'fav' :
170 $sql = "SELECT * FROM entries WHERE user_id=? AND is_fav=? " . $order;
171 $params = array($user_id, 1);
172 break;
173 default:
174 $sql = "SELECT * FROM entries WHERE user_id=? AND is_read=? " . $order;
175 $params = array($user_id, 0);
176 break;
177 }
178
179 $sql .= ' ' . $limit;
180
181 $query = $this->executeQuery($sql, $params);
182 $entries = $query->fetchAll();
183
184 return $entries;
185 }
186
187 public function add($url, $title, $content, $user_id) {
188 $sql_action = 'INSERT INTO entries ( url, title, content, user_id ) VALUES (?, ?, ?, ?)';
189 $params_action = array($url, $title, $content, $user_id);
190 $query = $this->executeQuery($sql_action, $params_action);
191 return $query;
192 }
193
194 public function deleteById($id, $user_id) {
195 $sql_action = "DELETE FROM entries WHERE id=? AND user_id=?";
196 $params_action = array($id, $user_id);
197 $query = $this->executeQuery($sql_action, $params_action);
198 return $query;
199 }
200
201 public function favoriteById($id, $user_id) {
202 $sql_action = "UPDATE entries SET is_fav=NOT is_fav WHERE id=? AND user_id=?";
203 $params_action = array($id, $user_id);
204 $query = $this->executeQuery($sql_action, $params_action);
205 }
206
207 public function archiveById($id, $user_id) {
208 $sql_action = "UPDATE entries SET is_read=NOT is_read WHERE id=? AND user_id=?";
209 $params_action = array($id, $user_id);
210 $query = $this->executeQuery($sql_action, $params_action);
211 }
212
213 public function getLastId($column = '') {
214 return $this->getHandle()->lastInsertId($column);
215 }
216}
diff --git a/inc/poche/Poche.class.php b/inc/poche/Poche.class.php
new file mode 100644
index 00000000..56910bc0
--- /dev/null
+++ b/inc/poche/Poche.class.php
@@ -0,0 +1,485 @@
1<?php
2/**
3 * poche, a read it later open source system
4 *
5 * @category poche
6 * @author Nicolas Lœuillet <support@inthepoche.com>
7 * @copyright 2013
8 * @license http://www.wtfpl.net/ see COPYING file
9 */
10
11class Poche
12{
13 public $user;
14 public $store;
15 public $tpl;
16 public $messages;
17 public $pagination;
18
19 function __construct()
20 {
21 $this->store = new Database();
22 $this->init();
23 $this->messages = new Messages();
24
25 # installation
26 if(!$this->store->isInstalled())
27 {
28 $this->install();
29 }
30 }
31
32 private function init()
33 {
34 if (file_exists('./install') && !DEBUG_POCHE) {
35 Tools::logm('folder /install exists');
36 die('the folder /install exists, you have to delete it before using poche.');
37 }
38
39 Tools::initPhp();
40 Session::init();
41
42 if (isset($_SESSION['poche_user']) && $_SESSION['poche_user'] != array()) {
43 $this->user = $_SESSION['poche_user'];
44 }
45 else {
46 # fake user, just for install & login screens
47 $this->user = new User();
48 $this->user->setConfig($this->getDefaultConfig());
49 }
50
51 # l10n
52 $language = $this->user->getConfigValue('language');
53 putenv('LC_ALL=' . $language);
54 setlocale(LC_ALL, $language);
55 bindtextdomain($language, LOCALE);
56 textdomain($language);
57
58 # template engine
59 $loader = new Twig_Loader_Filesystem(TPL);
60 if (DEBUG_POCHE) {
61 $twig_params = array();
62 }
63 else {
64 $twig_params = array('cache' => CACHE);
65 }
66 $this->tpl = new Twig_Environment($loader, $twig_params);
67 $this->tpl->addExtension(new Twig_Extensions_Extension_I18n());
68 # filter to display domain name of an url
69 $filter = new Twig_SimpleFilter('getDomain', 'Tools::getDomain');
70 $this->tpl->addFilter($filter);
71
72 # Pagination
73 $this->pagination = new Paginator($this->user->getConfigValue('pager'), 'p');
74 }
75
76 private function install()
77 {
78 Tools::logm('poche still not installed');
79 echo $this->tpl->render('install.twig', array(
80 'token' => Session::getToken()
81 ));
82 if (isset($_GET['install'])) {
83 if (($_POST['password'] == $_POST['password_repeat'])
84 && $_POST['password'] != "" && $_POST['login'] != "") {
85 # let's rock, install poche baby !
86 $this->store->install($_POST['login'], Tools::encodeString($_POST['password'] . $_POST['login']));
87 Session::logout();
88 Tools::logm('poche is now installed');
89 Tools::redirect();
90 }
91 else {
92 Tools::logm('error during installation');
93 Tools::redirect();
94 }
95 }
96 exit();
97 }
98
99 public function getDefaultConfig()
100 {
101 return array(
102 'pager' => PAGINATION,
103 'language' => LANG,
104 );
105 }
106
107 /**
108 * Call action (mark as fav, archive, delete, etc.)
109 */
110 public function action($action, Url $url, $id = 0, $import = FALSE)
111 {
112 switch ($action)
113 {
114 case 'add':
115 if($parametres_url = $url->fetchContent()) {
116 if ($this->store->add($url->getUrl(), $parametres_url['title'], $parametres_url['content'], $this->user->getId())) {
117 Tools::logm('add link ' . $url->getUrl());
118 $sequence = '';
119 if (STORAGE == 'postgres') {
120 $sequence = 'entries_id_seq';
121 }
122 $last_id = $this->store->getLastId($sequence);
123 if (DOWNLOAD_PICTURES) {
124 $content = filtre_picture($parametres_url['content'], $url->getUrl(), $last_id);
125 }
126 if (!$import) {
127 $this->messages->add('s', _('the link has been added successfully'));
128 }
129 }
130 else {
131 if (!$import) {
132 $this->messages->add('e', _('error during insertion : the link wasn\'t added'));
133 Tools::logm('error during insertion : the link wasn\'t added ' . $url->getUrl());
134 }
135 }
136 }
137 else {
138 if (!$import) {
139 $this->messages->add('e', _('error during fetching content : the link wasn\'t added'));
140 Tools::logm('error during content fetch ' . $url->getUrl());
141 }
142 }
143 if (!$import) {
144 Tools::redirect();
145 }
146 break;
147 case 'delete':
148 $msg = 'delete link #' . $id;
149 if ($this->store->deleteById($id, $this->user->getId())) {
150 if (DOWNLOAD_PICTURES) {
151 remove_directory(ABS_PATH . $id);
152 }
153 $this->messages->add('s', _('the link has been deleted successfully'));
154 }
155 else {
156 $this->messages->add('e', _('the link wasn\'t deleted'));
157 $msg = 'error : can\'t delete link #' . $id;
158 }
159 Tools::logm($msg);
160 Tools::redirect('?');
161 break;
162 case 'toggle_fav' :
163 $this->store->favoriteById($id, $this->user->getId());
164 Tools::logm('mark as favorite link #' . $id);
165 if (!$import) {
166 Tools::redirect();
167 }
168 break;
169 case 'toggle_archive' :
170 $this->store->archiveById($id, $this->user->getId());
171 Tools::logm('archive link #' . $id);
172 if (!$import) {
173 Tools::redirect();
174 }
175 break;
176 default:
177 break;
178 }
179 }
180
181 function displayView($view, $id = 0)
182 {
183 $tpl_vars = array();
184
185 switch ($view)
186 {
187 case 'config':
188 $dev = $this->getPocheVersion('dev');
189 $prod = $this->getPocheVersion('prod');
190 $compare_dev = version_compare(POCHE_VERSION, $dev);
191 $compare_prod = version_compare(POCHE_VERSION, $prod);
192 $tpl_vars = array(
193 'dev' => $dev,
194 'prod' => $prod,
195 'compare_dev' => $compare_dev,
196 'compare_prod' => $compare_prod,
197 );
198 Tools::logm('config view');
199 break;
200 case 'view':
201 $entry = $this->store->retrieveOneById($id, $this->user->getId());
202 if ($entry != NULL) {
203 Tools::logm('view link #' . $id);
204 $content = $entry['content'];
205 if (function_exists('tidy_parse_string')) {
206 $tidy = tidy_parse_string($content, array('indent'=>true, 'show-body-only' => true), 'UTF8');
207 $tidy->cleanRepair();
208 $content = $tidy->value;
209 }
210 $tpl_vars = array(
211 'entry' => $entry,
212 'content' => $content,
213 );
214 }
215 else {
216 Tools::logm('error in view call : entry is NULL');
217 }
218 break;
219 default: # home view
220 $entries = $this->store->getEntriesByView($view, $this->user->getId());
221 $this->pagination->set_total(count($entries));
222 $page_links = $this->pagination->page_links('?view=' . $view . '&sort=' . $_SESSION['sort'] . '&');
223 $datas = $this->store->getEntriesByView($view, $this->user->getId(), $this->pagination->get_limit());
224 $tpl_vars = array(
225 'entries' => $datas,
226 'page_links' => $page_links,
227 );
228 Tools::logm('display ' . $view . ' view');
229 break;
230 }
231
232 return $tpl_vars;
233 }
234
235 /**
236 * update the password of the current user.
237 * if MODE_DEMO is TRUE, the password can't be updated.
238 * @todo add the return value
239 * @todo set the new password in function header like this updatePassword($newPassword)
240 * @return boolean
241 */
242 public function updatePassword()
243 {
244 if (MODE_DEMO) {
245 $this->messages->add('i', _('in demo mode, you can\'t update your password'));
246 Tools::logm('in demo mode, you can\'t do this');
247 Tools::redirect('?view=config');
248 }
249 else {
250 if (isset($_POST['password']) && isset($_POST['password_repeat'])) {
251 if ($_POST['password'] == $_POST['password_repeat'] && $_POST['password'] != "") {
252 $this->messages->add('s', _('your password has been updated'));
253 $this->store->updatePassword($this->user->getId(), Tools::encodeString($_POST['password'] . $this->user->getUsername()));
254 Session::logout();
255 Tools::logm('password updated');
256 Tools::redirect();
257 }
258 else {
259 $this->messages->add('e', _('the two fields have to be filled & the password must be the same in the two fields'));
260 Tools::redirect('?view=config');
261 }
262 }
263 }
264 }
265
266 /**
267 * checks if login & password are correct and save the user in session.
268 * it redirects the user to the $referer link
269 * @param string $referer the url to redirect after login
270 * @todo add the return value
271 * @return boolean
272 */
273 public function login($referer)
274 {
275 if (!empty($_POST['login']) && !empty($_POST['password'])) {
276 $user = $this->store->login($_POST['login'], Tools::encodeString($_POST['password'] . $_POST['login']));
277 if ($user != array()) {
278 # Save login into Session
279 Session::login($user['username'], $user['password'], $_POST['login'], Tools::encodeString($_POST['password'] . $_POST['login']), array('poche_user' => new User($user)));
280
281 $this->messages->add('s', _('welcome to your poche'));
282 if (!empty($_POST['longlastingsession'])) {
283 $_SESSION['longlastingsession'] = 31536000;
284 $_SESSION['expires_on'] = time() + $_SESSION['longlastingsession'];
285 session_set_cookie_params($_SESSION['longlastingsession']);
286 } else {
287 session_set_cookie_params(0);
288 }
289 session_regenerate_id(true);
290 Tools::logm('login successful');
291 Tools::redirect($referer);
292 }
293 $this->messages->add('e', _('login failed: bad login or password'));
294 Tools::logm('login failed');
295 Tools::redirect();
296 } else {
297 $this->messages->add('e', _('login failed: you have to fill all fields'));
298 Tools::logm('login failed');
299 Tools::redirect();
300 }
301 }
302
303 /**
304 * log out the poche user. It cleans the session.
305 * @todo add the return value
306 * @return boolean
307 */
308 public function logout()
309 {
310 $this->user = array();
311 Session::logout();
312 $this->messages->add('s', _('see you soon!'));
313 Tools::logm('logout');
314 Tools::redirect();
315 }
316
317 /**
318 * import from Instapaper. poche needs a ./instapaper-export.html file
319 * @todo add the return value
320 * @return boolean
321 */
322 private function importFromInstapaper()
323 {
324 # TODO gestion des articles favs
325 $html = new simple_html_dom();
326 $html->load_file('./instapaper-export.html');
327 Tools::logm('starting import from instapaper');
328
329 $read = 0;
330 $errors = array();
331 foreach($html->find('ol') as $ul)
332 {
333 foreach($ul->find('li') as $li)
334 {
335 $a = $li->find('a');
336 $url = new Url(base64_encode($a[0]->href));
337 $this->action('add', $url, 0, TRUE);
338 if ($read == '1') {
339 $sequence = '';
340 if (STORAGE == 'postgres') {
341 $sequence = 'entries_id_seq';
342 }
343 $last_id = $this->store->getLastId($sequence);
344 $this->action('toggle_archive', $url, $last_id, TRUE);
345 }
346 }
347
348 # the second <ol> is for read links
349 $read = 1;
350 }
351 $this->messages->add('s', _('import from instapaper completed'));
352 Tools::logm('import from instapaper completed');
353 Tools::redirect();
354 }
355
356 /**
357 * import from Pocket. poche needs a ./ril_export.html file
358 * @todo add the return value
359 * @return boolean
360 */
361 private function importFromPocket()
362 {
363 # TODO gestion des articles favs
364 $html = new simple_html_dom();
365 $html->load_file('./ril_export.html');
366 Tools::logm('starting import from pocket');
367
368 $read = 0;
369 $errors = array();
370 foreach($html->find('ul') as $ul)
371 {
372 foreach($ul->find('li') as $li)
373 {
374 $a = $li->find('a');
375 $url = new Url(base64_encode($a[0]->href));
376 $this->action('add', $url, 0, TRUE);
377 if ($read == '1') {
378 $sequence = '';
379 if (STORAGE == 'postgres') {
380 $sequence = 'entries_id_seq';
381 }
382 $last_id = $this->store->getLastId($sequence);
383 $this->action('toggle_archive', $url, $last_id, TRUE);
384 }
385 }
386
387 # the second <ul> is for read links
388 $read = 1;
389 }
390 $this->messages->add('s', _('import from pocket completed'));
391 Tools::logm('import from pocket completed');
392 Tools::redirect();
393 }
394
395 /**
396 * import from Readability. poche needs a ./readability file
397 * @todo add the return value
398 * @return boolean
399 */
400 private function importFromReadability()
401 {
402 # TODO gestion des articles lus / favs
403 $str_data = file_get_contents("./readability");
404 $data = json_decode($str_data,true);
405 Tools::logm('starting import from Readability');
406
407 foreach ($data as $key => $value) {
408 $url = '';
409 foreach ($value as $attr => $attr_value) {
410 if ($attr == 'article__url') {
411 $url = new Url(base64_encode($attr_value));
412 }
413 $sequence = '';
414 if (STORAGE == 'postgres') {
415 $sequence = 'entries_id_seq';
416 }
417 // if ($attr_value == 'favorite' && $attr_value == 'true') {
418 // $last_id = $this->store->getLastId($sequence);
419 // $this->store->favoriteById($last_id);
420 // $this->action('toogle_fav', $url, $last_id, TRUE);
421 // }
422 if ($attr_value == 'archive' && $attr_value == 'true') {
423 $last_id = $this->store->getLastId($sequence);
424 $this->action('toggle_archive', $url, $last_id, TRUE);
425 }
426 }
427 if ($url->isCorrect())
428 $this->action('add', $url, 0, TRUE);
429 }
430 $this->messages->add('s', _('import from Readability completed'));
431 Tools::logm('import from Readability completed');
432 Tools::redirect();
433 }
434
435 /**
436 * import datas into your poche
437 * @param string $from name of the service to import : pocket, instapaper or readability
438 * @todo add the return value
439 * @return boolean
440 */
441 public function import($from)
442 {
443 if ($from == 'pocket') {
444 return $this->importFromPocket();
445 }
446 else if ($from == 'readability') {
447 return $this->importFromReadability();
448 }
449 else if ($from == 'instapaper') {
450 return $this->importFromInstapaper();
451 }
452 }
453
454 /**
455 * export poche entries in json
456 * @return json all poche entries
457 */
458 public function export()
459 {
460 $entries = $this->store->retrieveAll($this->user->getId());
461 echo $this->tpl->render('export.twig', array(
462 'export' => Tools::renderJson($entries),
463 ));
464 Tools::logm('export view');
465 }
466
467 /**
468 * Checks online the latest version of poche and cache it
469 * @param string $which 'prod' or 'dev'
470 * @return string latest $which version
471 */
472 private function getPocheVersion($which = 'prod')
473 {
474 $cache_file = CACHE . '/' . $which;
475
476 # checks if the cached version file exists
477 if (file_exists($cache_file) && (filemtime($cache_file) > (time() - 86400 ))) {
478 $version = file_get_contents($cache_file);
479 } else {
480 $version = file_get_contents('http://static.inthepoche.com/versions/' . $which);
481 file_put_contents($cache_file, $version, LOCK_EX);
482 }
483 return $version;
484 }
485} \ No newline at end of file
diff --git a/inc/poche/Tools.class.php b/inc/poche/Tools.class.php
new file mode 100644
index 00000000..d0e43166
--- /dev/null
+++ b/inc/poche/Tools.class.php
@@ -0,0 +1,226 @@
1<?php
2/**
3 * poche, a read it later open source system
4 *
5 * @category poche
6 * @author Nicolas Lœuillet <support@inthepoche.com>
7 * @copyright 2013
8 * @license http://www.wtfpl.net/ see COPYING file
9 */
10
11class Tools
12{
13 public static function initPhp()
14 {
15 define('START_TIME', microtime(true));
16
17 if (phpversion() < 5) {
18 die(_('Oops, it seems you don\'t have PHP 5.'));
19 }
20
21 error_reporting(E_ALL);
22
23 function stripslashesDeep($value) {
24 return is_array($value)
25 ? array_map('stripslashesDeep', $value)
26 : stripslashes($value);
27 }
28
29 if (get_magic_quotes_gpc()) {
30 $_POST = array_map('stripslashesDeep', $_POST);
31 $_GET = array_map('stripslashesDeep', $_GET);
32 $_COOKIE = array_map('stripslashesDeep', $_COOKIE);
33 }
34
35 ob_start();
36 register_shutdown_function('ob_end_flush');
37 }
38
39 public static function getPocheUrl()
40 {
41 $https = (!empty($_SERVER['HTTPS'])
42 && (strtolower($_SERVER['HTTPS']) == 'on'))
43 || (isset($_SERVER["SERVER_PORT"])
44 && $_SERVER["SERVER_PORT"] == '443'); // HTTPS detection.
45 $serverport = (!isset($_SERVER["SERVER_PORT"])
46 || $_SERVER["SERVER_PORT"] == '80'
47 || ($https && $_SERVER["SERVER_PORT"] == '443')
48 ? '' : ':' . $_SERVER["SERVER_PORT"]);
49
50 $scriptname = str_replace('/index.php', '/', $_SERVER["SCRIPT_NAME"]);
51
52 if (!isset($_SERVER["SERVER_NAME"])) {
53 return $scriptname;
54 }
55
56 return 'http' . ($https ? 's' : '') . '://'
57 . $_SERVER["SERVER_NAME"] . $serverport . $scriptname;
58 }
59
60 public static function redirect($url = '')
61 {
62 if ($url === '') {
63 $url = (empty($_SERVER['HTTP_REFERER'])?'?':$_SERVER['HTTP_REFERER']);
64 if (isset($_POST['returnurl'])) {
65 $url = $_POST['returnurl'];
66 }
67 }
68
69 # prevent loop
70 if (empty($url) || parse_url($url, PHP_URL_QUERY) === $_SERVER['QUERY_STRING']) {
71 $url = Tools::getPocheUrl();
72 }
73
74 if (substr($url, 0, 1) !== '?') {
75 $ref = Tools::getPocheUrl();
76 if (substr($url, 0, strlen($ref)) !== $ref) {
77 $url = $ref;
78 }
79 }
80 self::logm('redirect to ' . $url);
81 header('Location: '.$url);
82 exit();
83 }
84
85 public static function getTplFile($view)
86 {
87 $tpl_file = 'home.twig';
88 switch ($view)
89 {
90 case 'install':
91 $tpl_file = 'install.twig';
92 break;
93 case 'import';
94 $tpl_file = 'import.twig';
95 break;
96 case 'export':
97 $tpl_file = 'export.twig';
98 break;
99 case 'config':
100 $tpl_file = 'config.twig';
101 break;
102 case 'view':
103 $tpl_file = 'view.twig';
104 break;
105 default:
106 break;
107 }
108 return $tpl_file;
109 }
110
111 public static function getFile($url)
112 {
113 $timeout = 15;
114 $useragent = "Mozilla/5.0 (Windows NT 5.1; rv:18.0) Gecko/20100101 Firefox/18.0";
115
116 if (in_array ('curl', get_loaded_extensions())) {
117 # Fetch feed from URL
118 $curl = curl_init();
119 curl_setopt($curl, CURLOPT_URL, $url);
120 curl_setopt($curl, CURLOPT_TIMEOUT, $timeout);
121 curl_setopt($curl, CURLOPT_FOLLOWLOCATION, true);
122 curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
123 curl_setopt($curl, CURLOPT_HEADER, false);
124
125 # for ssl, do not verified certificate
126 curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, FALSE);
127 curl_setopt($curl, CURLOPT_AUTOREFERER, TRUE );
128
129 # FeedBurner requires a proper USER-AGENT...
130 curl_setopt($curl, CURL_HTTP_VERSION_1_1, true);
131 curl_setopt($curl, CURLOPT_ENCODING, "gzip, deflate");
132 curl_setopt($curl, CURLOPT_USERAGENT, $useragent);
133
134 $data = curl_exec($curl);
135 $httpcode = curl_getinfo($curl, CURLINFO_HTTP_CODE);
136 $httpcodeOK = isset($httpcode) and ($httpcode == 200 or $httpcode == 301);
137 curl_close($curl);
138 } else {
139 # create http context and add timeout and user-agent
140 $context = stream_context_create(
141 array(
142 'http' => array(
143 'timeout' => $timeout,
144 'header' => "User-Agent: " . $useragent,
145 'follow_location' => true
146 ),
147 'ssl' => array(
148 'verify_peer' => false,
149 'allow_self_signed' => true
150 )
151 )
152 );
153
154 # only download page lesser than 4MB
155 $data = @file_get_contents($url, false, $context, -1, 4000000);
156
157 if (isset($http_response_header) and isset($http_response_header[0])) {
158 $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));
159 }
160 }
161
162 # if response is not empty and response is OK
163 if (isset($data) and isset($httpcodeOK) and $httpcodeOK) {
164
165 # take charset of page and get it
166 preg_match('#<meta .*charset=.*>#Usi', $data, $meta);
167
168 # if meta tag is found
169 if (!empty($meta[0])) {
170 preg_match('#charset="?(.*)"#si', $meta[0], $encoding);
171 # if charset is found set it otherwise, set it to utf-8
172 $html_charset = (!empty($encoding[1])) ? strtolower($encoding[1]) : 'utf-8';
173 } else {
174 $html_charset = 'utf-8';
175 $encoding[1] = '';
176 }
177
178 # replace charset of url to charset of page
179 $data = str_replace('charset=' . $encoding[1], 'charset=' . $html_charset, $data);
180
181 return $data;
182 }
183 else {
184 return FALSE;
185 }
186 }
187
188 public static function renderJson($data)
189 {
190 header('Cache-Control: no-cache, must-revalidate');
191 header('Expires: Sat, 26 Jul 1997 05:00:00 GMT');
192 header('Content-type: application/json; charset=UTF-8');
193 echo json_encode($data);
194 exit();
195 }
196
197 public static function logm($message)
198 {
199 if (DEBUG_POCHE) {
200 $t = strval(date('Y/m/d_H:i:s')) . ' - ' . $_SERVER["REMOTE_ADDR"] . ' - ' . strval($message) . "\n";
201 file_put_contents(CACHE . '/log.txt', $t, FILE_APPEND);
202 error_log('DEBUG POCHE : ' . $message);
203 }
204 }
205
206 public static function encodeString($string)
207 {
208 return sha1($string . SALT);
209 }
210
211 public static function checkVar($var, $default = '')
212 {
213 return ((isset ($_REQUEST["$var"])) ? htmlentities($_REQUEST["$var"]) : $default);
214 }
215
216 public static function getDomain($url)
217 {
218 $pieces = parse_url($url);
219 $domain = isset($pieces['host']) ? $pieces['host'] : '';
220 if (preg_match('/(?P<domain>[a-z0-9][a-z0-9\-]{1,63}\.[a-z\.]{2,6})$/i', $domain, $regs)) {
221 return $regs['domain'];
222 }
223
224 return FALSE;
225 }
226} \ No newline at end of file
diff --git a/inc/poche/Url.class.php b/inc/poche/Url.class.php
new file mode 100644
index 00000000..f4a8f99e
--- /dev/null
+++ b/inc/poche/Url.class.php
@@ -0,0 +1,94 @@
1<?php
2/**
3 * poche, a read it later open source system
4 *
5 * @category poche
6 * @author Nicolas Lœuillet <support@inthepoche.com>
7 * @copyright 2013
8 * @license http://www.wtfpl.net/ see COPYING file
9 */
10
11class Url
12{
13 public $url;
14
15 function __construct($url)
16 {
17 $this->url = base64_decode($url);
18 }
19
20 public function getUrl() {
21 return $this->url;
22 }
23
24 public function setUrl($url) {
25 $this->url = $url;
26 }
27
28 public function isCorrect()
29 {
30 $pattern = '|^http(s)?://[a-z0-9-]+(.[a-z0-9-]+)*(:[0-9]+)?(/.*)?$|i';
31
32 return preg_match($pattern, $this->url);
33 }
34
35 public function clean()
36 {
37 $url = html_entity_decode(trim($this->url));
38
39 $stuff = strpos($url,'&utm_source=');
40 if ($stuff !== FALSE)
41 $url = substr($url, 0, $stuff);
42 $stuff = strpos($url,'?utm_source=');
43 if ($stuff !== FALSE)
44 $url = substr($url, 0, $stuff);
45 $stuff = strpos($url,'#xtor=RSS-');
46 if ($stuff !== FALSE)
47 $url = substr($url, 0, $stuff);
48
49 $this->url = $url;
50 }
51
52 public function fetchContent()
53 {
54 if ($this->isCorrect()) {
55 $this->clean();
56 $html = Encoding::toUTF8(Tools::getFile($this->getUrl()));
57
58 # if Tools::getFile() if not able to retrieve HTTPS content, try the same URL with HTTP protocol
59 if (!preg_match('!^https?://!i', $this->getUrl()) && (!isset($html) || strlen($html) <= 0)) {
60 $this->setUrl('http://' . $this->getUrl());
61 $html = Encoding::toUTF8(Tools::getFile($this->getUrl()));
62 }
63
64 if (function_exists('tidy_parse_string')) {
65 $tidy = tidy_parse_string($html, array(), 'UTF8');
66 $tidy->cleanRepair();
67 $html = $tidy->value;
68 }
69
70 $parameters = array();
71 if (isset($html) and strlen($html) > 0)
72 {
73 $readability = new Readability($html, $this->getUrl());
74 $readability->convertLinksToFootnotes = CONVERT_LINKS_FOOTNOTES;
75 $readability->revertForcedParagraphElements = REVERT_FORCED_PARAGRAPH_ELEMENTS;
76
77 if($readability->init())
78 {
79 $content = $readability->articleContent->innerHTML;
80 $parameters['title'] = $readability->articleTitle->innerHTML;
81 $parameters['content'] = $content;
82
83 return $parameters;
84 }
85 }
86 }
87 else {
88 #$msg->add('e', _('error during url preparation : the link is not valid'));
89 Tools::logm($this->getUrl() . ' is not a valid url');
90 }
91
92 return FALSE;
93 }
94} \ No newline at end of file
diff --git a/inc/poche/User.class.php b/inc/poche/User.class.php
new file mode 100644
index 00000000..6dac7839
--- /dev/null
+++ b/inc/poche/User.class.php
@@ -0,0 +1,50 @@
1<?php
2/**
3 * poche, a read it later open source system
4 *
5 * @category poche
6 * @author Nicolas Lœuillet <support@inthepoche.com>
7 * @copyright 2013
8 * @license http://www.wtfpl.net/ see COPYING file
9 */
10
11class User
12{
13 public $id;
14 public $username;
15 public $name;
16 public $password;
17 public $email;
18 public $config;
19
20 function __construct($user = array())
21 {
22 if ($user != array()) {
23 $this->id = $user['id'];
24 $this->username = $user['username'];
25 $this->name = $user['name'];
26 $this->password = $user['password'];
27 $this->email = $user['email'];
28 $this->config = $user['config'];
29 }
30 }
31
32 public function getId()
33 {
34 return $this->id;
35 }
36
37 public function getUsername()
38 {
39 return $this->username;
40 }
41
42 public function setConfig($config)
43 {
44 $this->config = $config;
45 }
46
47 public function getConfigValue($name) {
48 return (isset($this->config[$name])) ? $this->config[$name] : FALSE;
49 }
50} \ No newline at end of file
diff --git a/inc/poche/config.inc.php b/inc/poche/config.inc.php
new file mode 100644
index 00000000..0958600f
--- /dev/null
+++ b/inc/poche/config.inc.php
@@ -0,0 +1,61 @@
1<?php
2/**
3 * poche, a read it later open source system
4 *
5 * @category poche
6 * @author Nicolas Lœuillet <nicolas@loeuillet.org>
7 * @copyright 2013
8 * @license http://www.wtfpl.net/ see COPYING file
9 */
10
11# storage
12define ('STORAGE','sqlite'); # postgres, mysql, sqlite
13define ('STORAGE_SERVER', 'localhost'); # leave blank for sqlite
14define ('STORAGE_DB', 'poche'); # only for postgres & mysql
15define ('STORAGE_SQLITE', './db/poche.sqlite');
16define ('STORAGE_USER', 'postgres'); # leave blank for sqlite
17define ('STORAGE_PASSWORD', 'postgres'); # leave blank for sqlite
18
19define ('POCHE_VERSION', '1.0-beta1');
20define ('MODE_DEMO', FALSE);
21define ('DEBUG_POCHE', TRUE);
22define ('CONVERT_LINKS_FOOTNOTES', FALSE);
23define ('REVERT_FORCED_PARAGRAPH_ELEMENTS', FALSE);
24define ('DOWNLOAD_PICTURES', FALSE);
25define ('SHARE_TWITTER', TRUE);
26define ('SHARE_MAIL', TRUE);
27define ('SALT', '464v54gLLw928uz4zUBqkRJeiPY68zCX');
28define ('ABS_PATH', 'assets/');
29define ('TPL', './tpl');
30define ('LOCALE', './locale');
31define ('CACHE', './cache');
32define ('LANG', 'en_EN.UTF8');
33define ('PAGINATION', '10');
34define ('THEME', 'light');
35
36# /!\ Be careful if you change the lines below /!\
37require_once './inc/poche/User.class.php';
38require_once './inc/poche/Tools.class.php';
39require_once './inc/poche/Url.class.php';
40require_once './inc/3rdparty/class.messages.php';
41require_once './inc/poche/Poche.class.php';
42require_once './inc/3rdparty/Readability.php';
43require_once './inc/3rdparty/Encoding.php';
44require_once './inc/poche/Database.class.php';
45require_once './vendor/autoload.php';
46require_once './inc/3rdparty/simple_html_dom.php';
47require_once './inc/3rdparty/paginator.php';
48require_once './inc/3rdparty/Session.class.php';
49
50if (DOWNLOAD_PICTURES) {
51 require_once './inc/poche/pochePictures.php';
52}
53
54$poche = new Poche();
55#XSRF protection with token
56// if (!empty($_POST)) {
57// if (!Session::isToken($_POST['token'])) {
58// die(_('Wrong token'));
59// }
60// unset($_SESSION['tokens']);
61// } \ No newline at end of file
diff --git a/inc/poche/pochePictures.php b/inc/poche/pochePictures.php
new file mode 100644
index 00000000..4e4a0b08
--- /dev/null
+++ b/inc/poche/pochePictures.php
@@ -0,0 +1,110 @@
1<?php
2/**
3 * poche, a read it later open source system
4 *
5 * @category poche
6 * @author Nicolas Lœuillet <support@inthepoche.com>
7 * @copyright 2013
8 * @license http://www.wtfpl.net/ see COPYING file
9 */
10
11/**
12 * On modifie les URLS des images dans le corps de l'article
13 */
14function filtre_picture($content, $url, $id)
15{
16 $matches = array();
17 preg_match_all('#<\s*(img)[^>]+src="([^"]*)"[^>]*>#Si', $content, $matches, PREG_SET_ORDER);
18 foreach($matches as $i => $link) {
19 $link[1] = trim($link[1]);
20 if (!preg_match('#^(([a-z]+://)|(\#))#', $link[1])) {
21 $absolute_path = get_absolute_link($link[2],$url);
22 $filename = basename(parse_url($absolute_path, PHP_URL_PATH));
23 $directory = create_assets_directory($id);
24 $fullpath = $directory . '/' . $filename;
25 download_pictures($absolute_path, $fullpath);
26 $content = str_replace($matches[$i][2], $fullpath, $content);
27 }
28
29 }
30
31 return $content;
32}
33
34/**
35 * Retourne le lien absolu
36 */
37function get_absolute_link($relative_link, $url) {
38 /* return if already absolute URL */
39 if (parse_url($relative_link, PHP_URL_SCHEME) != '') return $relative_link;
40
41 /* queries and anchors */
42 if ($relative_link[0]=='#' || $relative_link[0]=='?') return $url . $relative_link;
43
44 /* parse base URL and convert to local variables:
45 $scheme, $host, $path */
46 extract(parse_url($url));
47
48 /* remove non-directory element from path */
49 $path = preg_replace('#/[^/]*$#', '', $path);
50
51 /* destroy path if relative url points to root */
52 if ($relative_link[0] == '/') $path = '';
53
54 /* dirty absolute URL */
55 $abs = $host . $path . '/' . $relative_link;
56
57 /* replace '//' or '/./' or '/foo/../' with '/' */
58 $re = array('#(/\.?/)#', '#/(?!\.\.)[^/]+/\.\./#');
59 for($n=1; $n>0; $abs=preg_replace($re, '/', $abs, -1, $n)) {}
60
61 /* absolute URL is ready! */
62 return $scheme.'://'.$abs;
63}
64
65/**
66 * Téléchargement des images
67 */
68function download_pictures($absolute_path, $fullpath)
69{
70 $rawdata = Tools::getFile($absolute_path);
71
72 if(file_exists($fullpath)) {
73 unlink($fullpath);
74 }
75 $fp = fopen($fullpath, 'x');
76 fwrite($fp, $rawdata);
77 fclose($fp);
78}
79
80/**
81 * Crée un répertoire de médias pour l'article
82 */
83function create_assets_directory($id)
84{
85 $assets_path = ABS_PATH;
86 if(!is_dir($assets_path)) {
87 mkdir($assets_path, 0705);
88 }
89
90 $article_directory = $assets_path . $id;
91 if(!is_dir($article_directory)) {
92 mkdir($article_directory, 0705);
93 }
94
95 return $article_directory;
96}
97
98/**
99 * Suppression du répertoire d'images
100 */
101function remove_directory($directory)
102{
103 if(is_dir($directory)) {
104 $files = array_diff(scandir($directory), array('.','..'));
105 foreach ($files as $file) {
106 (is_dir("$directory/$file")) ? remove_directory("$directory/$file") : unlink("$directory/$file");
107 }
108 return rmdir($directory);
109 }
110} \ No newline at end of file
diff --git a/inc/rain.tpl.class.php b/inc/rain.tpl.class.php
deleted file mode 100644
index 6522c798..00000000
--- a/inc/rain.tpl.class.php
+++ /dev/null
@@ -1,1043 +0,0 @@
1<?php
2
3/**
4 * RainTPL
5 * -------
6 * Realized by Federico Ulfo & maintained by the Rain Team
7 * Distributed under GNU/LGPL 3 License
8 *
9 * @version 2.7.2
10 */
11
12
13class RainTPL{
14
15 // -------------------------
16 // CONFIGURATION
17 // -------------------------
18
19 /**
20 * Template directory
21 *
22 * @var string
23 */
24 static $tpl_dir = "tpl/";
25
26
27 /**
28 * Cache directory. Is the directory where RainTPL will compile the template and save the cache
29 *
30 * @var string
31 */
32 static $cache_dir = "tmp/";
33
34
35 /**
36 * Template base URL. RainTPL will add this URL to the relative paths of element selected in $path_replace_list.
37 *
38 * @var string
39 */
40 static $base_url = null;
41
42
43 /**
44 * Template extension.
45 *
46 * @var string
47 */
48 static $tpl_ext = "html";
49
50
51 /**
52 * 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="...">)
53 * Set true to enable the path replace.
54 *
55 * @var unknown_type
56 */
57 static $path_replace = true;
58
59
60 /**
61 * You can set what the path_replace method will replace.
62 * Avaible options: a, img, link, script, input
63 *
64 * @var array
65 */
66 static $path_replace_list = array( 'a', 'img', 'link', 'script', 'input' );
67
68
69 /**
70 * You can define in the black list what string are disabled into the template tags
71 *
72 * @var unknown_type
73 */
74 static $black_list = array( '\$this', 'raintpl::', 'self::', '_SESSION', '_SERVER', '_ENV', 'eval', 'exec', 'unlink', 'rmdir' );
75
76
77 /**
78 * Check template.
79 * true: checks template update time, if changed it compile them
80 * false: loads the compiled template. Set false if server doesn't have write permission for cache_directory.
81 *
82 */
83 static $check_template_update = true;
84
85
86 /**
87 * PHP tags <? ?>
88 * True: php tags are enabled into the template
89 * False: php tags are disabled into the template and rendered as html
90 *
91 * @var bool
92 */
93 static $php_enabled = true;
94
95
96 /**
97 * Debug mode flag.
98 * True: debug mode is used, syntax errors are displayed directly in template. Execution of script is not terminated.
99 * False: exception is thrown on found error.
100 *
101 * @var bool
102 */
103 static $debug = false;
104
105 // -------------------------
106
107
108 // -------------------------
109 // RAINTPL VARIABLES
110 // -------------------------
111
112 /**
113 * Is the array where RainTPL keep the variables assigned
114 *
115 * @var array
116 */
117 public $var = array();
118
119 protected $tpl = array(), // variables to keep the template directories and info
120 $cache = false, // static cache enabled / disabled
121 $cache_id = null; // identify only one cache
122
123 protected static $config_name_sum = array(); // takes all the config to create the md5 of the file
124
125 // -------------------------
126
127
128
129 const CACHE_EXPIRE_TIME = 3600; // default cache expire time = hour
130
131
132
133 /**
134 * Assign variable
135 * eg. $t->assign('name','mickey');
136 *
137 * @param mixed $variable_name Name of template variable or associative array name/value
138 * @param mixed $value value assigned to this variable. Not set if variable_name is an associative array
139 */
140
141 function assign( $variable, $value = null ){
142 if( is_array( $variable ) )
143 $this->var += $variable;
144 else
145 $this->var[ $variable ] = $value;
146 }
147
148
149
150 /**
151 * Draw the template
152 * eg. $html = $tpl->draw( 'demo', TRUE ); // return template in string
153 * or $tpl->draw( $tpl_name ); // echo the template
154 *
155 * @param string $tpl_name template to load
156 * @param boolean $return_string true=return a string, false=echo the template
157 * @return string
158 */
159
160 function draw( $tpl_name, $return_string = false ){
161
162 try {
163 // compile the template if necessary and set the template filepath
164 $this->check_template( $tpl_name );
165 } catch (RainTpl_Exception $e) {
166 $output = $this->printDebug($e);
167 die($output);
168 }
169
170 // Cache is off and, return_string is false
171 // Rain just echo the template
172
173 if( !$this->cache && !$return_string ){
174 extract( $this->var );
175 include $this->tpl['compiled_filename'];
176 unset( $this->tpl );
177 }
178
179
180 // cache or return_string are enabled
181 // rain get the output buffer to save the output in the cache or to return it as string
182
183 else{
184
185 //----------------------
186 // get the output buffer
187 //----------------------
188 ob_start();
189 extract( $this->var );
190 include $this->tpl['compiled_filename'];
191 $raintpl_contents = ob_get_clean();
192 //----------------------
193
194
195 // save the output in the cache
196 if( $this->cache )
197 file_put_contents( $this->tpl['cache_filename'], "<?php if(!class_exists('raintpl')){exit;}?>" . $raintpl_contents );
198
199 // free memory
200 unset( $this->tpl );
201
202 // return or print the template
203 if( $return_string ) return $raintpl_contents; else echo $raintpl_contents;
204
205 }
206
207 }
208
209
210
211 /**
212 * If exists a valid cache for this template it returns the cache
213 *
214 * @param string $tpl_name Name of template (set the same of draw)
215 * @param int $expiration_time Set after how many seconds the cache expire and must be regenerated
216 * @return string it return the HTML or null if the cache must be recreated
217 */
218
219 function cache( $tpl_name, $expire_time = self::CACHE_EXPIRE_TIME, $cache_id = null ){
220
221 // set the cache_id
222 $this->cache_id = $cache_id;
223
224 if( !$this->check_template( $tpl_name ) && file_exists( $this->tpl['cache_filename'] ) && ( time() - filemtime( $this->tpl['cache_filename'] ) < $expire_time ) )
225 return substr( file_get_contents( $this->tpl['cache_filename'] ), 43 );
226 else{
227 //delete the cache of the selected template
228 if (file_exists($this->tpl['cache_filename']))
229 unlink($this->tpl['cache_filename'] );
230 $this->cache = true;
231 }
232 }
233
234
235
236 /**
237 * Configure the settings of RainTPL
238 *
239 */
240 static function configure( $setting, $value = null ){
241 if( is_array( $setting ) )
242 foreach( $setting as $key => $value )
243 self::configure( $key, $value );
244 else if( property_exists( __CLASS__, $setting ) ){
245 self::$$setting = $value;
246 self::$config_name_sum[ $setting ] = $value; // take trace of all config
247 }
248 }
249
250
251
252 // check if has to compile the template
253 // return true if the template has changed
254 protected function check_template( $tpl_name ){
255
256 if( !isset($this->tpl['checked']) ){
257
258 $tpl_basename = basename( $tpl_name ); // template basename
259 $tpl_basedir = strpos($tpl_name,"/") ? dirname($tpl_name) . '/' : null; // template basedirectory
260 $tpl_dir = self::$tpl_dir . $tpl_basedir; // template directory
261 $this->tpl['tpl_filename'] = $tpl_dir . $tpl_basename . '.' . self::$tpl_ext; // template filename
262 $temp_compiled_filename = self::$cache_dir . $tpl_basename . "." . md5( $tpl_dir . serialize(self::$config_name_sum));
263 $this->tpl['compiled_filename'] = $temp_compiled_filename . '.rtpl.php'; // cache filename
264 $this->tpl['cache_filename'] = $temp_compiled_filename . '.s_' . $this->cache_id . '.rtpl.php'; // static cache filename
265
266 // if the template doesn't exsist throw an error
267 if( self::$check_template_update && !file_exists( $this->tpl['tpl_filename'] ) ){
268 $e = new RainTpl_NotFoundException( 'Template '. $tpl_basename .' not found!' );
269 throw $e->setTemplateFile($this->tpl['tpl_filename']);
270 }
271
272 // file doesn't exsist, or the template was updated, Rain will compile the template
273 if( !file_exists( $this->tpl['compiled_filename'] ) || ( self::$check_template_update && filemtime($this->tpl['compiled_filename']) < filemtime( $this->tpl['tpl_filename'] ) ) ){
274 $this->compileFile( $tpl_basename, $tpl_basedir, $this->tpl['tpl_filename'], self::$cache_dir, $this->tpl['compiled_filename'] );
275 return true;
276 }
277 $this->tpl['checked'] = true;
278 }
279 }
280
281
282 /**
283 * execute stripslaches() on the xml block. Invoqued by preg_replace_callback function below
284 * @access protected
285 */
286 protected function xml_reSubstitution($capture) {
287 return "<?php echo '<?xml ".stripslashes($capture[1])." ?>'; ?>";
288 }
289
290 /**
291 * Compile and write the compiled template file
292 * @access protected
293 */
294 protected function compileFile( $tpl_basename, $tpl_basedir, $tpl_filename, $cache_dir, $compiled_filename ){
295
296 //read template file
297 $this->tpl['source'] = $template_code = file_get_contents( $tpl_filename );
298
299 //xml substitution
300 $template_code = preg_replace( "/<\?xml(.*?)\?>/s", "##XML\\1XML##", $template_code );
301
302 //disable php tag
303 if( !self::$php_enabled )
304 $template_code = str_replace( array("<?","?>"), array("&lt;?","?&gt;"), $template_code );
305
306 //xml re-substitution
307 $template_code = preg_replace_callback ( "/##XML(.*?)XML##/s", array($this, 'xml_reSubstitution'), $template_code );
308
309 //compile template
310 $template_compiled = "<?php if(!class_exists('raintpl')){exit;}?>" . $this->compileTemplate( $template_code, $tpl_basedir );
311
312
313 // fix the php-eating-newline-after-closing-tag-problem
314 $template_compiled = str_replace( "?>\n", "?>\n\n", $template_compiled );
315
316 // create directories
317 if( !is_dir( $cache_dir ) )
318 mkdir( $cache_dir, 0755, true );
319
320 if( !is_writable( $cache_dir ) )
321 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/');
322
323 //write compiled file
324 file_put_contents( $compiled_filename, $template_compiled );
325 }
326
327
328
329 /**
330 * Compile template
331 * @access protected
332 */
333 protected function compileTemplate( $template_code, $tpl_basedir ){
334
335 //tag list
336 $tag_regexp = array( 'loop' => '(\{loop(?: name){0,1}="\${0,1}[^"]*"\})',
337 'loop_close' => '(\{\/loop\})',
338 'if' => '(\{if(?: condition){0,1}="[^"]*"\})',
339 'elseif' => '(\{elseif(?: condition){0,1}="[^"]*"\})',
340 'else' => '(\{else\})',
341 'if_close' => '(\{\/if\})',
342 'function' => '(\{function="[^"]*"\})',
343 'noparse' => '(\{noparse\})',
344 'noparse_close'=> '(\{\/noparse\})',
345 'ignore' => '(\{ignore\}|\{\*)',
346 'ignore_close' => '(\{\/ignore\}|\*\})',
347 'include' => '(\{include="[^"]*"(?: cache="[^"]*")?\})',
348 'template_info'=> '(\{\$template_info\})',
349 'function' => '(\{function="(\w*?)(?:.*?)"\})'
350 );
351
352 $tag_regexp = "/" . join( "|", $tag_regexp ) . "/";
353
354 //split the code with the tags regexp
355 $template_code = preg_split ( $tag_regexp, $template_code, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY );
356
357 //path replace (src of img, background and href of link)
358 $template_code = $this->path_replace( $template_code, $tpl_basedir );
359
360 //compile the code
361 $compiled_code = $this->compileCode( $template_code );
362
363 //return the compiled code
364 return $compiled_code;
365
366 }
367
368
369
370 /**
371 * Compile the code
372 * @access protected
373 */
374 protected function compileCode( $parsed_code ){
375
376 //variables initialization
377 $compiled_code = $open_if = $comment_is_open = $ignore_is_open = null;
378 $loop_level = 0;
379
380 //read all parsed code
381 while( $html = array_shift( $parsed_code ) ){
382
383 //close ignore tag
384 if( !$comment_is_open && ( strpos( $html, '{/ignore}' ) !== FALSE || strpos( $html, '*}' ) !== FALSE ) )
385 $ignore_is_open = false;
386
387 //code between tag ignore id deleted
388 elseif( $ignore_is_open ){
389 //ignore the code
390 }
391
392 //close no parse tag
393 elseif( strpos( $html, '{/noparse}' ) !== FALSE )
394 $comment_is_open = false;
395
396 //code between tag noparse is not compiled
397 elseif( $comment_is_open )
398 $compiled_code .= $html;
399
400 //ignore
401 elseif( strpos( $html, '{ignore}' ) !== FALSE || strpos( $html, '{*' ) !== FALSE )
402 $ignore_is_open = true;
403
404 //noparse
405 elseif( strpos( $html, '{noparse}' ) !== FALSE )
406 $comment_is_open = true;
407
408 //include tag
409 elseif( preg_match( '/\{include="([^"]*)"(?: cache="([^"]*)"){0,1}\}/', $html, $code ) ){
410
411 //variables substitution
412 $include_var = $this->var_replace( $code[ 1 ], $left_delimiter = null, $right_delimiter = null, $php_left_delimiter = '".' , $php_right_delimiter = '."', $loop_level );
413
414 // if the cache is active
415 if( isset($code[ 2 ]) ){
416
417 //dynamic include
418 $compiled_code .= '<?php $tpl = new '.get_class($this).';' .
419 'if( $cache = $tpl->cache( $template = basename("'.$include_var.'") ) )' .
420 ' echo $cache;' .
421 'else{' .
422 ' $tpl_dir_temp = self::$tpl_dir;' .
423 ' $tpl->assign( $this->var );' .
424 ( !$loop_level ? null : '$tpl->assign( "key", $key'.$loop_level.' ); $tpl->assign( "value", $value'.$loop_level.' );' ).
425 ' $tpl->draw( dirname("'.$include_var.'") . ( substr("'.$include_var.'",-1,1) != "/" ? "/" : "" ) . basename("'.$include_var.'") );'.
426 '} ?>';
427 }
428 else{
429
430 //dynamic include
431 $compiled_code .= '<?php $tpl = new '.get_class($this).';' .
432 '$tpl_dir_temp = self::$tpl_dir;' .
433 '$tpl->assign( $this->var );' .
434 ( !$loop_level ? null : '$tpl->assign( "key", $key'.$loop_level.' ); $tpl->assign( "value", $value'.$loop_level.' );' ).
435 '$tpl->draw( dirname("'.$include_var.'") . ( substr("'.$include_var.'",-1,1) != "/" ? "/" : "" ) . basename("'.$include_var.'") );'.
436 '?>';
437
438
439 }
440
441 }
442
443 //loop
444 elseif( preg_match( '/\{loop(?: name){0,1}="\${0,1}([^"]*)"\}/', $html, $code ) ){
445
446 //increase the loop counter
447 $loop_level++;
448
449 //replace the variable in the loop
450 $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 );
451
452 //loop variables
453 $counter = "\$counter$loop_level"; // count iteration
454 $key = "\$key$loop_level"; // key
455 $value = "\$value$loop_level"; // value
456
457 //loop code
458 $compiled_code .= "<?php $counter=-1; if( isset($var) && is_array($var) && sizeof($var) ) foreach( $var as $key => $value ){ $counter++; ?>";
459
460 }
461
462 //close loop tag
463 elseif( strpos( $html, '{/loop}' ) !== FALSE ) {
464
465 //iterator
466 $counter = "\$counter$loop_level";
467
468 //decrease the loop counter
469 $loop_level--;
470
471 //close loop code
472 $compiled_code .= "<?php } ?>";
473
474 }
475
476 //if
477 elseif( preg_match( '/\{if(?: condition){0,1}="([^"]*)"\}/', $html, $code ) ){
478
479 //increase open if counter (for intendation)
480 $open_if++;
481
482 //tag
483 $tag = $code[ 0 ];
484
485 //condition attribute
486 $condition = $code[ 1 ];
487
488 // check if there's any function disabled by black_list
489 $this->function_check( $tag );
490
491 //variable substitution into condition (no delimiter into the condition)
492 $parsed_condition = $this->var_replace( $condition, $tag_left_delimiter = null, $tag_right_delimiter = null, $php_left_delimiter = null, $php_right_delimiter = null, $loop_level );
493
494 //if code
495 $compiled_code .= "<?php if( $parsed_condition ){ ?>";
496
497 }
498
499 //elseif
500 elseif( preg_match( '/\{elseif(?: condition){0,1}="([^"]*)"\}/', $html, $code ) ){
501
502 //tag
503 $tag = $code[ 0 ];
504
505 //condition attribute
506 $condition = $code[ 1 ];
507
508 //variable substitution into condition (no delimiter into the condition)
509 $parsed_condition = $this->var_replace( $condition, $tag_left_delimiter = null, $tag_right_delimiter = null, $php_left_delimiter = null, $php_right_delimiter = null, $loop_level );
510
511 //elseif code
512 $compiled_code .= "<?php }elseif( $parsed_condition ){ ?>";
513 }
514
515 //else
516 elseif( strpos( $html, '{else}' ) !== FALSE ) {
517
518 //else code
519 $compiled_code .= '<?php }else{ ?>';
520
521 }
522
523 //close if tag
524 elseif( strpos( $html, '{/if}' ) !== FALSE ) {
525
526 //decrease if counter
527 $open_if--;
528
529 // close if code
530 $compiled_code .= '<?php } ?>';
531
532 }
533
534 //function
535 elseif( preg_match( '/\{function="(\w*)(.*?)"\}/', $html, $code ) ){
536
537 //tag
538 $tag = $code[ 0 ];
539
540 //function
541 $function = $code[ 1 ];
542
543 // check if there's any function disabled by black_list
544 $this->function_check( $tag );
545
546 if( empty( $code[ 2 ] ) )
547 $parsed_function = $function . "()";
548 else
549 // parse the function
550 $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 );
551
552 //if code
553 $compiled_code .= "<?php echo $parsed_function; ?>";
554 }
555
556 // show all vars
557 elseif ( strpos( $html, '{$template_info}' ) !== FALSE ) {
558
559 //tag
560 $tag = '{$template_info}';
561
562 //if code
563 $compiled_code .= '<?php echo "<pre>"; print_r( $this->var ); echo "</pre>"; ?>';
564 }
565
566
567 //all html code
568 else{
569
570 //variables substitution (es. {$title})
571 $html = $this->var_replace( $html, $left_delimiter = '\{', $right_delimiter = '\}', $php_left_delimiter = '<?php ', $php_right_delimiter = ';?>', $loop_level, $echo = true );
572 //const substitution (es. {#CONST#})
573 $html = $this->const_replace( $html, $left_delimiter = '\{', $right_delimiter = '\}', $php_left_delimiter = '<?php ', $php_right_delimiter = ';?>', $loop_level, $echo = true );
574 //functions substitution (es. {"string"|functions})
575 $compiled_code .= $this->func_replace( $html, $left_delimiter = '\{', $right_delimiter = '\}', $php_left_delimiter = '<?php ', $php_right_delimiter = ';?>', $loop_level, $echo = true );
576 }
577 }
578
579 if( $open_if > 0 ) {
580 $e = new RainTpl_SyntaxException('Error! You need to close an {if} tag in ' . $this->tpl['tpl_filename'] . ' template');
581 throw $e->setTemplateFile($this->tpl['tpl_filename']);
582 }
583 return $compiled_code;
584 }
585
586
587 /**
588 * Reduce a path, eg. www/library/../filepath//file => www/filepath/file
589 * @param type $path
590 * @return type
591 */
592 protected function reduce_path( $path ){
593 $path = str_replace( "://", "@not_replace@", $path );
594 $path = str_replace( "//", "/", $path );
595 $path = str_replace( "@not_replace@", "://", $path );
596 return preg_replace('/\w+\/\.\.\//', '', $path );
597 }
598
599
600
601 /**
602 * replace the path of image src, link href and a href.
603 * url => template_dir/url
604 * url# => url
605 * http://url => http://url
606 *
607 * @param string $html
608 * @return string html sostituito
609 */
610 protected function path_replace( $html, $tpl_basedir ){
611
612 if( self::$path_replace ){
613
614 $tpl_dir = self::$base_url . self::$tpl_dir . $tpl_basedir;
615
616 // reduce the path
617 $path = $this->reduce_path($tpl_dir);
618
619 $exp = $sub = array();
620
621 if( in_array( "img", self::$path_replace_list ) ){
622 $exp = array( '/<img(.*?)src=(?:")(http|https)\:\/\/([^"]+?)(?:")/i', '/<img(.*?)src=(?:")([^"]+?)#(?:")/i', '/<img(.*?)src="(.*?)"/', '/<img(.*?)src=(?:\@)([^"]+?)(?:\@)/i' );
623 $sub = array( '<img$1src=@$2://$3@', '<img$1src=@$2@', '<img$1src="' . $path . '$2"', '<img$1src="$2"' );
624 }
625
626 if( in_array( "script", self::$path_replace_list ) ){
627 $exp = array_merge( $exp , array( '/<script(.*?)src=(?:")(http|https)\:\/\/([^"]+?)(?:")/i', '/<script(.*?)src=(?:")([^"]+?)#(?:")/i', '/<script(.*?)src="(.*?)"/', '/<script(.*?)src=(?:\@)([^"]+?)(?:\@)/i' ) );
628 $sub = array_merge( $sub , array( '<script$1src=@$2://$3@', '<script$1src=@$2@', '<script$1src="' . $path . '$2"', '<script$1src="$2"' ) );
629 }
630
631 if( in_array( "link", self::$path_replace_list ) ){
632 $exp = array_merge( $exp , array( '/<link(.*?)href=(?:")(http|https)\:\/\/([^"]+?)(?:")/i', '/<link(.*?)href=(?:")([^"]+?)#(?:")/i', '/<link(.*?)href="(.*?)"/', '/<link(.*?)href=(?:\@)([^"]+?)(?:\@)/i' ) );
633 $sub = array_merge( $sub , array( '<link$1href=@$2://$3@', '<link$1href=@$2@' , '<link$1href="' . $path . '$2"', '<link$1href="$2"' ) );
634 }
635
636 if( in_array( "a", self::$path_replace_list ) ){
637 $exp = array_merge( $exp , array( '/<a(.*?)href=(?:")(http\:\/\/|https\:\/\/|javascript:)([^"]+?)(?:")/i', '/<a(.*?)href="(.*?)"/', '/<a(.*?)href=(?:\@)([^"]+?)(?:\@)/i' ) );
638 $sub = array_merge( $sub , array( '<a$1href=@$2$3@', '<a$1href="' . self::$base_url . '$2"', '<a$1href="$2"' ) );
639 }
640
641 if( in_array( "input", self::$path_replace_list ) ){
642 $exp = array_merge( $exp , array( '/<input(.*?)src=(?:")(http|https)\:\/\/([^"]+?)(?:")/i', '/<input(.*?)src=(?:")([^"]+?)#(?:")/i', '/<input(.*?)src="(.*?)"/', '/<input(.*?)src=(?:\@)([^"]+?)(?:\@)/i' ) );
643 $sub = array_merge( $sub , array( '<input$1src=@$2://$3@', '<input$1src=@$2@', '<input$1src="' . $path . '$2"', '<input$1src="$2"' ) );
644 }
645
646 return preg_replace( $exp, $sub, $html );
647
648 }
649 else
650 return $html;
651
652 }
653
654
655
656
657
658 // replace const
659 function const_replace( $html, $tag_left_delimiter, $tag_right_delimiter, $php_left_delimiter = null, $php_right_delimiter = null, $loop_level = null, $echo = null ){
660 // const
661 return preg_replace( '/\{\#(\w+)\#{0,1}\}/', $php_left_delimiter . ( $echo ? " echo " : null ) . '\\1' . $php_right_delimiter, $html );
662 }
663
664
665
666 // replace functions/modifiers on constants and strings
667 function func_replace( $html, $tag_left_delimiter, $tag_right_delimiter, $php_left_delimiter = null, $php_right_delimiter = null, $loop_level = null, $echo = null ){
668
669 preg_match_all( '/' . '\{\#{0,1}(\"{0,1}.*?\"{0,1})(\|\w.*?)\#{0,1}\}' . '/', $html, $matches );
670
671 for( $i=0, $n=count($matches[0]); $i<$n; $i++ ){
672
673 //complete tag ex: {$news.title|substr:0,100}
674 $tag = $matches[ 0 ][ $i ];
675
676 //variable name ex: news.title
677 $var = $matches[ 1 ][ $i ];
678
679 //function and parameters associate to the variable ex: substr:0,100
680 $extra_var = $matches[ 2 ][ $i ];
681
682 // check if there's any function disabled by black_list
683 $this->function_check( $tag );
684
685 $extra_var = $this->var_replace( $extra_var, null, null, null, null, $loop_level );
686
687
688 // check if there's an operator = in the variable tags, if there's this is an initialization so it will not output any value
689 $is_init_variable = preg_match( "/^(\s*?)\=[^=](.*?)$/", $extra_var );
690
691 //function associate to variable
692 $function_var = ( $extra_var and $extra_var[0] == '|') ? substr( $extra_var, 1 ) : null;
693
694 //variable path split array (ex. $news.title o $news[title]) or object (ex. $news->title)
695 $temp = preg_split( "/\.|\[|\-\>/", $var );
696
697 //variable name
698 $var_name = $temp[ 0 ];
699
700 //variable path
701 $variable_path = substr( $var, strlen( $var_name ) );
702
703 //parentesis transform [ e ] in [" e in "]
704 $variable_path = str_replace( '[', '["', $variable_path );
705 $variable_path = str_replace( ']', '"]', $variable_path );
706
707 //transform .$variable in ["$variable"]
708 $variable_path = preg_replace('/\.\$(\w+)/', '["$\\1"]', $variable_path );
709
710 //transform [variable] in ["variable"]
711 $variable_path = preg_replace('/\.(\w+)/', '["\\1"]', $variable_path );
712
713 //if there's a function
714 if( $function_var ){
715
716 // check if there's a function or a static method and separate, function by parameters
717 $function_var = str_replace("::", "@double_dot@", $function_var );
718
719 // get the position of the first :
720 if( $dot_position = strpos( $function_var, ":" ) ){
721
722 // get the function and the parameters
723 $function = substr( $function_var, 0, $dot_position );
724 $params = substr( $function_var, $dot_position+1 );
725
726 }
727 else{
728
729 //get the function
730 $function = str_replace( "@double_dot@", "::", $function_var );
731 $params = null;
732
733 }
734
735 // replace back the @double_dot@ with ::
736 $function = str_replace( "@double_dot@", "::", $function );
737 $params = str_replace( "@double_dot@", "::", $params );
738
739
740 }
741 else
742 $function = $params = null;
743
744 $php_var = $var_name . $variable_path;
745
746 // compile the variable for php
747 if( isset( $function ) ){
748 if( $php_var )
749 $php_var = $php_left_delimiter . ( !$is_init_variable && $echo ? 'echo ' : null ) . ( $params ? "( $function( $php_var, $params ) )" : "$function( $php_var )" ) . $php_right_delimiter;
750 else
751 $php_var = $php_left_delimiter . ( !$is_init_variable && $echo ? 'echo ' : null ) . ( $params ? "( $function( $params ) )" : "$function()" ) . $php_right_delimiter;
752 }
753 else
754 $php_var = $php_left_delimiter . ( !$is_init_variable && $echo ? 'echo ' : null ) . $php_var . $extra_var . $php_right_delimiter;
755
756 $html = str_replace( $tag, $php_var, $html );
757
758 }
759
760 return $html;
761
762 }
763
764
765
766 function var_replace( $html, $tag_left_delimiter, $tag_right_delimiter, $php_left_delimiter = null, $php_right_delimiter = null, $loop_level = null, $echo = null ){
767
768 //all variables
769 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 ) ){
770
771 for( $parsed=array(), $i=0, $n=count($matches[0]); $i<$n; $i++ )
772 $parsed[$matches[0][$i]] = array('var'=>$matches[1][$i],'extra_var'=>$matches[2][$i]);
773
774 foreach( $parsed as $tag => $array ){
775
776 //variable name ex: news.title
777 $var = $array['var'];
778
779 //function and parameters associate to the variable ex: substr:0,100
780 $extra_var = $array['extra_var'];
781
782 // check if there's any function disabled by black_list
783 $this->function_check( $tag );
784
785 $extra_var = $this->var_replace( $extra_var, null, null, null, null, $loop_level );
786
787 // check if there's an operator = in the variable tags, if there's this is an initialization so it will not output any value
788 $is_init_variable = preg_match( "/^[a-z_A-Z\.\[\](\-\>)]*=[^=]*$/", $extra_var );
789
790 //function associate to variable
791 $function_var = ( $extra_var and $extra_var[0] == '|') ? substr( $extra_var, 1 ) : null;
792
793 //variable path split array (ex. $news.title o $news[title]) or object (ex. $news->title)
794 $temp = preg_split( "/\.|\[|\-\>/", $var );
795
796 //variable name
797 $var_name = $temp[ 0 ];
798
799 //variable path
800 $variable_path = substr( $var, strlen( $var_name ) );
801
802 //parentesis transform [ e ] in [" e in "]
803 $variable_path = str_replace( '[', '["', $variable_path );
804 $variable_path = str_replace( ']', '"]', $variable_path );
805
806 //transform .$variable in ["$variable"] and .variable in ["variable"]
807 $variable_path = preg_replace('/\.(\${0,1}\w+)/', '["\\1"]', $variable_path );
808
809 // if is an assignment also assign the variable to $this->var['value']
810 if( $is_init_variable )
811 $extra_var = "=\$this->var['{$var_name}']{$variable_path}" . $extra_var;
812
813
814
815 //if there's a function
816 if( $function_var ){
817
818 // check if there's a function or a static method and separate, function by parameters
819 $function_var = str_replace("::", "@double_dot@", $function_var );
820
821
822 // get the position of the first :
823 if( $dot_position = strpos( $function_var, ":" ) ){
824
825 // get the function and the parameters
826 $function = substr( $function_var, 0, $dot_position );
827 $params = substr( $function_var, $dot_position+1 );
828
829 }
830 else{
831
832 //get the function
833 $function = str_replace( "@double_dot@", "::", $function_var );
834 $params = null;
835
836 }
837
838 // replace back the @double_dot@ with ::
839 $function = str_replace( "@double_dot@", "::", $function );
840 $params = str_replace( "@double_dot@", "::", $params );
841 }
842 else
843 $function = $params = null;
844
845 //if it is inside a loop
846 if( $loop_level ){
847 //verify the variable name
848 if( $var_name == 'key' )
849 $php_var = '$key' . $loop_level;
850 elseif( $var_name == 'value' )
851 $php_var = '$value' . $loop_level . $variable_path;
852 elseif( $var_name == 'counter' )
853 $php_var = '$counter' . $loop_level;
854 else
855 $php_var = '$' . $var_name . $variable_path;
856 }else
857 $php_var = '$' . $var_name . $variable_path;
858
859 // compile the variable for php
860 if( isset( $function ) )
861 $php_var = $php_left_delimiter . ( !$is_init_variable && $echo ? 'echo ' : null ) . ( $params ? "( $function( $php_var, $params ) )" : "$function( $php_var )" ) . $php_right_delimiter;
862 else
863 $php_var = $php_left_delimiter . ( !$is_init_variable && $echo ? 'echo ' : null ) . $php_var . $extra_var . $php_right_delimiter;
864
865 $html = str_replace( $tag, $php_var, $html );
866
867
868 }
869 }
870
871 return $html;
872 }
873
874
875
876 /**
877 * Check if function is in black list (sandbox)
878 *
879 * @param string $code
880 * @param string $tag
881 */
882 protected function function_check( $code ){
883
884 $preg = '#(\W|\s)' . implode( '(\W|\s)|(\W|\s)', self::$black_list ) . '(\W|\s)#';
885
886 // check if the function is in the black list (or not in white list)
887 if( count(self::$black_list) && preg_match( $preg, $code, $match ) ){
888
889 // find the line of the error
890 $line = 0;
891 $rows=explode("\n",$this->tpl['source']);
892 while( !strpos($rows[$line],$code) )
893 $line++;
894
895 // stop the execution of the script
896 $e = new RainTpl_SyntaxException('Unallowed syntax in ' . $this->tpl['tpl_filename'] . ' template');
897 throw $e->setTemplateFile($this->tpl['tpl_filename'])
898 ->setTag($code)
899 ->setTemplateLine($line);
900 }
901
902 }
903
904 /**
905 * Prints debug info about exception or passes it further if debug is disabled.
906 *
907 * @param RainTpl_Exception $e
908 * @return string
909 */
910 protected function printDebug(RainTpl_Exception $e){
911 if (!self::$debug) {
912 throw $e;
913 }
914 $output = sprintf('<h2>Exception: %s</h2><h3>%s</h3><p>template: %s</p>',
915 get_class($e),
916 $e->getMessage(),
917 $e->getTemplateFile()
918 );
919 if ($e instanceof RainTpl_SyntaxException) {
920 if (null != $e->getTemplateLine()) {
921 $output .= '<p>line: ' . $e->getTemplateLine() . '</p>';
922 }
923 if (null != $e->getTag()) {
924 $output .= '<p>in tag: ' . htmlspecialchars($e->getTag()) . '</p>';
925 }
926 if (null != $e->getTemplateLine() && null != $e->getTag()) {
927 $rows=explode("\n", htmlspecialchars($this->tpl['source']));
928 $rows[$e->getTemplateLine()] = '<font color=red>' . $rows[$e->getTemplateLine()] . '</font>';
929 $output .= '<h3>template code</h3>' . implode('<br />', $rows) . '</pre>';
930 }
931 }
932 $output .= sprintf('<h3>trace</h3><p>In %s on line %d</p><pre>%s</pre>',
933 $e->getFile(), $e->getLine(),
934 nl2br(htmlspecialchars($e->getTraceAsString()))
935 );
936 return $output;
937 }
938}
939
940
941/**
942 * Basic Rain tpl exception.
943 */
944class RainTpl_Exception extends Exception{
945 /**
946 * Path of template file with error.
947 */
948 protected $templateFile = '';
949
950 /**
951 * Returns path of template file with error.
952 *
953 * @return string
954 */
955 public function getTemplateFile()
956 {
957 return $this->templateFile;
958 }
959
960 /**
961 * Sets path of template file with error.
962 *
963 * @param string $templateFile
964 * @return RainTpl_Exception
965 */
966 public function setTemplateFile($templateFile)
967 {
968 $this->templateFile = (string) $templateFile;
969 return $this;
970 }
971}
972
973/**
974 * Exception thrown when template file does not exists.
975 */
976class RainTpl_NotFoundException extends RainTpl_Exception{
977}
978
979/**
980 * Exception thrown when syntax error occurs.
981 */
982class RainTpl_SyntaxException extends RainTpl_Exception{
983 /**
984 * Line in template file where error has occured.
985 *
986 * @var int | null
987 */
988 protected $templateLine = null;
989
990 /**
991 * Tag which caused an error.
992 *
993 * @var string | null
994 */
995 protected $tag = null;
996
997 /**
998 * Returns line in template file where error has occured
999 * or null if line is not defined.
1000 *
1001 * @return int | null
1002 */
1003 public function getTemplateLine()
1004 {
1005 return $this->templateLine;
1006 }
1007
1008 /**
1009 * Sets line in template file where error has occured.
1010 *
1011 * @param int $templateLine
1012 * @return RainTpl_SyntaxException
1013 */
1014 public function setTemplateLine($templateLine)
1015 {
1016 $this->templateLine = (int) $templateLine;
1017 return $this;
1018 }
1019
1020 /**
1021 * Returns tag which caused an error.
1022 *
1023 * @return string
1024 */
1025 public function getTag()
1026 {
1027 return $this->tag;
1028 }
1029
1030 /**
1031 * Sets tag which caused an error.
1032 *
1033 * @param string $tag
1034 * @return RainTpl_SyntaxException
1035 */
1036 public function setTag($tag)
1037 {
1038 $this->tag = (string) $tag;
1039 return $this;
1040 }
1041}
1042
1043// -- end
diff --git a/inc/store/file.class.php b/inc/store/file.class.php
deleted file mode 100644
index ad20937d..00000000
--- a/inc/store/file.class.php
+++ /dev/null
@@ -1,51 +0,0 @@
1<?php
2/**
3 * poche, a read it later open source system
4 *
5 * @category poche
6 * @author Nicolas Lœuillet <support@inthepoche.com>
7 * @copyright 2013
8 * @license http://www.wtfpl.net/ see COPYING file
9 */
10
11class File extends Store {
12 function __construct() {
13
14 }
15
16 public function add() {
17
18 }
19
20 public function retrieveOneById($id) {
21
22 }
23
24 public function retrieveOneByURL($url) {
25
26 }
27
28 public function deleteById($id) {
29
30 }
31
32 public function favoriteById($id) {
33
34 }
35
36 public function archiveById($id) {
37
38 }
39
40 public function getEntriesByView($view) {
41
42 }
43
44 public function getLastId() {
45
46 }
47
48 public function updateContentById($id) {
49
50 }
51}
diff --git a/inc/store/sqlite.class.php b/inc/store/sqlite.class.php
deleted file mode 100644
index 21081608..00000000
--- a/inc/store/sqlite.class.php
+++ /dev/null
@@ -1,202 +0,0 @@
1<?php
2/**
3 * poche, a read it later open source system
4 *
5 * @category poche
6 * @author Nicolas Lœuillet <support@inthepoche.com>
7 * @copyright 2013
8 * @license http://www.wtfpl.net/ see COPYING file
9 */
10
11class Sqlite extends Store {
12
13 public static $db_path = 'sqlite:./db/poche.sqlite';
14 var $handle;
15
16 function __construct() {
17 parent::__construct();
18
19 $this->handle = new PDO(self::$db_path);
20 $this->handle->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
21 }
22
23 private function getHandle() {
24 return $this->handle;
25 }
26
27 public function isInstalled() {
28 $sql = "SELECT name FROM sqlite_sequence WHERE name=?";
29 $query = $this->executeQuery($sql, array('config'));
30 $hasConfig = $query->fetchAll();
31
32 if (count($hasConfig) == 0)
33 return FALSE;
34
35 if (!$this->getLogin() || !$this->getPassword())
36 return FALSE;
37
38 return TRUE;
39 }
40
41 public function install($login, $password) {
42 $this->getHandle()->exec('CREATE TABLE IF NOT EXISTS "config" ("id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL UNIQUE , "name" VARCHAR UNIQUE, "value" BLOB)');
43
44 $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)');
45
46 if (!$this->getLogin()) {
47 $sql_login = 'INSERT INTO config ( name, value ) VALUES (?, ?)';
48 $params_login = array('login', $login);
49 $query = $this->executeQuery($sql_login, $params_login);
50 }
51
52 if (!$this->getPassword()) {
53 $sql_pass = 'INSERT INTO config ( name, value ) VALUES (?, ?)';
54 $params_pass = array('password', $password);
55 $query = $this->executeQuery($sql_pass, $params_pass);
56 }
57
58 return TRUE;
59 }
60
61 public function getLogin() {
62 $sql = "SELECT value FROM config WHERE name=?";
63 $query = $this->executeQuery($sql, array('login'));
64 $login = $query->fetchAll();
65
66 return isset($login[0]['value']) ? $login[0]['value'] : FALSE;
67 }
68
69 public function getPassword() {
70 $sql = "SELECT value FROM config WHERE name=?";
71 $query = $this->executeQuery($sql, array('password'));
72 $pass = $query->fetchAll();
73
74 return isset($pass[0]['value']) ? $pass[0]['value'] : FALSE;
75 }
76
77 public function updatePassword($password)
78 {
79 $sql_update = "UPDATE config SET value=? WHERE name='password'";
80 $params_update = array($password);
81 $query = $this->executeQuery($sql_update, $params_update);
82 }
83
84 private function executeQuery($sql, $params) {
85 try
86 {
87 $query = $this->getHandle()->prepare($sql);
88 $query->execute($params);
89 return $query;
90 }
91 catch (Exception $e)
92 {
93 logm('execute query error : '.$e->getMessage());
94 }
95 }
96
97 public function retrieveAll() {
98 $sql = "SELECT * FROM entries ORDER BY id";
99 $query = $this->executeQuery($sql, array());
100 $entries = $query->fetchAll();
101
102 return $entries;
103 }
104
105 public function retrieveOneById($id) {
106 parent::__construct();
107
108 $entry = NULL;
109 $sql = "SELECT * FROM entries WHERE id=?";
110 $params = array(intval($id));
111 $query = $this->executeQuery($sql, $params);
112 $entry = $query->fetchAll();
113
114 return $entry[0];
115 }
116
117 public function getEntriesByView($view) {
118 parent::__construct();
119
120 switch ($_SESSION['sort'])
121 {
122 case 'ia':
123 $order = 'ORDER BY id';
124 break;
125 case 'id':
126 $order = 'ORDER BY id DESC';
127 break;
128 case 'ta':
129 $order = 'ORDER BY lower(title)';
130 break;
131 case 'td':
132 $order = 'ORDER BY lower(title) DESC';
133 break;
134 default:
135 $order = 'ORDER BY id';
136 break;
137 }
138
139 switch ($view)
140 {
141 case 'archive':
142 $sql = "SELECT * FROM entries WHERE is_read=? " . $order;
143 $params = array(-1);
144 break;
145 case 'fav' :
146 $sql = "SELECT * FROM entries WHERE is_fav=? " . $order;
147 $params = array(-1);
148 break;
149 default:
150 $sql = "SELECT * FROM entries WHERE is_read=? " . $order;
151 $params = array(0);
152 break;
153 }
154
155 $query = $this->executeQuery($sql, $params);
156 $entries = $query->fetchAll();
157
158 return $entries;
159 }
160
161 public function add($url, $title, $content) {
162 parent::__construct();
163 $sql_action = 'INSERT INTO entries ( url, title, content ) VALUES (?, ?, ?)';
164 $params_action = array($url, $title, $content);
165 $query = $this->executeQuery($sql_action, $params_action);
166 return $query;
167 }
168
169 public function deleteById($id) {
170 parent::__construct();
171 $sql_action = "DELETE FROM entries WHERE id=?";
172 $params_action = array($id);
173 $query = $this->executeQuery($sql_action, $params_action);
174 return $query;
175 }
176
177 public function favoriteById($id) {
178 parent::__construct();
179 $sql_action = "UPDATE entries SET is_fav=~is_fav WHERE id=?";
180 $params_action = array($id);
181 $query = $this->executeQuery($sql_action, $params_action);
182 }
183
184 public function archiveById($id) {
185 parent::__construct();
186 $sql_action = "UPDATE entries SET is_read=~is_read WHERE id=?";
187 $params_action = array($id);
188 $query = $this->executeQuery($sql_action, $params_action);
189 }
190
191 public function getLastId() {
192 parent::__construct();
193 return $this->getHandle()->lastInsertId();
194 }
195
196 public function updateContentById($id) {
197 parent::__construct();
198 $sql_update = "UPDATE entries SET content=? WHERE id=?";
199 $params_update = array($content, $id);
200 $query = $this->executeQuery($sql_update, $params_update);
201 }
202}
diff --git a/inc/store/store.class.php b/inc/store/store.class.php
deleted file mode 100644
index dd7d4cfe..00000000
--- a/inc/store/store.class.php
+++ /dev/null
@@ -1,63 +0,0 @@
1<?php
2/**
3 * poche, a read it later open source system
4 *
5 * @category poche
6 * @author Nicolas Lœuillet <support@inthepoche.com>
7 * @copyright 2013
8 * @license http://www.wtfpl.net/ see COPYING file
9 */
10
11class Store {
12 function __construct() {
13
14 }
15
16 public function getLogin() {
17
18 }
19
20 public function getPassword() {
21
22 }
23
24 public function add() {
25
26 }
27
28 public function retrieveAll() {
29
30 }
31
32 public function retrieveOneById($id) {
33
34 }
35
36 public function retrieveOneByURL($url) {
37
38 }
39
40 public function deleteById($id) {
41
42 }
43
44 public function favoriteById($id) {
45
46 }
47
48 public function archiveById($id) {
49
50 }
51
52 public function getEntriesByView($view) {
53
54 }
55
56 public function getLastId() {
57
58 }
59
60 public function updateContentById($id) {
61
62 }
63}
diff --git a/index.php b/index.php
index 0a778d08..a60e4e94 100644
--- a/index.php
+++ b/index.php
@@ -8,83 +8,57 @@
8 * @license http://www.wtfpl.net/ see COPYING file 8 * @license http://www.wtfpl.net/ see COPYING file
9 */ 9 */
10 10
11include dirname(__FILE__).'/inc/config.php'; 11include dirname(__FILE__).'/inc/poche/config.inc.php';
12 12
13myTool::initPhp(); 13# Parse GET & REFERER vars
14 14$referer = empty($_SERVER['HTTP_REFERER']) ? '' : $_SERVER['HTTP_REFERER'];
15# XSRF protection with token 15$view = Tools::checkVar('view', 'home');
16if (!empty($_POST)) { 16$action = Tools::checkVar('action');
17 if (!Session::isToken($_POST['token'])) { 17$id = Tools::checkVar('id');
18 die('Wrong token.'); 18$_SESSION['sort'] = Tools::checkVar('sort', 'id');
19 } 19$url = new Url((isset ($_GET['url'])) ? $_GET['url'] : '');
20 unset($_SESSION['tokens']);
21}
22
23$ref = empty($_SERVER['HTTP_REFERER']) ? '' : $_SERVER['HTTP_REFERER'];
24 20
21# poche actions
25if (isset($_GET['login'])) { 22if (isset($_GET['login'])) {
26 // Login 23 # hello you
27 if (!empty($_POST['login']) && !empty($_POST['password'])) { 24 $poche->login($referer);
28 if (Session::login($_SESSION['login'], $_SESSION['pass'], $_POST['login'], encode_string($_POST['password'] . $_POST['login']))) {
29 logm('login successful');
30 $msg->add('s', 'welcome in your poche!');
31 if (!empty($_POST['longlastingsession'])) {
32 $_SESSION['longlastingsession'] = 31536000;
33 $_SESSION['expires_on'] = time() + $_SESSION['longlastingsession'];
34 session_set_cookie_params($_SESSION['longlastingsession']);
35 } else {
36 session_set_cookie_params(0); // when browser closes
37 }
38 session_regenerate_id(true);
39
40 MyTool::redirect($ref);
41 }
42 logm('login failed');
43 die("Login failed !");
44 } else {
45 logm('login failed');
46 }
47} 25}
48elseif (isset($_GET['logout'])) { 26elseif (isset($_GET['logout'])) {
49 logm('logout'); 27 # see you soon !
50 Session::logout(); 28 $poche->logout();
51 MyTool::redirect();
52} 29}
53elseif (isset($_GET['config'])) { 30elseif (isset($_GET['config'])) {
54 if (isset($_POST['password']) && isset($_POST['password_repeat'])) { 31 # Update password
55 if ($_POST['password'] == $_POST['password_repeat'] && $_POST['password'] != "") { 32 $poche->updatePassword();
56 logm('password updated'); 33}
57 if (!DEMO) { 34elseif (isset($_GET['import'])) {
58 $store->updatePassword(encode_string($_POST['password'] . $_SESSION['login'])); 35 $import = $poche->import($_GET['from']);
59 $msg->add('s', 'your password has been updated'); 36}
60 } 37elseif (isset($_GET['export'])) {
61 else { 38 $poche->export();
62 $msg->add('i', 'in demo mode, you can\'t update password');
63 }
64 }
65 else
66 $msg->add('e', 'your password can\'t be empty and you have to repeat it in the second field');
67 }
68} 39}
69 40
70# Traitement des paramètres et déclenchement des actions 41# vars to send to templates
71$view = (isset ($_REQUEST['view'])) ? htmlentities($_REQUEST['view']) : 'index'; 42$tpl_vars = array(
72$full_head = (isset ($_REQUEST['full_head'])) ? htmlentities($_REQUEST['full_head']) : 'yes'; 43 'referer' => $referer,
73$action = (isset ($_REQUEST['action'])) ? htmlentities($_REQUEST['action']) : ''; 44 'view' => $view,
74$_SESSION['sort'] = (isset ($_REQUEST['sort'])) ? htmlentities($_REQUEST['sort']) : 'id'; 45 'poche_url' => Tools::getPocheUrl(),
75$id = (isset ($_REQUEST['id'])) ? htmlspecialchars($_REQUEST['id']) : ''; 46 'title' => _('poche, a read it later open source system'),
76$url = (isset ($_GET['url'])) ? $_GET['url'] : ''; 47 'token' => Session::getToken(),
77 48);
78$tpl->assign('isLogged', Session::isLogged());
79$tpl->assign('referer', $ref);
80$tpl->assign('view', $view);
81$tpl->assign('poche_url', myTool::getUrl());
82$tpl->assign('title', 'poche, a read it later open source system');
83 49
84if (Session::isLogged()) { 50if (Session::isLogged()) {
85 action_to_do($action, $url, $id); 51 $poche->action($action, $url, $id);
86 display_view($view, $id, $full_head); 52 $tpl_file = Tools::getTplFile($view);
53 $tpl_vars = array_merge($tpl_vars, $poche->displayView($view, $id));
87} 54}
88else { 55else {
89 $tpl->draw('login'); 56 $tpl_file = 'login.twig';
90} 57}
58
59# because messages can be added in $poche->action(), we have to add this entry now (we can add it before)
60$messages = $poche->messages->display('all', FALSE);
61$tpl_vars = array_merge($tpl_vars, array('messages' => $messages));
62
63# display poche
64echo $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
index 00000000..cb232a84
--- /dev/null
+++ b/install/mysql.sql
@@ -0,0 +1,34 @@
1CREATE TABLE IF NOT EXISTS `config` (
2 `id` int(11) NOT NULL AUTO_INCREMENT,
3 `name` varchar(255) NOT NULL,
4 `value` varchar(255) NOT NULL,
5 PRIMARY KEY (`id`)
6) ENGINE=InnoDB DEFAULT CHARSET=latin1;
7
8CREATE TABLE IF NOT EXISTS `entries` (
9 `id` int(11) NOT NULL AUTO_INCREMENT,
10 `title` varchar(255) NOT NULL,
11 `url` varchar(255) NOT NULL,
12 `is_read` tinyint(1) NOT NULL,
13 `is_fav` tinyint(1) NOT NULL,
14 `content` blob NOT NULL,
15 `user_id` int(11) NOT NULL,
16 PRIMARY KEY (`id`)
17) ENGINE=InnoDB DEFAULT CHARSET=latin1;
18
19CREATE TABLE IF NOT EXISTS `users` (
20 `id` int(11) NOT NULL AUTO_INCREMENT,
21 `username` varchar(255) NOT NULL,
22 `password` varchar(255) NOT NULL,
23 `name` int(255) NOT NULL,
24 `email` varchar(255) NOT NULL,
25 PRIMARY KEY (`id`)
26) ENGINE=InnoDB DEFAULT CHARSET=latin1;
27
28CREATE TABLE IF NOT EXISTS `users_config` (
29 `id` int(11) NOT NULL AUTO_INCREMENT,
30 `user_id` int(11) NOT NULL,
31 `name` varchar(255) NOT NULL,
32 `value` varchar(255) NOT NULL,
33 PRIMARY KEY (`id`)
34) ENGINE=InnoDB DEFAULT CHARSET=latin1; \ No newline at end of file
diff --git a/db/poche.sqlite b/install/poche.sqlite
index 2aee61f4..c268223d 100755
--- a/db/poche.sqlite
+++ b/install/poche.sqlite
Binary files differ
diff --git a/install/postgres.sql b/install/postgres.sql
new file mode 100644
index 00000000..9e0e8276
--- /dev/null
+++ b/install/postgres.sql
@@ -0,0 +1,30 @@
1CREATE TABLE config (
2 id bigserial primary key,
3 name varchar(255) NOT NULL,
4 value varchar(255) NOT NULL
5);
6
7CREATE TABLE entries (
8 id bigserial primary key,
9 title varchar(255) NOT NULL,
10 url varchar(255) NOT NULL,
11 is_read boolean DEFAULT false,
12 is_fav boolean DEFAULT false,
13 content TEXT,
14 user_id integer NOT NULL
15);
16
17CREATE TABLE users (
18 id bigserial primary key,
19 username varchar(255) NOT NULL,
20 password varchar(255) NOT NULL,
21 name varchar(255) NOT NULL,
22 email varchar(255) NOT NULL
23);
24
25CREATE TABLE users_config (
26 id bigserial primary key,
27 user_id integer NOT NULL,
28 name varchar(255) NOT NULL,
29 value varchar(255) NOT NULL
30); \ 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
index 00000000..299abf48
--- /dev/null
+++ b/install/update_sqlite_from_0_to_1.php
@@ -0,0 +1,72 @@
1<?php
2# import script to upgrade from poche 0.3
3$db_path = 'sqlite:../db/poche.sqlite';
4$handle = new PDO($db_path);
5$handle->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
6
7# Requêtes à exécuter pour mettre à jour poche.sqlite en 1.x
8
9# ajout d'un champ user_id sur la table entries
10$sql = 'ALTER TABLE entries RENAME TO tempEntries;';
11$query = $handle->prepare($sql);
12$query->execute();
13
14$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);';
15$query = $handle->prepare($sql);
16$query->execute();
17
18$sql = 'INSERT INTO entries (id, title, url, is_read, is_fav, content) SELECT id, title, url, is_read, is_fav, content FROM tempEntries;';
19$query = $handle->prepare($sql);
20$query->execute();
21
22# Update tout pour mettre user_id = 1
23$sql = 'UPDATE entries SET user_id = 1;';
24$query = $handle->prepare($sql);
25$query->execute();
26
27# Changement des flags pour les lus / favoris
28$sql = 'UPDATE entries SET is_read = 1 WHERE is_read = -1;';
29$query = $handle->prepare($sql);
30$query->execute();
31
32$sql = 'UPDATE entries SET is_fav = 1 WHERE is_fav = -1;';
33$query = $handle->prepare($sql);
34$query->execute();
35
36# Création de la table users
37$sql = 'CREATE TABLE users (id INTEGER PRIMARY KEY, username TEXT, password TEXT, name TEXT, email TEXT);';
38$query = $handle->prepare($sql);
39$query->execute();
40
41$sql = 'INSERT INTO users (username) SELECT value FROM config WHERE name = "login";';
42$query = $handle->prepare($sql);
43$query->execute();
44
45$sql = "UPDATE users SET password = (SELECT value FROM config WHERE name = 'password')";
46$query = $handle->prepare($sql);
47$query->execute();
48
49# Création de la table users_config
50$sql = 'CREATE TABLE users_config (id INTEGER PRIMARY KEY, user_id NUMERIC, name TEXT, value TEXT);';
51$query = $handle->prepare($sql);
52$query->execute();
53
54$sql = 'INSERT INTO users_config (user_id, name, value) VALUES (1, "pager", "10");';
55$query = $handle->prepare($sql);
56$query->execute();
57
58$sql = 'INSERT INTO users_config (user_id, name, value) VALUES (1, "language", "en_EN.UTF8");';
59$query = $handle->prepare($sql);
60$query->execute();
61
62# Suppression de la table temporaire
63$sql = 'DROP TABLE tempEntries;';
64$query = $handle->prepare($sql);
65$query->execute();
66
67# Vidage de la table de config
68$sql = 'DELETE FROM config;';
69$query = $handle->prepare($sql);
70$query->execute();
71
72echo '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
index 006e9531..00000000
--- a/js/jquery-1.9.1.min.js
+++ /dev/null
@@ -1,5 +0,0 @@
1/*! jQuery v1.9.1 | (c) 2005, 2012 jQuery Foundation, Inc. | jquery.org/license
2//@ sourceMappingURL=jquery.min.map
3*/(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;
4return(!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)
5}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
index 57c081c6..00000000
--- a/js/jquery.masonry.min.js
+++ /dev/null
@@ -1,10 +0,0 @@
1/**
2 * jQuery Masonry v2.1.08
3 * A dynamic layout plugin for jQuery
4 * The flip-side of CSS Floats
5 * http://masonry.desandro.com
6 *
7 * Licensed under the MIT license.
8 * Copyright 2012 David DeSandro
9 */
10(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
index 97d9911d..00000000
--- a/js/poche.js
+++ /dev/null
@@ -1,57 +0,0 @@
1function toggle_favorite(element, id) {
2 $(element).toggleClass('fav-off');
3 $.ajax ({
4 url: "index.php?action=toggle_fav",
5 data:{id:id}
6 });
7}
8
9function toggle_archive(element, id, view_article) {
10 $(element).toggleClass('archive-off');
11 $.ajax ({
12 url: "index.php?action=toggle_archive",
13 data:{id:id}
14 });
15 var obj = $('#entry-'+id);
16
17 // on vient de la vue de l'article, donc pas de gestion de grille
18 if (view_article != 1) {
19 $('#content').masonry('remove',obj);
20 $('#content').masonry('reloadItems');
21 $('#content').masonry('reload');
22 }
23}
24
25function sort_links(view, sort) {
26 $.get('index.php', { view: view, sort: sort, full_head: 'no' }, function(data) {
27 $('#content').html(data);
28 });
29}
30
31
32// ---------- Swith light or dark view
33function setActiveStyleSheet(title) {
34 var i, a, main;
35 for(i=0; (a = document.getElementsByTagName("link")[i]); i++) {
36 if(a.getAttribute("rel").indexOf("style") != -1 && a.getAttribute("title")) {
37 a.disabled = true;
38 if(a.getAttribute("title") == title) a.disabled = false;
39 }
40 }
41}
42$('#themeswitch').click(function() {
43 // we want the dark
44 if ($('body').hasClass('light-style')) {
45 setActiveStyleSheet('dark-style');
46 $('body').addClass('dark-style');
47 $('body').removeClass('light-style');
48 $('#themeswitch').text('light');
49 // we want the light
50 } else if ($('body').hasClass('dark-style')) {
51 setActiveStyleSheet('light-style');
52 $('body').addClass('light-style');
53 $('body').removeClass('dark-style');
54 $('#themeswitch').text('dark');
55 }
56 return false;
57});
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
index 00000000..c0d4a9d6
--- /dev/null
+++ b/locale/fr_FR.UTF8/LC_MESSAGES/fr_FR.UTF8.mo
Binary files 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
index 00000000..7f8cf784
--- /dev/null
+++ b/locale/fr_FR.UTF8/LC_MESSAGES/fr_FR.UTF8.po
@@ -0,0 +1,376 @@
1msgid ""
2msgstr ""
3"Project-Id-Version: poche\n"
4"POT-Creation-Date: 2013-08-06 08:35+0100\n"
5"PO-Revision-Date: 2013-08-06 08:35+0100\n"
6"Last-Translator: Nicolas Lœuillet <nicolas.loeuillet@gmail.com>\n"
7"Language-Team: poche <support@inthepoche.com>\n"
8"Language: Français\n"
9"MIME-Version: 1.0\n"
10"Content-Type: text/plain; charset=UTF-8\n"
11"Content-Transfer-Encoding: 8bit\n"
12"X-Generator: Poedit 1.5.4\n"
13"X-Poedit-KeywordsList: _;gettext;gettext_noop\n"
14"X-Poedit-Basepath: /\n"
15"Plural-Forms: nplurals=2; plural=(n > 1);\n"
16"X-Poedit-SourceCharset: UTF-8\n"
17"X-Poedit-SearchPath-0: /var/www/poche-i18n\n"
18
19#: /var/www/poche-i18n/index.php:43
20msgid "poche, a read it later open source system"
21msgstr "poche, a read it later open source system"
22
23#: /var/www/poche-i18n/inc/poche/Poche.class.php:101
24msgid "the link has been added successfully"
25msgstr "le lien a été ajouté avec succès"
26
27#: /var/www/poche-i18n/inc/poche/Poche.class.php:104
28msgid "error during insertion : the link wasn't added"
29msgstr "erreur durant l'insertion : le lien n'a pas été ajouté"
30
31#: /var/www/poche-i18n/inc/poche/Poche.class.php:109
32msgid "error during fetching content : the link wasn't added"
33msgstr "erreur durant la récupération du contenu : le lien n'a pas été ajouté"
34
35#: /var/www/poche-i18n/inc/poche/Poche.class.php:119
36msgid "the link has been deleted successfully"
37msgstr "le lien a été supprimé avec succès"
38
39#: /var/www/poche-i18n/inc/poche/Poche.class.php:123
40msgid "the link wasn't deleted"
41msgstr "le lien n'a pas été supprimé"
42
43#: /var/www/poche-i18n/inc/poche/Tools.class.php:18
44msgid "Oops, it seems you don't have PHP 5."
45msgstr "Oups, il semblerait que PHP 5 ne soit pas installé. "
46
47#: /var/www/poche-i18n/cache/c9/b0/845a8dc93165e6c00b6b43068799.php:32
48#: /var/www/poche-i18n/cache/c9/b0/845a8dc93165e6c00b6b43068799.php:70
49#: /var/www/poche-i18n/cache/76/a4/e7c21f2e0ba29104fc654cd8ba41.php:50
50msgid "config"
51msgstr "config"
52
53#: /var/www/poche-i18n/cache/c9/b0/845a8dc93165e6c00b6b43068799.php:46
54#: /var/www/poche-i18n/cache/7a/1e/68e6b4aec1301ae024cc85232e7c.php:31
55#: /var/www/poche-i18n/cache/76/a4/e7c21f2e0ba29104fc654cd8ba41.php:26
56#: /var/www/poche-i18n/cache/dd/a8/530129765655dcde0a70a31a78b6.php:34
57msgid "home"
58msgstr "accueil"
59
60#: /var/www/poche-i18n/cache/c9/b0/845a8dc93165e6c00b6b43068799.php:54
61#: /var/www/poche-i18n/cache/76/a4/e7c21f2e0ba29104fc654cd8ba41.php:34
62msgid "favorites"
63msgstr "favoris"
64
65#: /var/www/poche-i18n/cache/c9/b0/845a8dc93165e6c00b6b43068799.php:62
66#: /var/www/poche-i18n/cache/76/a4/e7c21f2e0ba29104fc654cd8ba41.php:42
67msgid "archive"
68msgstr "archives"
69
70#: /var/www/poche-i18n/cache/c9/b0/845a8dc93165e6c00b6b43068799.php:74
71#: /var/www/poche-i18n/cache/c9/b0/845a8dc93165e6c00b6b43068799.php:76
72#: /var/www/poche-i18n/cache/76/a4/e7c21f2e0ba29104fc654cd8ba41.php:54
73#: /var/www/poche-i18n/cache/76/a4/e7c21f2e0ba29104fc654cd8ba41.php:56
74msgid "logout"
75msgstr "déconnexion"
76
77#: /var/www/poche-i18n/cache/c9/b0/845a8dc93165e6c00b6b43068799.php:87
78msgid "Bookmarklet"
79msgstr "Bookmarklet"
80
81#: /var/www/poche-i18n/cache/c9/b0/845a8dc93165e6c00b6b43068799.php:91
82msgid ""
83"Thanks to the bookmarklet, you will be able to easily add a link to your "
84"poche."
85msgstr ""
86"Grâce au bookmarklet, vous pouvez ajouter facilement un lien dans votre "
87"poche."
88
89#: /var/www/poche-i18n/cache/c9/b0/845a8dc93165e6c00b6b43068799.php:93
90msgid "Have a look to this documentation:"
91msgstr "Jetez un œil à la documentation :"
92
93#: /var/www/poche-i18n/cache/c9/b0/845a8dc93165e6c00b6b43068799.php:97
94msgid "Drag & drop this link to your bookmarks bar and have fun with poche."
95msgstr ""
96"Glissez / déposez ce lien dans votre barre de favoris de votre navigateur et "
97"prenez du bon temps avec poche."
98
99#: /var/www/poche-i18n/cache/c9/b0/845a8dc93165e6c00b6b43068799.php:103
100msgid "poche it!"
101msgstr "poche-le !"
102
103#: /var/www/poche-i18n/cache/c9/b0/845a8dc93165e6c00b6b43068799.php:108
104msgid "Updating poche"
105msgstr "Mettre à jour poche"
106
107#: /var/www/poche-i18n/cache/c9/b0/845a8dc93165e6c00b6b43068799.php:113
108msgid "your version"
109msgstr "votre version"
110
111#: /var/www/poche-i18n/cache/c9/b0/845a8dc93165e6c00b6b43068799.php:119
112msgid "latest stable version"
113msgstr "dernière version stable"
114
115#: /var/www/poche-i18n/cache/c9/b0/845a8dc93165e6c00b6b43068799.php:125
116msgid "a more recent stable version is available."
117msgstr "une version stable plus récente est disponible."
118
119#: /var/www/poche-i18n/cache/c9/b0/845a8dc93165e6c00b6b43068799.php:128
120#: /var/www/poche-i18n/cache/c9/b0/845a8dc93165e6c00b6b43068799.php:142
121msgid "you are up to date."
122msgstr "vous êtes à jour."
123
124#: /var/www/poche-i18n/cache/c9/b0/845a8dc93165e6c00b6b43068799.php:133
125msgid "latest dev version"
126msgstr "dernière version de développement"
127
128#: /var/www/poche-i18n/cache/c9/b0/845a8dc93165e6c00b6b43068799.php:139
129msgid "a more recent development version is available."
130msgstr "une version de développement plus récente est disponible."
131
132#: /var/www/poche-i18n/cache/c9/b0/845a8dc93165e6c00b6b43068799.php:150
133msgid "Change your password"
134msgstr "Modifier votre mot de passe"
135
136#: /var/www/poche-i18n/cache/c9/b0/845a8dc93165e6c00b6b43068799.php:157
137msgid "New password:"
138msgstr "Nouveau mot de passe :"
139
140#: /var/www/poche-i18n/cache/c9/b0/845a8dc93165e6c00b6b43068799.php:161
141#: /var/www/poche-i18n/cache/c9/b0/845a8dc93165e6c00b6b43068799.php:171
142#: /var/www/poche-i18n/cache/d4/28/e0d08991ec2d8a7b133505e7c651.php:60
143#: /var/www/poche-i18n/cache/ae/26/05eb67771213c16bd8c9aaf2d2c4.php:68
144msgid "Password"
145msgstr "Mot de passe"
146
147#: /var/www/poche-i18n/cache/c9/b0/845a8dc93165e6c00b6b43068799.php:167
148msgid "Repeat your new password:"
149msgstr "Répétez le nouveau mot de passe :"
150
151#: /var/www/poche-i18n/cache/c9/b0/845a8dc93165e6c00b6b43068799.php:177
152msgid "Update"
153msgstr "Mettre à jour"
154
155#: /var/www/poche-i18n/cache/c9/b0/845a8dc93165e6c00b6b43068799.php:193
156msgid "Import"
157msgstr "Import"
158
159#: /var/www/poche-i18n/cache/c9/b0/845a8dc93165e6c00b6b43068799.php:197
160msgid "Please execute the import script locally, it can take a very long time."
161msgstr "Merci d'exécuter l'import en local, cela peut prendre du temps. "
162
163#: /var/www/poche-i18n/cache/c9/b0/845a8dc93165e6c00b6b43068799.php:201
164msgid "More infos in the official doc:"
165msgstr "Plus d'infos sur la documentation officielle :"
166
167#: /var/www/poche-i18n/cache/c9/b0/845a8dc93165e6c00b6b43068799.php:206
168msgid "import from Pocket"
169msgstr "l'import depuis Pocket est terminé."
170
171#: /var/www/poche-i18n/cache/c9/b0/845a8dc93165e6c00b6b43068799.php:210
172msgid "import from Readability"
173msgstr "l'import depuis Readability est terminé."
174
175#: /var/www/poche-i18n/cache/c9/b0/845a8dc93165e6c00b6b43068799.php:214
176msgid "import from Instapaper"
177msgstr "Import depuis Instapaper"
178
179#: /var/www/poche-i18n/cache/c9/b0/845a8dc93165e6c00b6b43068799.php:220
180msgid "Export your poche datas"
181msgstr "Exporter vos données de poche"
182
183#: /var/www/poche-i18n/cache/c9/b0/845a8dc93165e6c00b6b43068799.php:224
184msgid "Click here"
185msgstr "Cliquez-ici"
186
187#: /var/www/poche-i18n/cache/c9/b0/845a8dc93165e6c00b6b43068799.php:226
188msgid "to export your poche datas."
189msgstr "pour exporter vos données de poche."
190
191#: /var/www/poche-i18n/cache/7a/1e/68e6b4aec1301ae024cc85232e7c.php:46
192#: /var/www/poche-i18n/cache/7a/1e/68e6b4aec1301ae024cc85232e7c.php:139
193#: /var/www/poche-i18n/cache/30/97/b548692380c89d047a16cec7af79.php:22
194msgid "back to home"
195msgstr "retour à l'accueil"
196
197#: /var/www/poche-i18n/cache/7a/1e/68e6b4aec1301ae024cc85232e7c.php:50
198#: /var/www/poche-i18n/cache/7a/1e/68e6b4aec1301ae024cc85232e7c.php:147
199#: /var/www/poche-i18n/cache/dd/a8/530129765655dcde0a70a31a78b6.php:119
200msgid "toggle mark as read"
201msgstr "marquer comme lu"
202
203#: /var/www/poche-i18n/cache/7a/1e/68e6b4aec1301ae024cc85232e7c.php:60
204#: /var/www/poche-i18n/cache/7a/1e/68e6b4aec1301ae024cc85232e7c.php:157
205#: /var/www/poche-i18n/cache/dd/a8/530129765655dcde0a70a31a78b6.php:129
206msgid "toggle favorite"
207msgstr "favori"
208
209#: /var/www/poche-i18n/cache/7a/1e/68e6b4aec1301ae024cc85232e7c.php:70
210#: /var/www/poche-i18n/cache/7a/1e/68e6b4aec1301ae024cc85232e7c.php:167
211#: /var/www/poche-i18n/cache/dd/a8/530129765655dcde0a70a31a78b6.php:139
212msgid "delete"
213msgstr "supprimer"
214
215#: /var/www/poche-i18n/cache/7a/1e/68e6b4aec1301ae024cc85232e7c.php:82
216#: /var/www/poche-i18n/cache/7a/1e/68e6b4aec1301ae024cc85232e7c.php:179
217msgid "tweet"
218msgstr "tweeter"
219
220#: /var/www/poche-i18n/cache/7a/1e/68e6b4aec1301ae024cc85232e7c.php:93
221#: /var/www/poche-i18n/cache/7a/1e/68e6b4aec1301ae024cc85232e7c.php:190
222msgid "email"
223msgstr "envoyer par email"
224
225#: /var/www/poche-i18n/cache/7a/1e/68e6b4aec1301ae024cc85232e7c.php:109
226#: /var/www/poche-i18n/cache/7a/1e/68e6b4aec1301ae024cc85232e7c.php:125
227#: /var/www/poche-i18n/cache/dd/a8/530129765655dcde0a70a31a78b6.php:153
228msgid "original"
229msgstr "original"
230
231#: /var/www/poche-i18n/cache/7a/1e/68e6b4aec1301ae024cc85232e7c.php:143
232msgid "back to top"
233msgstr "retour en haut de page"
234
235#: /var/www/poche-i18n/cache/7a/1e/68e6b4aec1301ae024cc85232e7c.php:198
236msgid "this article appears wrong?"
237msgstr "cet article s'affiche mal ?"
238
239#: /var/www/poche-i18n/cache/7a/1e/68e6b4aec1301ae024cc85232e7c.php:200
240msgid "create an issue"
241msgstr "créer un ticket"
242
243#: /var/www/poche-i18n/cache/7a/1e/68e6b4aec1301ae024cc85232e7c.php:202
244msgid "or"
245msgstr "ou"
246
247#: /var/www/poche-i18n/cache/7a/1e/68e6b4aec1301ae024cc85232e7c.php:206
248msgid "contact us by mail"
249msgstr "contactez-nous par email"
250
251#: /var/www/poche-i18n/cache/88/8a/ee3b7080c13204391c14947a0c2c.php:22
252msgid "powered by"
253msgstr "propulsé par"
254
255#: /var/www/poche-i18n/cache/d4/28/e0d08991ec2d8a7b133505e7c651.php:31
256msgid "installation"
257msgstr "installation"
258
259#: /var/www/poche-i18n/cache/d4/28/e0d08991ec2d8a7b133505e7c651.php:42
260msgid "install your poche"
261msgstr "installez votre poche"
262
263#: /var/www/poche-i18n/cache/d4/28/e0d08991ec2d8a7b133505e7c651.php:47
264msgid ""
265"poche is still not installed. Please fill the below form to install it. "
266"Don't hesitate to <a href='http://inthepoche.com/?pages/Documentation'>read "
267"the documentation on poche website</a>."
268msgstr ""
269"poche n'est pas encore installé. Merci de remplir les champs ci-dessous pour "
270"l'installer. N'hésitez pas à <a href='http://inthepoche.com/?pages/"
271"Documentation'>lire la documentation sur le site de poche</a>."
272
273#: /var/www/poche-i18n/cache/d4/28/e0d08991ec2d8a7b133505e7c651.php:53
274#: /var/www/poche-i18n/cache/ae/26/05eb67771213c16bd8c9aaf2d2c4.php:55
275msgid "Login"
276msgstr "Nom d'utilisateur"
277
278#: /var/www/poche-i18n/cache/d4/28/e0d08991ec2d8a7b133505e7c651.php:67
279msgid "Repeat your password"
280msgstr "Répétez votre mot de passe"
281
282#: /var/www/poche-i18n/cache/d4/28/e0d08991ec2d8a7b133505e7c651.php:74
283msgid "Install"
284msgstr "Installer"
285
286#: /var/www/poche-i18n/cache/ae/26/05eb67771213c16bd8c9aaf2d2c4.php:31
287#: /var/www/poche-i18n/cache/ae/26/05eb67771213c16bd8c9aaf2d2c4.php:42
288msgid "login to your poche"
289msgstr "Se connecter à votre poche"
290
291#: /var/www/poche-i18n/cache/ae/26/05eb67771213c16bd8c9aaf2d2c4.php:48
292msgid "you are in demo mode, some features may be disabled."
293msgstr ""
294"vous êtes en mode démo, certaines fonctionnalités sont peut-être désactivées."
295
296#: /var/www/poche-i18n/cache/ae/26/05eb67771213c16bd8c9aaf2d2c4.php:80
297msgid "Stay signed in"
298msgstr "rester connecté"
299
300#: /var/www/poche-i18n/cache/ae/26/05eb67771213c16bd8c9aaf2d2c4.php:86
301msgid "(Do not check on public computers)"
302msgstr "(à ne pas cocher sur un ordinateur public)"
303
304#: /var/www/poche-i18n/cache/ae/26/05eb67771213c16bd8c9aaf2d2c4.php:93
305msgid "Sign in"
306msgstr ""
307
308#: /var/www/poche-i18n/cache/dd/a8/530129765655dcde0a70a31a78b6.php:55
309#: /var/www/poche-i18n/cache/dd/a8/530129765655dcde0a70a31a78b6.php:57
310msgid "by date asc"
311msgstr "par date asc"
312
313#: /var/www/poche-i18n/cache/dd/a8/530129765655dcde0a70a31a78b6.php:59
314msgid "by date"
315msgstr "par date"
316
317#: /var/www/poche-i18n/cache/dd/a8/530129765655dcde0a70a31a78b6.php:65
318#: /var/www/poche-i18n/cache/dd/a8/530129765655dcde0a70a31a78b6.php:67
319msgid "by date desc"
320msgstr "par date desc"
321
322#: /var/www/poche-i18n/cache/dd/a8/530129765655dcde0a70a31a78b6.php:75
323#: /var/www/poche-i18n/cache/dd/a8/530129765655dcde0a70a31a78b6.php:77
324msgid "by title asc"
325msgstr "par titre asc"
326
327#: /var/www/poche-i18n/cache/dd/a8/530129765655dcde0a70a31a78b6.php:79
328msgid "by title"
329msgstr "par titre"
330
331#: /var/www/poche-i18n/cache/dd/a8/530129765655dcde0a70a31a78b6.php:85
332#: /var/www/poche-i18n/cache/dd/a8/530129765655dcde0a70a31a78b6.php:87
333msgid "by title desc"
334msgstr "par titre desc"
335
336#~ msgid "Please choose between Pocket & Readabilty :"
337#~ msgstr "Merci de choisir entre Pocket & Readability :"
338
339#~ msgid "Bye bye Pocket, let's go !"
340#~ msgstr "Bye bye Pocket, en route !"
341
342#~ msgid "Bye bye Readability, let's go !"
343#~ msgstr "Bye bye Readability, en route !"
344
345#~ msgid "Welcome to poche !"
346#~ msgstr "Bienvenue dans poche !"
347
348#~ msgid "Error with the import."
349#~ msgstr "Erreur durant l'import."
350
351#~ msgid "Wrong token."
352#~ msgstr "Mauvais jeton."
353
354#~ msgid "Login failed !"
355#~ msgstr "Connexion échouée."
356
357#~ msgid "your password has been updated"
358#~ msgstr "Votre mot de passe a été mis à jour. "
359
360#~ msgid "in demo mode, you can't update password"
361#~ msgstr "En mode démo, le mot de passe ne peut être modifié."
362
363#~ msgid ""
364#~ "your password can't be empty and you have to repeat it in the second field"
365#~ msgstr ""
366#~ "Votre mot de passe ne peut être vide et vous devez le répéter dans le "
367#~ "second champ."
368
369#~ msgid "error during url preparation : the link wasn't added"
370#~ msgstr "erreur durant l'insertion : le lien n'a pas été ajouté"
371
372#~ msgid "error during url preparation : the link is not valid"
373#~ msgstr "erreur durant la préparation de l'URL : le lien n'est pas valide"
374
375#~ msgid "TEST"
376#~ msgstr "NICOLAS"
diff --git a/phpunit.xml.dist b/phpunit.xml.dist
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/phpunit.xml.dist
diff --git a/tpl/_bookmarklet.twig b/tpl/_bookmarklet.twig
new file mode 100644
index 00000000..2f3b2d16
--- /dev/null
+++ b/tpl/_bookmarklet.twig
@@ -0,0 +1,3 @@
1 <script type="text/javascript">
2 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>"
3 </script> \ No newline at end of file
diff --git a/tpl/_footer.twig b/tpl/_footer.twig
new file mode 100644
index 00000000..6891756a
--- /dev/null
+++ b/tpl/_footer.twig
@@ -0,0 +1,4 @@
1 <footer class="w600p center mt3 smaller txtright">
2 <p>{% trans "powered by" %} <a href="http://inthepoche.com">poche</a></p>
3 {% 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 %}
4 </footer> \ No newline at end of file
diff --git a/tpl/_head.twig b/tpl/_head.twig
new file mode 100644
index 00000000..f25f0471
--- /dev/null
+++ b/tpl/_head.twig
@@ -0,0 +1,9 @@
1 <link rel="shortcut icon" type="image/x-icon" href="./tpl/img/favicon.ico" />
2 <link rel="apple-touch-icon-precomposed" sizes="144x144" href="./tpl/img/apple-touch-icon-144x144-precomposed.png">
3 <link rel="apple-touch-icon-precomposed" sizes="72x72" href="./tpl/img/apple-touch-icon-72x72-precomposed.png">
4 <link rel="apple-touch-icon-precomposed" href="./tpl/img/apple-touch-icon-precomposed.png">
5 <link rel="stylesheet" href="./tpl/css/knacss.css" media="all">
6 <link rel="stylesheet" href="./tpl/css/style.css" media="all">
7 <link rel="stylesheet" href="./tpl/css/style-{{ constant('THEME') }}.css" media="all" title="{{ constant('THEME') }} theme">
8 <link rel="stylesheet" href="./tpl/css/messages.css" media="all">
9 <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
index 00000000..699d6a0c
--- /dev/null
+++ b/tpl/_menu.twig
@@ -0,0 +1,7 @@
1 <ul id="links">
2 <li><a href="./" {% if view == 'home' %}class="current"{% endif %}>{% trans "home" %}</a></li>
3 <li><a href="./?view=fav" {% if view == 'fav' %}class="current"{% endif %}>{% trans "favorites" %}</a></li>
4 <li><a href="./?view=archive" {% if view == 'archive' %}class="current"{% endif %}>{% trans "archive" %}</a></li>
5 <li><a href="./?view=config" {% if view == 'config' %}class="current"{% endif %}>{% trans "config" %}</a></li>
6 <li><a href="./?logout" title="{% trans "logout" %}">{% trans "logout" %}</a></li>
7 </ul> \ No newline at end of file
diff --git a/tpl/_messages.twig b/tpl/_messages.twig
new file mode 100644
index 00000000..679aa098
--- /dev/null
+++ b/tpl/_messages.twig
@@ -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
index 00000000..ae01cc3f
--- /dev/null
+++ b/tpl/_top.twig
@@ -0,0 +1,3 @@
1 <header class="w600p center mbm">
2 <h1><a href="./" title="{% trans "back to home" %}" ><img src="./tpl/img/logo.png" alt="logo poche" /></a></h1>
3 </header> \ No newline at end of file
diff --git a/tpl/config.html b/tpl/config.html
deleted file mode 100644
index 1100d455..00000000
--- a/tpl/config.html
+++ /dev/null
@@ -1,27 +0,0 @@
1 <div id="content">
2 <h2>Bookmarklet</h2>
3 <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>
4 <p>Drag & drop this link to your bookmarks bar and have fun with poche.</p>
5 <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>
6
7 <h2>Password</h2>
8 <form method="post" action="?config" name="loginform">
9 <fieldset class="w500p">
10 <div class="row">
11 <label class="col w150p" for="password">New password</label>
12 <input class="col" type="password" id="password" name="password" placeholder="Password" tabindex="2">
13 </div>
14 <div class="row">
15 <label class="col w150p" for="password_repeat">Repeat your new password</label>
16 <input class="col" type="password" id="password_repeat" name="password_repeat" placeholder="Password" tabindex="3">
17 </div>
18 <div class="row mts txtcenter">
19 <button class="bouton" type="submit" tabindex="4">Update</button>
20 </div>
21 </fieldset>
22 <input type="hidden" name="returnurl" value="<?php echo htmlspecialchars($referer);?>">
23 <input type="hidden" name="token" value="<?php echo Session::getToken(); ?>">
24 </form>
25 <h2>Export</h2>
26 <p><a href="?view=export" target="_blank">Click here</a> to export your poche datas.</p>
27 </div> \ No newline at end of file
diff --git a/tpl/config.twig b/tpl/config.twig
new file mode 100644
index 00000000..95d5d8cf
--- /dev/null
+++ b/tpl/config.twig
@@ -0,0 +1,57 @@
1{% extends "layout.twig" %}
2
3{% block title %}{% trans "config" %}{% endblock %}
4{% block menu %}
5 <ul id="links">
6 <li><a href="./" {% if view == 'home' %}class="current"{% endif %}>{% trans "home" %}</a></li>
7 <li><a href="./?view=fav" {% if view == 'fav' %}class="current"{% endif %}>{% trans "favorites" %}</a></li>
8 <li><a href="./?view=archive" {% if view == 'archive' %}class="current"{% endif %}>{% trans "archive" %}</a></li>
9 <li><a href="./?view=config" {% if view == 'config' %}class="current"{% endif %}>{% trans "config" %}</a></li>
10 <li><a href="./?logout" title="{% trans "logout" %}">{% trans "logout" %}</a></li>
11 </ul>
12{% endblock %}
13{% block content %}
14 <h2>{% trans "Bookmarklet" %}</h2>
15 <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>
16 <p>{% trans "Drag & drop this link to your bookmarks bar and have fun with poche." %}</p>
17 <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>
18
19 <h2>{% trans "Updating poche" %}</h2>
20 <p><ul>
21 <li>{% trans "your version" %} : <strong>{{ constant('POCHE_VERSION') }}</strong></li>
22 <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>
23 <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>
24 </ul>
25 </p>
26
27 <h2>{% trans "Change your password" %}</h2>
28 <form method="post" action="?config" name="loginform">
29 <fieldset class="w500p">
30 <div class="row">
31 <label class="col w150p" for="password">{% trans "New password:" %}</label>
32 <input class="col" type="password" id="password" name="password" placeholder="{% trans "Password" %}" tabindex="2">
33 </div>
34 <div class="row">
35 <label class="col w150p" for="password_repeat">{% trans "Repeat your new password:" %}</label>
36 <input class="col" type="password" id="password_repeat" name="password_repeat" placeholder="{% trans "Password" %}" tabindex="3">
37 </div>
38 <div class="row mts txtcenter">
39 <button class="bouton" type="submit" tabindex="4">{% trans "Update" %}</button>
40 </div>
41 </fieldset>
42 <input type="hidden" name="returnurl" value="{{ referer }}">
43 <input type="hidden" name="token" value="{{ token }}">
44 </form>
45
46 <h2>{% trans "Import" %}</h2>
47 <p>{% trans "Please execute the import script locally, it can take a very long time." %}</p>
48 <p>{% trans "More infos in the official doc:" %} <a href="http://inthepoche.com/?pages/Documentation">inthepoche.com</a></p>
49 <p><ul>
50 <li><a href="./?import&from=pocket">{% trans "import from Pocket" %}</a> (you must have a "ril_export.html" file on your server)</li>
51 <li><a href="./?import&from=readability">{% trans "import from Readability" %}</a> (you must have a "readability" file on your server)</li>
52 <li><a href="./?import&from=instapaper">{% trans "import from Instapaper" %}</a> (you must have a "instapaper-export.html" file on your server)</li>
53 </ul></p>
54
55 <h2>{% trans "Export your poche datas" %}</h2>
56 <p><a href="./?export" target="_blank">{% trans "Click here" %}</a> {% trans "to export your poche datas." %}</p>
57{% endblock %} \ No newline at end of file
diff --git a/css/knacss.css b/tpl/css/knacss.css
index ca0696b7..ca0696b7 100644
--- a/css/knacss.css
+++ b/tpl/css/knacss.css
diff --git a/tpl/css/messages.css b/tpl/css/messages.css
new file mode 100755
index 00000000..9222bb88
--- /dev/null
+++ b/tpl/css/messages.css
@@ -0,0 +1,13 @@
1.messages { width: 400px; -moz-border-radius: 4px; border-radius: 4px; display: block; padding: 10px 0; margin: 10px auto 10px; clear: both; }
2.messages a.closeMessage { margin: -14px -8px 0 0; display:none; width: 16px; height: 16px; float: right; background: url(../img/messages/close.png) no-repeat; }
3/*.messages:hover a.closeMessage { visibility:visible; }*/
4.messages p { margin: 3px 0 3px 10px !important; padding: 0 10px 0 23px !important; font-size: 14px; line-height: 16px; }
5.messages.error { border: 1px solid #C42608; color: #c00 !important; background: #FFF0EF; }
6.messages.error p { background: url(../img/messages/cross.png ) no-repeat 0px 50%; color:#c00 !important; }
7.messages.success {background: #E0FBCC; border: 1px solid #6DC70C; }
8.messages.success p { background: url(../img/messages/tick.png) no-repeat 0px 50%; color: #2B6301 !important; }
9.messages.warning { background: #FFFCD3; border: 1px solid #EBCD41; color: #000; }
10.messages.warning p { background: url(../img/messages/warning.png ) no-repeat 0px 50%; color: #5F4E01; }
11.messages.information, .messages.info { background: #DFEBFB; border: 1px solid #82AEE7; }
12.messages.information p, .messages.info p { background: url(../img/messages/help.png ) no-repeat 0px 50%; color: #064393; }
13.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
index 00000000..9ea7955a
--- /dev/null
+++ b/tpl/css/style-light.css
@@ -0,0 +1,53 @@
1
2a.back span {
3 background: url('../img/light/left.png') no-repeat;
4}
5
6a.top span {
7 background: url('../img/light/top.png') no-repeat;
8}
9
10
11a.fav span {
12 background: url('../img/light/star-on.png') no-repeat;
13}
14
15a.fav span:hover {
16 background: url('../img/light/star-off.png') no-repeat;
17}
18
19a.fav-off span {
20 background: url('../img/light/star-off.png') no-repeat;
21}
22
23a.fav-off span:hover {
24 background: url('../img/light/star-on.png') no-repeat;
25}
26
27a.archive span {
28 background: url('../img/light/checkmark-on.png') no-repeat;
29}
30
31a.archive span:hover {
32 background: url('../img/light/checkmark-off.png') no-repeat;
33}
34
35a.archive-off span {
36 background: url('../img/light/checkmark-off.png') no-repeat;
37}
38
39a.archive-off span:hover {
40 background: url('../img/light/checkmark-on.png') no-repeat;
41}
42
43a.twitter span {
44 background: url('../img/light/twitter.png') no-repeat;
45}
46
47a.email span {
48 background: url('../img/light/envelop.png') no-repeat;
49}
50
51a.delete span {
52 background: url('../img/light/remove.png') no-repeat;
53} \ No newline at end of file
diff --git a/tpl/css/style.css b/tpl/css/style.css
new file mode 100644
index 00000000..d23c1896
--- /dev/null
+++ b/tpl/css/style.css
@@ -0,0 +1,244 @@
1body {
2 font-size: 16px;
3 font-family: 'Roboto', sans-serif;
4 margin: 10px;
5}
6
7header {
8 text-align: center;
9}
10
11header h1 {
12 font-size: 1.3em;
13}
14
15.bouton {
16 border-radius: 2px;
17}
18
19#main {
20 margin: 0 auto;
21}
22
23#main ul#links {
24 padding: 0;
25 list-style-type: none;
26 text-align: center;
27 font-size: 0.9em;
28}
29
30#main ul#links li {
31 display: inline;
32}
33
34#main ul#links li a.current {
35 -webkit-border-radius: 2px;
36 border-radius: 2px;
37}
38
39#main ul#sort {
40 padding: 0;
41 list-style-type: none;
42 text-align: center;
43 opacity: 0.5;
44}
45
46#main ul#sort li {
47 display: inline;
48 font-size: 0.9em;
49}
50
51#main ul#sort img:hover {
52 cursor: pointer;
53}
54
55
56#links a{
57 text-decoration: none;
58 padding: 5px 10px;
59}
60#links a:hover{
61 -webkit-border-radius: 2px;
62 border-radius: 2px;
63}
64
65/*** ***/
66/*** LINKS DISPLAY ***/
67
68#main a.tool {
69 text-decoration: none;
70 cursor: pointer;
71}
72
73#main #content {
74 margin-top: 20px;
75}
76
77#main #content h2 {
78 font-size: 1.3em;
79 text-decoration: none;
80}
81
82#main #content .entrie {
83 border-bottom: 1px dashed #222222;
84}
85
86#main .entrie ul.tools {
87 list-style-type: none;
88}
89
90#main .entrie ul.tools li {
91 /*display: inline;*/
92}
93
94.tools {
95 float: right;
96 text-align: right;
97 opacity: 0.5;
98}
99
100.tools p {
101 font-size: 0.8em;}
102
103/*
104.tools ul {
105 padding: 0; margin: 0;
106 list-style-type: none;
107}
108
109.tools ul li {
110 line-height: 20px;
111}
112
113.tools a.tool {
114 cursor: pointer;
115}*/
116
117#main .entrie .tools a.tool span, #article .tools a.tool span {
118 display: inline-block;
119 width: 16px;
120 height: 16px;
121}
122
123#main .entrie .url {
124 font-size: 13px;
125}
126
127
128/*** ***/
129/*** ARTICLE PAGE ***/
130
131#article {
132 margin: 0 auto;
133}
134#article header {
135 text-align: left;
136}
137
138#article header a {
139 text-decoration: none;
140}
141
142.vieworiginal a, .vieworiginal a:hover, .vieworiginal a:visited {
143 text-decoration: none;
144 color: #888888;
145}
146
147.backhome {
148 display: inline;
149}
150
151#article .tools {
152 position: relative;
153 display: inline;
154 top: 0px;
155 right: 0px;
156 width: 100%;
157}
158
159#article .tools ul li{
160 display: inline;
161}
162
163
164/*** GENERAL ***/
165body {
166 color: #000;
167}
168
169a, a:hover, a:visited {
170 color: #000;
171}
172
173.bouton {
174 background-color: #000;
175 color: #fff;
176 border: none;
177}
178.bouton:hover {
179 background-color: #222222;
180 color: #F1F1F1;
181}
182
183#main ul#links li a.current {
184 background-color: #000;
185 color: #fff;
186}
187
188#links a:hover{
189 background-color: #040707;
190 color: #F1F1F1;
191}
192
193
194/*** ***/
195/*** ARTICLE PAGE ***/
196
197#article header, #article article {
198 border-bottom: 1px solid #222222;
199}
200
201
202/* Pagination */
203.pagination {
204 clear: both;
205 padding-bottom: 20px;
206 padding-top: 10px;
207 text-align: right;
208}
209.pagination a {
210 border: 1px solid #D5D5D5;
211 color: #333;
212 font-size: 11px;
213 font-weight: bold;
214 height: 25px;
215 padding: 4px 8px;
216 text-decoration: none;
217 margin:2px;
218}
219.pagination a:hover, .pagination a:active {
220 background:#efefef;
221}
222.pagination span.current {
223 background-color: #ccc;
224 border: 1px solid #D5D5D5;
225 color: #000;
226 font-size: 11px;
227 font-weight: bold;
228 height: 25px;
229 padding: 4px 8px;
230 text-decoration: none;
231 margin:2px;
232}
233.pagination span.disabled {
234 border: 1px solid #EEEEEE;
235 color: #DDDDDD;
236 margin:2px;
237 padding: 4px 8px;
238 font-size: 11px;
239 font-weight: bold;
240}
241
242footer {
243 clear: both;
244} \ No newline at end of file
diff --git a/tpl/entries.html b/tpl/entries.html
deleted file mode 100644
index 8526a3c9..00000000
--- a/tpl/entries.html
+++ /dev/null
@@ -1,18 +0,0 @@
1 <div id="content">
2 {loop="entries"}
3 <div id="entry-{$value.id}" class="entrie mb2">
4 <span class="content">
5 <h2 class="h6-like">
6 <a href="index.php?&view=view&id={$value.id}">{$value.title}</a>
7 </h2>
8 <div class="tools">
9 <ul>
10 <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>
11 <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>
12 <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>
13 </ul>
14 </div>
15 </span>
16 </div>
17 {/loop}
18 </div> \ No newline at end of file
diff --git a/tpl/export.html b/tpl/export.html
deleted file mode 100644
index d22d05fc..00000000
--- a/tpl/export.html
+++ /dev/null
@@ -1 +0,0 @@
1export {$export} \ No newline at end of file
diff --git a/tpl/export.twig b/tpl/export.twig
new file mode 100644
index 00000000..4adb9540
--- /dev/null
+++ b/tpl/export.twig
@@ -0,0 +1 @@
{{ export }} \ No newline at end of file
diff --git a/tpl/footer.html b/tpl/footer.html
deleted file mode 100644
index b8bd755c..00000000
--- a/tpl/footer.html
+++ /dev/null
@@ -1,7 +0,0 @@
1 </div>
2 <footer class="mr2 mt3 smaller">
3 <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>
4 </footer>
5
6 </body>
7</html> \ No newline at end of file
diff --git a/tpl/head.html b/tpl/head.html
deleted file mode 100644
index e95f6100..00000000
--- a/tpl/head.html
+++ /dev/null
@@ -1,22 +0,0 @@
1<!DOCTYPE html>
2<!--[if lte IE 6]> <html class="no-js ie6 ie67 ie678" lang="en"> <![endif]-->
3<!--[if lte IE 7]> <html class="no-js ie7 ie67 ie678" lang="en"> <![endif]-->
4<!--[if IE 8]> <html class="no-js ie8 ie678" lang="en"> <![endif]-->
5<!--[if gt IE 8]><!--> <html class="no-js" lang="en"> <!--<![endif]-->
6<html>
7 <head>
8 <meta name="viewport" content="width=device-width, minimum-scale=1.0, maximum-scale=1.0">
9 <meta charset="utf-8">
10 <meta http-equiv="X-UA-Compatible" content="IE=10">
11 <title>{$title}</title>
12 <link rel="shortcut icon" type="image/x-icon" href="./img/favicon.ico" />
13 <link rel="apple-touch-icon-precomposed" sizes="144x144" href="./img/apple-touch-icon-144x144-precomposed.png">
14 <link rel="apple-touch-icon-precomposed" sizes="72x72" href="./img/apple-touch-icon-72x72-precomposed.png">
15 <link rel="apple-touch-icon-precomposed" href="./img/apple-touch-icon-precomposed.png">
16 <link rel="stylesheet" href="./css/knacss.css" media="all">
17 <link rel="stylesheet" href="./css/style.css" media="all">
18 <!-- Light Theme -->
19 <link rel="stylesheet" href="./css/style-light.css" media="all" title="light-style">
20 <!-- Dark Theme -->
21 <link rel="alternate stylesheet" href="./css/style-dark.css" media="all" title="dark-style">
22 </head> \ No newline at end of file
diff --git a/tpl/home.html b/tpl/home.html
deleted file mode 100644
index 90e247f7..00000000
--- a/tpl/home.html
+++ /dev/null
@@ -1,19 +0,0 @@
1 <body class="light-style">
2 <header>
3 <h1><a href="index.php"><img src="./img/logo.png" alt="logo poche" /></a>poche</h1>
4 </header>
5 <div id="main">
6 <ul id="links">
7 <li><a href="index.php" {if="$view == 'index'"}class="current"{/if}>home</a></li>
8 <li><a href="?view=fav" {if="$view == 'fav'"}class="current"{/if}>favorites</a></li>
9 <li><a href="?view=archive" {if="$view == 'archive'"}class="current"{/if}>archive</a></li>
10 <li><a href="?view=config" {if="$view == 'config'"}class="current"{/if}>config</a></li>
11 <li><a href="?logout" title="Logout">logout</a></li>
12 </ul>
13 {if condition="isset($entries)"}
14 <ul id="sort">
15 <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>
16 <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>
17 </ul>
18 {/if}
19 {include="messages"} \ No newline at end of file
diff --git a/tpl/home.twig b/tpl/home.twig
new file mode 100644
index 00000000..a6da641f
--- /dev/null
+++ b/tpl/home.twig
@@ -0,0 +1,29 @@
1{% extends "layout.twig" %}
2{% block title %}{% trans "home" %}{% endblock %}
3{% block menu %}
4{% include '_menu.twig' %}
5{% endblock %}
6{% block precontent %}
7 <ul id="sort">
8 <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>
9 <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>
10 </ul>
11{% endblock %}
12{% block content %}
13 {{ page_links | raw }}
14 {% for entry in entries %}
15 <div id="entry-{{ entry.id|e }}" class="entrie">
16 <h2><a href="index.php?view=view&id={{ entry.id|e }}">{{ entry.title|e }}</a></h2>
17 <ul class="tools">
18 <li>
19 <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>
20 <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>
21 <li><a title="{% trans "delete" %}" class="tool delete" href="./?action=delete&id={{ entry.id|e }}"><span></span></a></li>
22 </li>
23 </ul>
24 <p>{{ entry.content|striptags|slice(0, 300) }}...</p>
25 <p class="vieworiginal txtright small"><a href="{{ entry.url|e }}" target="_blank" title="{% trans "original" %} : {{ entry.title|e }}">{{ entry.url | e | getDomain }}</a></p>
26 </div>
27 {% endfor %}
28 {{ page_links | raw }}
29{% endblock %} \ No newline at end of file
diff --git a/img/apple-touch-icon-144x144-precomposed.png b/tpl/img/apple-touch-icon-144x144-precomposed.png
index 557b479c..557b479c 100644
--- a/img/apple-touch-icon-144x144-precomposed.png
+++ b/tpl/img/apple-touch-icon-144x144-precomposed.png
Binary files differ
diff --git a/img/apple-touch-icon-72x72-precomposed.png b/tpl/img/apple-touch-icon-72x72-precomposed.png
index e167d3a4..e167d3a4 100644
--- a/img/apple-touch-icon-72x72-precomposed.png
+++ b/tpl/img/apple-touch-icon-72x72-precomposed.png
Binary files differ
diff --git a/img/apple-touch-icon.png b/tpl/img/apple-touch-icon.png
index 4d222fba..4d222fba 100644
--- a/img/apple-touch-icon.png
+++ b/tpl/img/apple-touch-icon.png
Binary files differ
diff --git a/img/favicon.ico b/tpl/img/favicon.ico
index 0e9ff779..0e9ff779 100644
--- a/img/favicon.ico
+++ b/tpl/img/favicon.ico
Binary files differ
diff --git a/img/light/checkmark-off.png b/tpl/img/light/checkmark-off.png
index 3db5a06d..3db5a06d 100644
--- a/img/light/checkmark-off.png
+++ b/tpl/img/light/checkmark-off.png
Binary files differ
diff --git a/img/light/checkmark-on.png b/tpl/img/light/checkmark-on.png
index cd3abb2c..cd3abb2c 100644
--- a/img/light/checkmark-on.png
+++ b/tpl/img/light/checkmark-on.png
Binary files differ
diff --git a/img/down.png b/tpl/img/light/down.png
index b9d536a7..b9d536a7 100644
--- a/img/down.png
+++ b/tpl/img/light/down.png
Binary files differ
diff --git a/tpl/img/light/envelop.png b/tpl/img/light/envelop.png
new file mode 100755
index 00000000..6be1c886
--- /dev/null
+++ b/tpl/img/light/envelop.png
Binary files differ
diff --git a/tpl/img/light/left.png b/tpl/img/light/left.png
new file mode 100755
index 00000000..a0a53631
--- /dev/null
+++ b/tpl/img/light/left.png
Binary files differ
diff --git a/img/light/remove.png b/tpl/img/light/remove.png
index f8ad56a3..f8ad56a3 100644
--- a/img/light/remove.png
+++ b/tpl/img/light/remove.png
Binary files differ
diff --git a/img/light/star-off.png b/tpl/img/light/star-off.png
index 6a0133a7..6a0133a7 100644
--- a/img/light/star-off.png
+++ b/tpl/img/light/star-off.png
Binary files differ
diff --git a/img/light/star-on.png b/tpl/img/light/star-on.png
index a9f96eaa..a9f96eaa 100644
--- a/img/light/star-on.png
+++ b/tpl/img/light/star-on.png
Binary files differ
diff --git a/img/up.png b/tpl/img/light/top.png
index 954a8c0a..954a8c0a 100644..100755
--- a/img/up.png
+++ b/tpl/img/light/top.png
Binary files differ
diff --git a/tpl/img/light/twitter.png b/tpl/img/light/twitter.png
new file mode 100755
index 00000000..cfcfe419
--- /dev/null
+++ b/tpl/img/light/twitter.png
Binary files differ
diff --git a/tpl/img/logo.png b/tpl/img/logo.png
new file mode 100644
index 00000000..549b8466
--- /dev/null
+++ b/tpl/img/logo.png
Binary files differ
diff --git a/img/messages/close.png b/tpl/img/messages/close.png
index 731aa018..731aa018 100644..100755
--- a/img/messages/close.png
+++ b/tpl/img/messages/close.png
Binary files differ
diff --git a/img/messages/cross.png b/tpl/img/messages/cross.png
index 1514d51a..1514d51a 100644..100755
--- a/img/messages/cross.png
+++ b/tpl/img/messages/cross.png
Binary files differ
diff --git a/img/messages/help.png b/tpl/img/messages/help.png
index 5c870176..5c870176 100644..100755
--- a/img/messages/help.png
+++ b/tpl/img/messages/help.png
Binary files differ
diff --git a/img/messages/tick.png b/tpl/img/messages/tick.png
index a9925a06..a9925a06 100644..100755
--- a/img/messages/tick.png
+++ b/tpl/img/messages/tick.png
Binary files differ
diff --git a/img/messages/warning.png b/tpl/img/messages/warning.png
index 628cf2da..628cf2da 100644..100755
--- a/img/messages/warning.png
+++ b/tpl/img/messages/warning.png
Binary files differ
diff --git a/tpl/install.html b/tpl/install.html
deleted file mode 100644
index d11a7810..00000000
--- a/tpl/install.html
+++ /dev/null
@@ -1,30 +0,0 @@
1{include="head"}
2 <body class="light-style">
3 <header>
4 <h1><a href="index.php"><img src="./img/logo.png" alt="logo poche" /></a>poche</h1>
5 </header>
6 <div id="main">
7 <form method="post" action="?install" name="loginform">
8 <fieldset class="w500p center">
9 <h2 class="mbs txtcenter">install your poche</h2>
10 <div class="row">
11 <label class="col w150p" for="login">Login</label>
12 <input class="col" type="text" id="login" name="login" placeholder="Login" tabindex="1" autofocus />
13 </div>
14 <div class="row">
15 <label class="col w150p" for="password">Password</label>
16 <input class="col" type="password" id="password" name="password" placeholder="Password" tabindex="2">
17 </div>
18 <div class="row">
19 <label class="col w150p" for="password_repeat">Repeat your password</label>
20 <input class="col" type="password" id="password_repeat" name="password_repeat" placeholder="Password" tabindex="3">
21 </div>
22 <div class="row mts txtcenter">
23 <button class="bouton" type="submit" tabindex="4">Install</button>
24 </div>
25 </fieldset>
26 <input type="hidden" name="returnurl" value="<?php echo htmlspecialchars($referer);?>">
27 <input type="hidden" name="token" value="<?php echo Session::getToken(); ?>">
28 </form>
29
30{include="footer"}
diff --git a/tpl/install.twig b/tpl/install.twig
new file mode 100644
index 00000000..8bcede0d
--- /dev/null
+++ b/tpl/install.twig
@@ -0,0 +1,28 @@
1{% extends "layout.twig" %}
2{% block title %}{% trans "installation" %}{% endblock %}
3{% block content %}
4 <form method="post" action="?install" name="loginform">
5 <fieldset class="w500p center">
6 <h2 class="mbs txtcenter">{% trans "install your poche" %}</h2>
7 <p>
8 {% 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>." %}
9 </p>
10 <div class="row">
11 <label class="col w150p" for="login">{% trans "Login" %}</label>
12 <input class="col" type="text" id="login" name="login" placeholder="Login" tabindex="1" autofocus />
13 </div>
14 <div class="row">
15 <label class="col w150p" for="password">{% trans "Password" %}</label>
16 <input class="col" type="password" id="password" name="password" placeholder="Password" tabindex="2">
17 </div>
18 <div class="row">
19 <label class="col w150p" for="password_repeat">{% trans "Repeat your password" %}</label>
20 <input class="col" type="password" id="password_repeat" name="password_repeat" placeholder="Password" tabindex="3">
21 </div>
22 <div class="row mts txtcenter">
23 <button class="bouton" type="submit" tabindex="4">{% trans "Install" %}</button>
24 </div>
25 </fieldset>
26 <input type="hidden" name="token" value="{{ token }}">
27 </form>
28{% endblock %} \ No newline at end of file
diff --git a/tpl/js.html b/tpl/js.html
deleted file mode 100644
index a02212b0..00000000
--- a/tpl/js.html
+++ /dev/null
@@ -1,22 +0,0 @@
1 <script type="text/javascript" src="js/jquery-1.9.1.min.js"></script>
2 <script type="text/javascript" src="js/poche.js"></script>
3
4 {if="$load_all_js == '1'"}
5 <script type="text/javascript" src="js/jquery.masonry.min.js"></script>
6 <script type="text/javascript">
7 $( window ).load( function()
8 {
9 var columns = 3,
10 setColumns = function() { columns = $( window ).width() > 640 ? 3 : $( window ).width() > 320 ? 2 : 1; };
11
12 setColumns();
13 $( window ).resize( setColumns );
14
15 $( '#content' ).masonry(
16 {
17 itemSelector: '.entrie',
18 columnWidth: function( containerWidth ) { return containerWidth / columns; }
19 });
20 });
21 </script>
22 {/if} \ No newline at end of file
diff --git a/tpl/layout.twig b/tpl/layout.twig
new file mode 100644
index 00000000..c4bbe779
--- /dev/null
+++ b/tpl/layout.twig
@@ -0,0 +1,29 @@
1<!DOCTYPE html>
2<!--[if lte IE 6]> <html class="no-js ie6 ie67 ie678" lang="en"> <![endif]-->
3<!--[if lte IE 7]> <html class="no-js ie7 ie67 ie678" lang="en"> <![endif]-->
4<!--[if IE 8]> <html class="no-js ie8 ie678" lang="en"> <![endif]-->
5<!--[if gt IE 8]><!--> <html class="no-js" lang="en"> <!--<![endif]-->
6<html>
7 <head>
8 <meta name="viewport" content="width=device-width, minimum-scale=1.0, maximum-scale=1.0">
9 <meta charset="utf-8">
10 <meta http-equiv="X-UA-Compatible" content="IE=10">
11 <title>{% block title %}{% endblock %} - poche</title>
12 {% include '_head.twig' %}
13 {% include '_bookmarklet.twig' %}
14 </head>
15 <body>
16 {% include '_top.twig' %}
17 <div id="main">
18 {% block menu %}{% endblock %}
19 {% block precontent %}{% endblock %}
20 {% block messages %}
21 {% include '_messages.twig' %}
22 {% endblock %}
23 <div id="content" class="w600p center">
24 {% block content %}{% endblock %}
25 </div>
26 </div>
27 {% include '_footer.twig' %}
28 </body>
29</html> \ No newline at end of file
diff --git a/tpl/login.html b/tpl/login.html
deleted file mode 100644
index 69c17a55..00000000
--- a/tpl/login.html
+++ /dev/null
@@ -1,33 +0,0 @@
1{include="head"}
2 <body class="light-style">
3 <header>
4 <h1><a href="index.php"><img src="./img/logo.png" alt="logo poche" /></a>poche</h1>
5 </header>
6 <div id="main">
7 <form method="post" action="?login" name="loginform">
8 <fieldset class="w500p center">
9 <h2 class="mbs txtcenter">login to your poche</h2>
10 <div class="row">
11 <label class="col w150p" for="login">Login</label>
12 <input class="col" type="text" id="login" name="login" placeholder="Login" tabindex="1" autofocus />
13 </div>
14 <div class="row">
15 <label class="col w150p" for="password">Password</label>
16 <input class="col" type="password" id="password" name="password" placeholder="Password" tabindex="2">
17 </div>
18 <div class="row">
19 <label class="col w150p">Stay signed in</label>
20 <div class="col">
21 <input type="checkbox" name="longlastingsession" tabindex="3">
22 <small class="inbl">(Do not check on public computers)</small>
23 </div>
24 </div>
25 <div class="row mts txtcenter">
26 <button class="bouton" type="submit" tabindex="4">Sign in</button>
27 </div>
28 </fieldset>
29 <input type="hidden" name="returnurl" value="<?php echo htmlspecialchars($referer);?>">
30 <input type="hidden" name="token" value="<?php echo Session::getToken(); ?>">
31 </form>
32
33{include="footer"}
diff --git a/tpl/login.twig b/tpl/login.twig
new file mode 100644
index 00000000..0ae130bc
--- /dev/null
+++ b/tpl/login.twig
@@ -0,0 +1,32 @@
1{% extends "layout.twig" %}
2
3{% block title %}{% trans "login to your poche" %}{% endblock %}
4{% block content %}
5 <form method="post" action="?login" name="loginform">
6 <fieldset class="w500p center">
7 <h2 class="mbs txtcenter">{% trans "login to your poche" %}</h2>
8 {% if constant('MODE_DEMO') == 1 %}<p>{% trans "you are in demo mode, some features may be disabled." %}</p>{% endif %}
9 <div class="row">
10 <label class="col w150p" for="login">{% trans "Login" %}</label>
11 <input class="col" type="text" id="login" name="login" placeholder="Login" tabindex="1" autofocus {% if constant('MODE_DEMO') == 1 %}value="poche"{% endif %} />
12 </div>
13
14 <div class="row">
15 <label class="col w150p" for="password">{% trans "Password" %}</label>
16 <input class="col" type="password" id="password" name="password" placeholder="Password" tabindex="2" {% if constant('MODE_DEMO') == 1 %}value="poche"{% endif %} />
17 </div>
18 <div class="row">
19 <label class="col w150p" for="longlastingsession">{% trans "Stay signed in" %}</label>
20 <div class="col">
21 <input type="checkbox" id="longlastingsession" name="longlastingsession" tabindex="3">
22 <small class="inbl">{% trans "(Do not check on public computers)" %}</small>
23 </div>
24 </div>
25 <div class="row mts txtcenter">
26 <button class="bouton" type="submit" tabindex="4">{% trans "Login" %}</button>
27 </div>
28 </fieldset>
29 <input type="hidden" name="returnurl" value="{{ referer }}">
30 <input type="hidden" name="token" value="{{ token }}">
31 </form>
32{% endblock %} \ No newline at end of file
diff --git a/tpl/messages.html b/tpl/messages.html
deleted file mode 100644
index 87af259b..00000000
--- a/tpl/messages.html
+++ /dev/null
@@ -1 +0,0 @@
1<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
index af94df2e..00000000
--- a/tpl/view.html
+++ /dev/null
@@ -1,53 +0,0 @@
1<!DOCTYPE html>
2<!--[if lte IE 6]> <html class="no-js ie6 ie67 ie678" lang="en"> <![endif]-->
3<!--[if lte IE 7]> <html class="no-js ie7 ie67 ie678" lang="en"> <![endif]-->
4<!--[if IE 8]> <html class="no-js ie8 ie678" lang="en"> <![endif]-->
5<!--[if gt IE 8]><!--> <html class="no-js" lang="en"> <!--<![endif]-->
6<html>
7 <head>
8 <meta name="viewport" content="width=device-width, minimum-scale=1.0, maximum-scale=1.0">
9 <meta charset="utf-8">
10 <meta http-equiv="X-UA-Compatible" content="IE=10">
11 <title>{$title}</title>
12 <link rel="shortcut icon" type="image/x-icon" href="./img/favicon.ico" />
13 <link rel="apple-touch-icon-precomposed" sizes="144x144" href="./img/apple-touch-icon-144x144-precomposed.png">
14 <link rel="apple-touch-icon-precomposed" sizes="72x72" href="./img/apple-touch-icon-72x72-precomposed.png">
15 <link rel="apple-touch-icon-precomposed" href="./img/apple-touch-icon-precomposed.png">
16 <link rel="stylesheet" href="./css/knacss.css" media="all">
17 <link rel="stylesheet" href="./css/style.css" media="all">
18 <!-- Light Theme -->
19 <link rel="stylesheet" href="./css/style-light.css" media="all" title="light-style">
20 <!-- Dark Theme -->
21 <link rel="alternate stylesheet" href="./css/style-dark.css" media="all" title="dark-style">
22 </head>
23 <body class="article light-style">
24 <div id="article" class="w600p">
25 <div class="backhome">
26 <a href="index.php" title="back to home">&larr;</a>
27 </div>
28 <div class="tools">
29 <ul>
30 <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>
31 <li><a href="#" id="themeswitch">dark</a></li>
32 <li><a title="toggle favorite" class="tool fav {if="$is_fav == '0'"}fav-off{/if}" onclick="toggle_favorite(this, {$id})"><span></span></a></li>
33 <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>
34 <li><a href="?logout" title="Logout">logout</a></li>
35 </ul>
36 </div>
37 <header class="mbm">
38 <h1><a href="{$url}">{$title}</a></h1>
39 <div class="vieworiginal txtright small"><a href="{$url}" target="_blank" title="original : {$title}">view original</a></div>
40 </header>
41 {include="messages"}
42 <article>
43 <div id="readityourselfcontent">
44 {$content}
45 </div>
46 </article>
47 <div class="vieworiginal txtright small"><a href="{$url}" target="_blank" title="original : {$title}">view original</a></div>
48 <div class="backhome">
49 <a href="index.php" title="back to home">&larr;</a>
50 </div>
51
52 {include="js"}
53 {include="footer"} \ No newline at end of file
diff --git a/tpl/view.twig b/tpl/view.twig
new file mode 100644
index 00000000..e999afa1
--- /dev/null
+++ b/tpl/view.twig
@@ -0,0 +1,40 @@
1{% extends "layout.twig" %}
2{% block title %}{% trans "home" %}{% endblock %}
3{% block content %}
4 <div id="article">
5 <div class="tools">
6 <ul class="tools">
7 <li>
8 <li><a href="{{ referer }}" title="{% trans "back to home" %}" class="tool back"><span></span></a></li>
9 <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>
10 <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>
11 <li><a title="{% trans "delete" %}" class="tool delete" href="./?action=delete&id={{ entry.id|e }}"><span></span></a></li>
12 {% 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 %}
13 {% 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 %}
14 </li>
15 </ul>
16 </div>
17 <header class="mbm">
18 <h1>{{ entry.title|e }}</h1>
19 <div class="vieworiginal txtright small"><a href="{{ entry.url|e }}" target="_blank" title="{% trans "original" %} : {{ entry.title|e }}">{{ entry.url | e | getDomain }}</a></div>
20 </header>
21 <article>
22 {{ content | raw }}
23 <div class="vieworiginal txtright small"><a href="{{ entry.url|e }}" target="_blank" title="{% trans "original" %} : {{ entry.title|e }}">{{ entry.url | e | getDomain }}</a></div>
24 </article>
25 <div class="tools">
26 <ul class="tools">
27 <li>
28 <li><a href="{{ referer }}" title="{% trans "back to home" %}" class="tool back"><span></span></a></li>
29 <li><a href="#" title="{% trans "back to top" %}" class="tool top"><span></span></a></li>
30 <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>
31 <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>
32 <li><a title="{% trans "delete" %}" class="tool delete" href="./?action=delete&id={{ entry.id|e }}"><span></span></a></li>
33 {% 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 %}
34 {% 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 %}
35 </li>
36 </ul>
37 <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>
38 </div>
39 </div>
40{% endblock %} \ No newline at end of file