]> git.immae.eu Git - github/shaarli/Shaarli.git/commitdiff
Merge pull request #59 from tst2005/master
authornodiscc <nodiscc@gmail.com>
Thu, 13 Nov 2014 00:48:52 +0000 (01:48 +0100)
committernodiscc <nodiscc@gmail.com>
Thu, 13 Nov 2014 00:48:52 +0000 (01:48 +0100)
uniform if syntax

31 files changed:
.gitignore
COPYING
README.md
cache/.htaccess [new file with mode: 0644]
data/.htaccess [new file with mode: 0644]
images/calendar.png
images/delete_icon.png
images/edit_icon.png
images/feed-icon-14x14.png
images/floral_left.png
images/floral_right.png
images/private.png
images/private_16x16.png
images/private_16x16_active.png
images/qrcode.png
images/squiggle.png
images/squiggle2.png
images/squiggle_closing.png
images/tag_blue.png
inc/qr.min.js
inc/shaarli.css
index.php
pagecache/.htaccess [new file with mode: 0644]
shaarli_version.txt [new file with mode: 0644]
tmp/.htaccess [new file with mode: 0644]
tpl/configure.html
tpl/import.html
tpl/linklist.html
tpl/page.header.html
tpl/tagcloud.html
tpl/tools.html

index bcd745e4a00f249a15534c8b7a6be6f474d4f767..6452c2c69e68f3566248dff00956293ee588a170 100644 (file)
@@ -7,4 +7,7 @@ pagecache
 # Eclipse project files
 .settings
 .buildpath
-.project
\ No newline at end of file
+.project
+
+# Ignore raintpl generated pages
+*.rtpl.php
\ No newline at end of file
diff --git a/COPYING b/COPYING
index d9731d27d269136a484b8b0dbca9dafe812aa32c..91ef03402d66ee06bfc6226f3683ea0e7716fed5 100644 (file)
--- a/COPYING
+++ b/COPYING
@@ -1,6 +1,52 @@
-Shaarli is distributed under the zlib/libpng License:
+Files: * 
+License: zlib/libpng
+Copyright: (c) 2011-2014 Sébastien SAUVAGE <sebsauvage@sebsauvage.net>
+           (c) 2011-2014 Alexandre Alapetite <alexandre@alapetite.fr>
+           (c) 2011-2014 David Sferruzza <david.sferruzza@gmail.com>
+           (c) 2011-2014 Christophe HENRY <christophe.henry@sbgodin.fr>
+           (c) 2011-2014 Mathieu Chabanon <git@matchab.fr>
+           (c) 2011-2014 BoboTiG <bobotig@gmail.com>
+           (c) 2011-2014 Bronco <bronco@warriordudimanche.net>
+           (c) 2011-2014 Emilien Klein <emilien@klein.st>
+           (c) 2011-2014 Knah Tsaeb <knah-tsaeb@knah-tsaeb.org>
+           (c) 2011-2014 Lionel Martin <renarddesmers@gmail.com>
+           (c) 2011-2014 lehollandaisvolant <levoltigeurhollandais@gmail.com>
+           (c) 2011-2014 timo van neerden <fire@lehollandaisvolant.net>
+           (c) 2011-2014 nodiscc <nodiscc@gmail.com>
+
+
+
+Files: images/calendar.png, images/edit_icon.png, images/feed-icon-14x14.png, images/private.png, images/private_16x16.png, images/private_16x16_active.png, images/qrcode.png, images/tag_blue.png
+License: CC-BY (http://creativecommons.org/licenses/by/3.0/)
+Copyright: (c) 2014 Yusuke Kamiyamane
+Source: http://p.yusukekamiyamane.com/
+
+Files: images/delete_icon.png
+License: CC-BY (http://creativecommons.org/licenses/by/3.0/)
+Copyright: (c) 2014 Designmodo
+Source: http://designmodo.com/linecons-free/
+
+
+Files: images/floral_left.png, images/floral_right.png, images/squiggle.png, images/squiggle2.png, images/squiggle_closing.png
+Licence: Public Domain
+Source: https://openclipart.org/people/j4p4n/j4p4n_ornimental_bookend_-_left.svg
+
+
+Files: images/Paper_texture_v5_by_bashcorpo_w1000.jpg
+Licence: Public Domain
+Source: http://bashcorpo.deviantart.com/art/Grungy-paper-texture-v-5-22966998
+
+Files: images/logo.png
+License: zlib/libpng
+Copyright: (c) 2011-2014 idleman idleman@idleman.fr
+
+Files: ins/qr.min.js
+License: GPLv3
+Copyright: (C) 2014 Alasdair Mercer, http://neocotic.com
+
+--------------------------------------------------------
+ZLIB/LIBPNG LICENSE
 
-Copyright (c) 2011 Sébastien SAUVAGE (sebsauvage.net)
 
 This software is provided 'as-is', without any express or implied warranty.
 In no event will the authors be held liable for any damages arising from
@@ -19,3 +65,19 @@ freely, subject to the following restrictions:
      not be misrepresented as being the original software.
 
   3. This notice may not be removed or altered from any source distribution.
+
+----------------------------------------------------
+GPLv3 LICENSE
+
+This program is free software: you can redistribute it and/or modify  
+it under the terms of the GNU General Public License as published by  
+the Free Software Foundation, either version 3 of the License, or  
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,  
+but WITHOUT ANY WARRANTY; without even the implied warranty of  
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the  
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License  
+along with this program. If not, see <http://www.gnu.org/licenses/>.
\ No newline at end of file
index cff718cfc556dd0f5674f2252ba78b0cd8900e35..d07437836226bf9fe8055daefe89f2ce897c452b 100644 (file)
--- a/README.md
+++ b/README.md
@@ -1,71 +1,76 @@
-![Shaarli logo](http://sebsauvage.net/wiki/lib/exe/fetch.php?media=php:php_shaarli:php_shaarli_logo_inkscape_w600_transp-nq8.png)
+![Shaarli logo](https://cdn.mediacru.sh/W2NGCIHB3quT.png)
 
 Shaarli, the personal, minimalist, super-fast, no-database delicious clone.
 
 You want to share the links you discover ? Shaarli is a minimalist delicious clone you can install on your own website.
-It is designed to be personal (single-user), fast and handy. 
+It is designed to be personal (single-user), fast and handy.
 
 
-Features:
+## Features:
 
  * Minimalist design (simple is beautiful)
  * **FAST**
  * Dead-simple installation: Drop the files, open the page. No database required.
- * Easy to use: Single button in your browser to bookmark a page
- * Save url, title, description (unlimited size). Classify links with tags (with autocomplete)
- * Tag renaming, merging and deletion.
- * Automatic thumbnails for various services (imgur, imageshack.us, flickr, youtube, vimeo, dailymotion…)
- * Automatic conversion of URLs to clickable links in descriptions. Support for http/ftp/file/apt/magnet protocols.
- * Save links as public or private
- * 1-clic access to your private links/notes
- * Browse links by page, filter by tag or use the full text search engine
- * Permalinks (with QR-Code) for easy reference
- * RSS and ATOM feeds (which can be filtered by tag or text search)
- * Tag cloud
- * Picture wall (which can be filtered by tag or text search)
- * “Links of the day” Newspaper-like digest, browsable by day.
- * “Daily” RSS feed: Get each day a digest of all new links.
- * [PubSubHubbub](https://code.google.com/p/pubsubhubbub/) protocol support
+ * Easy to use: Single button in your browser to bookmark a page (**bookmarklet**)
+ * Save **URL, title, description** (unlimited size).
+ * Classify, search and filter links with **tags**.
+  * Tag autocompletion, renaming, merging and deletion.
+ * Save links as **public or private**
+ * Browse links by page, filter by tag or use the **full text search engine**
+ * **Tag cloud**
+ * **Picture wall** (which can be filtered by tag or text search)
+ * **“Daily”** Newspaper-like digest, browsable by day.
+ * **Permalinks** (with QR-Code) for easy reference
+ * **RSS** and ATOM feeds
+  * Can be filtered by tag or text search!
+  * “Daily” RSS feed: Get each day a digest of all new links.
+ * Can **import/export** Netscape bookmarks (for import/export from/to Firefox, Opera, Chrome, Delicious…)
+ * Automatic **image/video thumbnails** for various services (imgur, imageshack.us, flickr, youtube, vimeo, dailymotion…)
+ * Support for http/ftp/file/apt/magnet protocol links
+  * URLs in descriptions are automatically converted to clickable links in descriptions
  * Easy backup (Data stored in a single file)
+ * 1-click access to your private links/notes
  * Compact storage (1315 links stored in 150 kb)
  * Mobile browsers support
  * Also works with javascript disabled
- * Can import/export Netscape bookmarks (for import/export from/to Firefox, Opera, Chrome, Delicious…)
  * Brute force protected login form
- * Protected against [XSRF](http://en.wikipedia.org/wiki/Cross-site_request_forgery), session cookie hijacking.
+ * [PubSubHubbub](https://code.google.com/p/pubsubhubbub/) protocol support
  * Automatic removal of annoying FeedBurner/Google FeedProxy parameters in URL (?utm_source…)
- * Shaarli is a bookmarking application, but you can use it for micro-blogging (like Twitter), a pastebin, an online notepad, a snippet repository, etc.
- * You will be automatically notified by a discreet popup if a new version is available
  * Pages are easy to customize (using CSS and simple RainTPL templates)
+ * Protected against [XSRF](http://en.wikipedia.org/wiki/Cross-site_request_forgery), session cookie hijacking.
+ * You will be automatically notified by a discreet popup if a new version is available
+ * **Shaarli is a bookmarking application, but you can use it for micro-blogging (like Twitter), a pastebin, an online notepad, a snippet repository, etc. See [Usage examples](https://github.com/shaarli/Shaarli/wiki#usage-examples)**
+
+## Links
+ * **[Wiki/documentation](https://github.com/shaarli/Shaarli/wiki)**
+ * [Bugs/Feature requests/Discussion](https://github.com/shaarli/Shaarli/issues/)
+
 
+## Installing
+Shaarli requires php 5.1
 
-Requires php 5.1
+ * Download the latest stable release from https://github.com/shaarli/Shaarli/releases
+ * Unpack the archive in a directory on your web server
+ * Visit this directory from a web browser.
+ * Choose login, password, timezone and page title. Save.
 
-More information on the project page:
-http://sebsauvage.net/wiki/doku.php?id=php:shaarli
+_To get the development version, download https://github.com/shaarli/Shaarli/archive/master.zip or `git clone https://github.com/shaarli/Shaarli`_
 
-------------------------------------------------------------------------------
+## Upgrading
+Delete all files and directories except the `data` directory, then unzip the new version of Shaarli.  
+You will not lose your links and you will not have to reconfigure it.
 
-Shaarli is distributed under the zlib/libpng License:
 
-Copyright (c) 2011 Sébastien SAUVAGE (sebsauvage.net)
+## Screenshots
 
-This software is provided 'as-is', without any express or implied warranty.
-In no event will the authors be held liable for any damages arising from
-the use of this software.
+[![](https://cdn.mediacru.sh/AjZc6-emICeO.png)](https://cdn.mediacru.sh/kE8SyD-PvGuC.png)  [![](https://cdn.mediacru.sh/MfC-DzklMYs2.png)](https://cdn.mediacru.sh/iqTvO1-yP9pU.png) [![](https://cdn.mediacru.sh/dxmXskaubYcg.png)](https://cdn.mediacru.sh/mMoi31f94wdL.png) [![](https://cdn.mediacru.sh/-ptB2veFivBp.png)](https://cdn.mediacru.sh/GcoZPZmCZ-DR.png) [![](https://cdn.mediacru.sh/QmRdTAr8x427.png)](https://cdn.mediacru.sh/TDDujpMWT31q.png)
 
-Permission is granted to anyone to use this software for any purpose,
-including commercial applications, and to alter it and redistribute it 
-freely, subject to the following restrictions:
+## About
 
-  1. The origin of this software must not be misrepresented; you must not 
-     claim that you wrote the original software. If you use this software
-     in a product, an acknowledgment in the product documentation would
-     be appreciated but is not required.
+Original Project page: http://sebsauvage.net/wiki/doku.php?id=php:shaarli  
+Shaarli is developed by [Sébastien SAUVAGE](http://sebsauvage.net) and [contributors](COPYING).
 
-  2. Altered source versions must be plainly marked as such, and must
-     not be misrepresented as being the original software.
+Shaarli is [Free Software](https://en.wikipedia.org/wiki/Free_software) distributed under the [zlib/libpng License](http://www.gzip.org/zlib/zlib_license.html)
 
-  3. This notice may not be removed or altered from any source distribution.
+This friendly fork is maintained by the community at https://github.com/shaarli/Shaarli
 
-------------------------------------------------------------------------------
diff --git a/cache/.htaccess b/cache/.htaccess
new file mode 100644 (file)
index 0000000..b584d98
--- /dev/null
@@ -0,0 +1,2 @@
+Allow from none
+Deny from all
diff --git a/data/.htaccess b/data/.htaccess
new file mode 100644 (file)
index 0000000..b584d98
--- /dev/null
@@ -0,0 +1,2 @@
+Allow from none
+Deny from all
index 658913852d60fc6ca8557568d26b8e93e7d56525..81c745197952cf7da2bf2db9822a0e62a3c600a1 100644 (file)
Binary files a/images/calendar.png and b/images/calendar.png differ
index 55e388b4ad0e8b65d52a4706493884c9b9324df0..810b94d829fbbce0514c635c08c0649504c0e57b 100644 (file)
Binary files a/images/delete_icon.png and b/images/delete_icon.png differ
index 5cff57433ffb4763a7ede6b6f115b59b9d185a39..16c440c86787658b3df8c005b0d27df9c8a6a6fe 100644 (file)
Binary files a/images/edit_icon.png and b/images/edit_icon.png differ
index b3c949d2244f2c0c81d65e74719af2a1b56d06a3..101617027fda16e31aab926d6a5882d7d52c8481 100644 (file)
Binary files a/images/feed-icon-14x14.png and b/images/feed-icon-14x14.png differ
index 5a4ad89b5c467e7ec1ed9fa7bbc888954948e6f2..f09a861df4e5243c964b211de24cf06fabfcb3ba 100644 (file)
Binary files a/images/floral_left.png and b/images/floral_left.png differ
index cb7b201fcb4182ea60f1dcec7ae9bab02757c1cd..0dfb6112965efa469668c5f4b2807d229d5e6c3b 100644 (file)
Binary files a/images/floral_right.png and b/images/floral_right.png differ
index 5cea272ae3b391f6c769e5b501368fabc813778a..1364b355f3f32832a0a6a02b026cf2ebb453f36f 100644 (file)
Binary files a/images/private.png and b/images/private.png differ
index d58c4823cea04964a2125b71b625d87c4fa40ace..8bb34d7d8b6292854f6c2f3bc9a236102f6b2b36 100644 (file)
Binary files a/images/private_16x16.png and b/images/private_16x16.png differ
index dd43baf2f888becd1452f8744c8825eb8f1405ac..af990d2cb8b599e507be610d248c16ac6b9293da 100644 (file)
Binary files a/images/private_16x16_active.png and b/images/private_16x16_active.png differ
index 99f252673f6393c12c102d635ed978ffa917246e..c2cfa4765abf4f837d36536299b9b0715acfea4d 100644 (file)
Binary files a/images/qrcode.png and b/images/qrcode.png differ
index 9fd2129d01b8ffdb594f84f7c14f0274ab347af6..a6ce218c71972a1f48e1e7026a8764e2b2a49c9b 100644 (file)
Binary files a/images/squiggle.png and b/images/squiggle.png differ
index 23409ce371784c6a8b7f83218f48103d1c34cc09..c795f0a393cd61f81329c1c2e654c23b235f76ca 100644 (file)
Binary files a/images/squiggle2.png and b/images/squiggle2.png differ
index 901fff5fe30748043714389a48527a688c3447b0..3f9d02b1729f2d5b9165c075750f78222549b508 100644 (file)
Binary files a/images/squiggle_closing.png and b/images/squiggle_closing.png differ
index 9757fc6ed6597438eb8e5a70a1ab2402cdebd5d1..7ec902fce2c9b38e78a08dfdc21a11f714eb8c9c 100644 (file)
Binary files a/images/tag_blue.png and b/images/tag_blue.png differ
index 663ce9303316fee88cea7976ea7ad443d493545d..19d704e18dcff3859d2bcf3bf7c54ad7cd6215e5 100644 (file)
@@ -1,9 +1,5 @@
-// [qr.js](http://neocotic.com/qr.js) 1.0.3
-// (c) 2011 Alasdair Mercer
-// Freely distributable under the MIT license.
-// Based on jsqrencode
-// (c) 2010 tz@execpc.com
-// Licensed under the GPL Version 3 license.
-// For all details and documentation:
-// http://neocotic.com/qr.js
-(function(a){function Q(a){var c,h,i,j,k,m,n,u;k=a.length;C=0;do{C++;i=(r-1)*4+(C-1)*16;z=d[i++];A=d[i++];o=d[i++];p=d[i];i=o*(z+A)+A-3+(C<=9);if(k<=i)break}while(C<40);D=17+4*C;m=o+(o+p)*(z+A)+A;for(k=0;k<m;k++)q[k]=0;B=a.slice(0);for(k=0;k<D*D;k++)s[k]=0;for(k=0;k<(D*(D+1)+1)/2;k++)t[k]=0;for(k=0;k<3;k++){i=0;u=0;if(k===1)i=D-7;if(k===2)u=D-7;s[u+3+D*(i+3)]=1;for(n=0;n<6;n++){s[u+n+D*i]=1;s[u+D*(i+n+1)]=1;s[u+6+D*(i+n)]=1;s[u+n+1+D*(i+6)]=1}for(n=1;n<5;n++){I(u+n,i+1);I(u+1,i+n+1);I(u+5,i+n);I(u+n+1,i+5)}for(n=2;n<4;n++){s[u+n+D*(i+2)]=1;s[u+2+D*(i+n+1)]=1;s[u+4+D*(i+n)]=1;s[u+n+1+D*(i+4)]=1}}if(C>1){k=b[C];u=D-7;for(;;){n=D-7;while(n>k-3){J(n,u);if(n<k)break;n-=k}if(u<=k+9)break;u-=k;J(6,u);J(u,6)}}s[8+D*(D-8)]=1;for(u=0;u<7;u++){I(7,u);I(D-8,u);I(7,u+D-7)}for(n=0;n<8;n++){I(n,7);I(n+D-8,7);I(n,D-8)}for(n=0;n<9;n++)I(n,8);for(n=0;n<8;n++){I(n+D-8,8);I(8,n)}for(u=0;u<7;u++)I(8,u+D-7);for(n=0;n<D-14;n++){if(n&1){I(8+n,6);I(6,8+n)}else{s[8+n+D*6]=1;s[6+D*(8+n)]=1}}if(C>6){k=l[C-7];i=17;for(n=0;n<6;n++){for(u=0;u<3;u++,i--){if(1&(i>11?C>>i-12:k>>i)){s[5-n+D*(2-u+D-11)]=1;s[2-u+D-11+D*(5-n)]=1}else{I(5-n,2-u+D-11);I(2-u+D-11,5-n)}}}}for(u=0;u<D;u++){for(n=0;n<=u;n++){if(s[n+D*u])I(n,u)}}m=B.length;for(c=0;c<m;c++)q[c]=B.charCodeAt(c);B=q.slice(0);n=o*(z+A)+A;if(m>=n-2){m=n-2;if(C>9)m--}c=m;if(C>9){B[c+2]=0;B[c+3]=0;while(c--){k=B[c];B[c+3]|=255&k<<4;B[c+2]=k>>4}B[2]|=255&m<<4;B[1]=m>>4;B[0]=64|m>>12}else{B[c+1]=0;B[c+2]=0;while(c--){k=B[c];B[c+2]|=255&k<<4;B[c+1]=k>>4}B[1]|=255&m<<4;B[0]=64|m>>4}c=m+3-(C<10);while(c<n){B[c++]=236;B[c++]=17}x[0]=1;for(c=0;c<p;c++){x[c+1]=1;for(h=c;h>0;h--){x[h]=x[h]?x[h-1]^f[K(g[x[h]]+c)]:x[h-1]}x[0]=f[K(g[x[0]]+c)]}for(c=0;c<=p;c++)x[c]=g[x[c]];i=n;u=0;for(c=0;c<z;c++){L(u,o,i,p);u+=o;i+=p}for(c=0;c<A;c++){L(u,o+1,i,p);u+=o+1;i+=p}u=0;for(c=0;c<o;c++){for(h=0;h<z;h++){q[u++]=B[c+h*o]}for(h=0;h<A;h++){q[u++]=B[z*o+c+h*(o+1)]}}for(h=0;h<A;h++){q[u++]=B[z*o+c+h*(o+1)]}for(c=0;c<p;c++){for(h=0;h<z+A;h++){q[u++]=B[n+c+h*p]}}B=q;n=u=D-1;i=m=1;j=(o+p)*(z+A)+A;for(c=0;c<j;c++){k=B[c];for(h=0;h<8;h++,k<<=1){if(128&k)s[n+D*u]=1;do{if(m){n--}else{n++;if(i){if(u!==0){u--}else{n-=2;i=!i;if(n===6){n--;u=9}}}else{if(u!==D-1){u++}else{n-=2;i=!i;if(n===6){n--;u-=8}}}}m=!m}while(M(n,u))}}B=s.slice(0);k=0;u=3e4;for(i=0;i<8;i++){N(i);n=P();if(n<u){u=n;k=i}if(k===7)break;s=B.slice(0)}if(k!==i)N(k);u=e[k+(r-1<<3)];for(i=0;i<8;i++,u>>=1){if(u&1){s[D-1-i+D*8]=1;if(i<6){s[8+D*i]=1}else{s[8+D*(i+1)]=1}}}for(i=0;i<7;i++,u>>=1){if(u&1){s[8+D*(D-7+i)]=1;if(i){s[6-i+D*8]=1}else{s[7+D*8]=1}}}return s}function P(){var a,b,c,d,e,f,g=0,h=0,j=0;for(f=0;f<D-1;f++){for(e=0;e<D-1;e++){if(s[e+D*f]&&s[e+1+D*f]&&s[e+D*(f+1)]&&s[e+1+D*(f+1)]||!(s[e+D*f]||s[e+1+D*f]||s[e+D*(f+1)]||s[e+1+D*(f+1)])){g+=i}}}for(f=0;f<D;f++){m[0]=0;for(d=a=e=0;e<D;e++){if((b=s[e+D*f])===a){m[d]++}else{m[++d]=1}a=b;h+=a?1:-1}g+=O(d)}if(h<0)h=-h;c=h;c+=c<<2;c<<=1;while(c>D*D){c-=D*D;j++}g+=j*k;for(e=0;e<D;e++){m[0]=0;for(d=a=f=0;f<D;f++){if((b=s[e+D*f])===a){m[d]++}else{m[++d]=1}a=b}g+=O(d)}return g}function O(a){var b=0,c;for(c=0;c<=a;c++){if(m[c]>=5)b+=h+m[c]-5}for(c=3;c<a-1;c+=2){if(m[c-2]===m[c+2]&&m[c+2]===m[c-1]&&m[c-1]===m[c+1]&&m[c-1]*3===m[c]&&(m[c-3]===0||c+3>a||m[c-3]*3>=m[c]*4||m[c+3]*3>=m[c]*4)){b+=j}}return b}function N(a){var b,c,d,e;switch(a){case 0:for(c=0;c<D;c++){for(b=0;b<D;b++){if(!(b+c&1)&&!M(b,c)){s[b+c*D]^=1}}}break;case 1:for(c=0;c<D;c++){for(b=0;b<D;b++){if(!(c&1)&&!M(b,c))s[b+c*D]^=1}}break;case 2:for(c=0;c<D;c++){for(d=0,b=0;b<D;b++,d++){if(d===3)d=0;if(!d&&!M(b,c))s[b+c*D]^=1}}break;case 3:for(e=0,c=0;c<D;c++,e++){if(e===3)e=0;for(d=e,b=0;b<D;b++,d++){if(d===3)d=0;if(!d&&!M(b,c))s[b+c*D]^=1}}break;case 4:for(c=0;c<D;c++){for(d=0,e=c>>1&1,b=0;b<D;b++,d++){if(d===3){d=0;e=!e}if(!e&&!M(b,c))s[b+c*D]^=1}}break;case 5:for(e=0,c=0;c<D;c++,e++){if(e===3)e=0;for(d=0,b=0;b<D;b++,d++){if(d===3)d=0;if(!((b&c&1)+!(!d|!e))&&!M(b,c)){s[b+c*D]^=1}}}break;case 6:for(e=0,c=0;c<D;c++,e++){if(e===3)e=0;for(d=0,b=0;b<D;b++,d++){if(d===3)d=0;if(!((b&c&1)+(d&&d===e)&1)&&!M(b,c)){s[b+c*D]^=1}}}break;case 7:for(e=0,c=0;c<D;c++,e++){if(e===3)e=0;for(d=0,b=0;b<D;b++,d++){if(d===3)d=0;if(!((d&&d===e)+(b+c&1)&1)&&!M(b,c)){s[b+c*D]^=1}}}break}}function M(a,b){var c;if(a>b){c=a;a=b;b=c}c=b;c+=b*b;c>>=1;c+=a;return t[c]}function L(a,b,c,d){var e,h,i;for(h=0;h<d;h++)B[c+h]=0;for(h=0;h<b;h++){e=g[B[a+h]^B[c]];if(e!==255){for(i=1;i<d;i++){B[c+i-1]=B[c+i]^f[K(e+x[d-i])]}}else{for(i=c;i<c+d;i++){B[i]=B[i+1]}}B[c+d-1]=e===255?0:f[K(e+x[0])]}}function K(a){while(a>=255){a-=255;a=(a>>8)+(a&255)}return a}function J(a,b){var c;s[a+D*b]=1;for(c=-2;c<2;c++){s[a+c+D*(b-2)]=1;s[a-2+D*(b+c+1)]=1;s[a+2+D*(b+c)]=1;s[a+c+1+D*(b+2)]=1}for(c=0;c<2;c++){I(a-1,b+c);I(a+1,b-c);I(a-c,b-1);I(a+c,b+1)}}function I(a,b){var c;if(a>b){c=a;a=b;b=c}c=b;c*=b;c+=b;c>>=1;c+=a;t[c]=1}function H(a,b,c){try{var d=a.apply(c||this);if(typeof b==="function")return b(null,d);return d}catch(e){if(typeof b==="function")return b(e);throw e}}function G(){for(var a=arguments.length;a>=0;--a){if(typeof arguments[a]==="function")return arguments[a]}}function F(){return w?new v:a.document.createElement("img")}function E(){return w?new n:a.document.createElement("canvas")}var b=[0,11,15,19,23,27,31,16,18,20,22,24,26,28,20,22,24,24,26,28,28,22,24,24,26,26,28,28,24,24,26,26,26,28,28,24,26,26,26,28,28],c="image/octet-stream",d=[1,0,19,7,1,0,16,10,1,0,13,13,1,0,9,17,1,0,34,10,1,0,28,16,1,0,22,22,1,0,16,28,1,0,55,15,1,0,44,26,2,0,17,18,2,0,13,22,1,0,80,20,2,0,32,18,2,0,24,26,4,0,9,16,1,0,108,26,2,0,43,24,2,2,15,18,2,2,11,22,2,0,68,18,4,0,27,16,4,0,19,24,4,0,15,28,2,0,78,20,4,0,31,18,2,4,14,18,4,1,13,26,2,0,97,24,2,2,38,22,4,2,18,22,4,2,14,26,2,0,116,30,3,2,36,22,4,4,16,20,4,4,12,24,2,2,68,18,4,1,43,26,6,2,19,24,6,2,15,28,4,0,81,20,1,4,50,30,4,4,22,28,3,8,12,24,2,2,92,24,6,2,36,22,4,6,20,26,7,4,14,28,4,0,107,26,8,1,37,22,8,4,20,24,12,4,11,22,3,1,115,30,4,5,40,24,11,5,16,20,11,5,12,24,5,1,87,22,5,5,41,24,5,7,24,30,11,7,12,24,5,1,98,24,7,3,45,28,15,2,19,24,3,13,15,30,1,5,107,28,10,1,46,28,1,15,22,28,2,17,14,28,5,1,120,30,9,4,43,26,17,1,22,28,2,19,14,28,3,4,113,28,3,11,44,26,17,4,21,26,9,16,13,26,3,5,107,28,3,13,41,26,15,5,24,30,15,10,15,28,4,4,116,28,17,0,42,26,17,6,22,28,19,6,16,30,2,7,111,28,17,0,46,28,7,16,24,30,34,0,13,24,4,5,121,30,4,14,47,28,11,14,24,30,16,14,15,30,6,4,117,30,6,14,45,28,11,16,24,30,30,2,16,30,8,4,106,26,8,13,47,28,7,22,24,30,22,13,15,30,10,2,114,28,19,4,46,28,28,6,22,28,33,4,16,30,8,4,122,30,22,3,45,28,8,26,23,30,12,28,15,30,3,10,117,30,3,23,45,28,4,31,24,30,11,31,15,30,7,7,116,30,21,7,45,28,1,37,23,30,19,26,15,30,5,10,115,30,19,10,47,28,15,25,24,30,23,25,15,30,13,3,115,30,2,29,46,28,42,1,24,30,23,28,15,30,17,0,115,30,10,23,46,28,10,35,24,30,19,35,15,30,17,1,115,30,14,21,46,28,29,19,24,30,11,46,15,30,13,6,115,30,14,23,46,28,44,7,24,30,59,1,16,30,12,7,121,30,12,26,47,28,39,14,24,30,22,41,15,30,6,14,121,30,6,34,47,28,46,10,24,30,2,64,15,30,17,4,122,30,29,14,46,28,49,10,24,30,24,46,15,30,4,18,122,30,13,32,46,28,48,14,24,30,42,32,15,30,20,4,117,30,40,7,47,28,43,22,24,30,10,67,15,30,19,6,118,30,18,31,47,28,34,34,24,30,20,61,15,30],e=[30660,29427,32170,30877,26159,25368,27713,26998,21522,20773,24188,23371,17913,16590,20375,19104,13663,12392,16177,14854,9396,8579,11994,11245,5769,5054,7399,6608,1890,597,3340,2107],f=[1,2,4,8,16,32,64,128,29,58,116,232,205,135,19,38,76,152,45,90,180,117,234,201,143,3,6,12,24,48,96,192,157,39,78,156,37,74,148,53,106,212,181,119,238,193,159,35,70,140,5,10,20,40,80,160,93,186,105,210,185,111,222,161,95,190,97,194,153,47,94,188,101,202,137,15,30,60,120,240,253,231,211,187,107,214,177,127,254,225,223,163,91,182,113,226,217,175,67,134,17,34,68,136,13,26,52,104,208,189,103,206,129,31,62,124,248,237,199,147,59,118,236,197,151,51,102,204,133,23,46,92,184,109,218,169,79,158,33,66,132,21,42,84,168,77,154,41,82,164,85,170,73,146,57,114,228,213,183,115,230,209,191,99,198,145,63,126,252,229,215,179,123,246,241,255,227,219,171,75,150,49,98,196,149,55,110,220,165,87,174,65,130,25,50,100,200,141,7,14,28,56,112,224,221,167,83,166,81,162,89,178,121,242,249,239,195,155,43,86,172,69,138,9,18,36,72,144,61,122,244,245,247,243,251,235,203,139,11,22,44,88,176,125,250,233,207,131,27,54,108,216,173,71,142,0],g=[255,0,1,25,2,50,26,198,3,223,51,238,27,104,199,75,4,100,224,14,52,141,239,129,28,193,105,248,200,8,76,113,5,138,101,47,225,36,15,33,53,147,142,218,240,18,130,69,29,181,194,125,106,39,249,185,201,154,9,120,77,228,114,166,6,191,139,98,102,221,48,253,226,152,37,179,16,145,34,136,54,208,148,206,143,150,219,189,241,210,19,92,131,56,70,64,30,66,182,163,195,72,126,110,107,58,40,84,250,133,186,61,202,94,155,159,10,21,121,43,78,212,229,172,115,243,167,87,7,112,192,247,140,128,99,13,103,74,222,237,49,197,254,24,227,165,153,119,38,184,180,124,17,68,146,217,35,32,137,46,55,63,209,91,149,188,207,205,144,135,151,178,220,252,190,97,242,86,211,171,20,42,93,158,132,60,57,83,71,109,65,162,31,45,67,216,183,123,164,118,196,23,73,236,127,12,111,246,108,161,59,82,41,157,85,170,251,96,134,177,187,204,62,90,203,89,95,176,156,169,160,81,11,245,22,235,122,117,44,215,79,174,213,233,230,231,173,232,116,214,244,234,168,80,88,175],h=3,i=3,j=40,k=10,l=[3220,1468,2713,1235,3062,1890,2119,1549,2344,2936,1117,2583,1330,2470,1667,2249,2028,3780,481,4011,142,3098,831,3445,592,2517,1776,2234,1951,2827,1070,2660,1345,3177];var m=[],n,o,p,q=[],r=1,s=[],t=[],u,v,w=false,x=[],y=a.qr,z,A,B=[],C,D;var R={VERSION:"1.0.3",canvas:function(a,b){b=G(a,b);return H(function c(){switch(typeof a){case"object":break;case"string":a={value:a};break;default:a={};break}var b,c,d,e,f,g,h=4,i=25;if(a.size>=1&&a.size<=10)h=a.size;h*=i;b=a.canvas||E();c=b.getContext("2d");c.canvas.width=h;c.canvas.height=h;c.fillStyle=a.background||"#fff";c.fillRect(0,0,h,h);if(a.level){switch(a.level.toUpperCase()){case"L":r=1;break;case"M":r=2;break;case"Q":r=3;break;case"H":r=4;break}}g=Q(a.value||"");c.lineWidth=1;f=h;f/=D;f=Math.round(f-.5);c.clearRect(0,0,h,h);c.fillStyle=a.background||"#fff";c.fillRect(0,0,f*(D+8),f*(D+8));c.fillStyle=a.foreground||"#000";for(d=0;d<D;d++){for(e=0;e<D;e++){if(g[e*D+d])c.fillRect(f*d,f*e,f,f)}}return b},b,this)},image:function(a,b){b=G(a,b);return H(function c(){switch(typeof a){case"object":break;case"string":a={value:a};break;default:a={};break}var b=this.canvas(a),c=a.image||F();c.src=b.toDataURL();c.height=b.height;c.width=b.width;return c},b,this)},save:function(b,d,e){e=G(b,d,e);return H(function f(){switch(typeof b){case"object":if(typeof d==="string"&&!b.path)b.path=d;break;case"string":b={value:b};if(typeof d==="string")b.path=d;break;default:b={};break}var e=this.canvas(b);if(w){if(typeof b.path!=="string"){throw new TypeError("Invalid path type: "+typeof b.path)}var f,g,h=function i(){u.write(f,g,0,g.length,0,function a(a){u.close(f);if(a)throw a})};e.toBuffer(function j(a,b){if(a)throw a;g=b;if(f)h()});u.open(b.path,"w",438,function k(a,b){if(a)throw a;f=b;if(g)h()})}else{a.location.href=e.toDataURL().replace("image/png",c)}},e,this)},toDataURL:function(a,b){b=G(a,b);return H(function c(){return this.canvas(a).toDataURL()},b,this)},noConflict:function(b){return H(function c(){a.qr=y;return this},b,this)}};if(typeof exports!=="undefined"){w=true;if(typeof module!=="undefined"&&module.exports){exports=module.exports=R}exports.qr=R;n=require("canvas");v=n.Image;u=require("fs")}else if(typeof define==="function"&&define.amd){define("qr",function S(){return R})}else{if(!a.HTMLCanvasElement){R.canvas=function(){};R.image=function(){};R.save=function(){};R.toDataURL=function(){}}a.qr=R}})(this)
+/*! qr-js v1.1.3 | (c) 2014 Alasdair Mercer | GPL v3 License
+jsqrencode | (c) 2010 tz@execpc.com | GPL v3 License
+*/
+!function(a){"use strict";function b(){return T?new r:a.document.createElement("canvas")}function c(){return T?new x:a.document.createElement("img")}function d(b,c,d){var e=c.mime||B;a.location.href=b.toDataURL(e).replace(e,C),"function"==typeof d&&d()}function e(a){return"string"==typeof a&&(a={value:a}),a||{}}function f(a){function b(b){a[b]=function(){throw new Error(b+" requires HTML5 canvas element support")}}var c,d=["canvas","image","save","saveSync","toDataURL"];for(c=0;c<d.length;c++)b(d[c])}function g(a,b,c){function d(){w.write(e,f,0,f.length,0,function(a){w.close(e),c(a)})}if("string"!=typeof b.path)return c(new TypeError("Invalid path type: "+typeof b.path));var e,f;a.toBuffer(function(a,b){return a?c(a):(f=b,void(e&&d()))}),w.open(b.path,"w",N,function(a,b){return a?c(a):(e=b,void(f&&d()))})}function h(a,b){if("string"!=typeof b.path)throw new TypeError("Invalid path type: "+typeof b.path);var c=a.toBuffer(),d=w.openSync(b.path,"w",N);try{w.writeSync(d,c,0,c.length,0)}finally{w.closeSync(d)}}function i(a,b){var c;a>b&&(c=a,a=b,b=c),c=b,c*=b,c+=b,c>>=1,c+=a,S[c]=1}function j(a,b){var c;for(R[a+z*b]=1,c=-2;2>c;c++)R[a+c+z*(b-2)]=1,R[a-2+z*(b+c+1)]=1,R[a+2+z*(b+c)]=1,R[a+c+1+z*(b+2)]=1;for(c=0;2>c;c++)i(a-1,b+c),i(a+1,b-c),i(a-c,b-1),i(a+c,b+1)}function k(a){for(;a>=255;)a-=255,a=(a>>8)+(255&a);return a}function l(a,b,c,d){var e,f,g;for(f=0;d>f;f++)W[c+f]=0;for(f=0;b>f;f++){if(e=H[W[a+f]^W[c]],255!==e)for(g=1;d>g;g++)W[c+g-1]=W[c+g]^G[k(e+U[d-g])];else for(g=c;c+d>g;g++)W[g]=W[g+1];W[c+d-1]=255===e?0:G[k(e+U[0])]}}function m(a,b){var c;return a>b&&(c=a,a=b,b=c),c=b,c+=b*b,c>>=1,c+=a,1===S[c]}function n(a){var b,c,d,e;switch(a){case 0:for(c=0;z>c;c++)for(b=0;z>b;b++)b+c&1||m(b,c)||(R[b+c*z]^=1);break;case 1:for(c=0;z>c;c++)for(b=0;z>b;b++)1&c||m(b,c)||(R[b+c*z]^=1);break;case 2:for(c=0;z>c;c++)for(d=0,b=0;z>b;b++,d++)3===d&&(d=0),d||m(b,c)||(R[b+c*z]^=1);break;case 3:for(e=0,c=0;z>c;c++,e++)for(3===e&&(e=0),d=e,b=0;z>b;b++,d++)3===d&&(d=0),d||m(b,c)||(R[b+c*z]^=1);break;case 4:for(c=0;z>c;c++)for(d=0,e=c>>1&1,b=0;z>b;b++,d++)3===d&&(d=0,e=!e),e||m(b,c)||(R[b+c*z]^=1);break;case 5:for(e=0,c=0;z>c;c++,e++)for(3===e&&(e=0),d=0,b=0;z>b;b++,d++)3===d&&(d=0),(b&c&1)+!(!d|!e)||m(b,c)||(R[b+c*z]^=1);break;case 6:for(e=0,c=0;z>c;c++,e++)for(3===e&&(e=0),d=0,b=0;z>b;b++,d++)3===d&&(d=0),(b&c&1)+(d&&d===e)&1||m(b,c)||(R[b+c*z]^=1);break;case 7:for(e=0,c=0;z>c;c++,e++)for(3===e&&(e=0),d=0,b=0;z>b;b++,d++)3===d&&(d=0),(d&&d===e)+(b+c&1)&1||m(b,c)||(R[b+c*z]^=1)}}function o(a){var b,c=0;for(b=0;a>=b;b++)O[b]>=5&&(c+=I+O[b]-5);for(b=3;a-1>b;b+=2)O[b-2]===O[b+2]&&O[b+2]===O[b-1]&&O[b-1]===O[b+1]&&3*O[b-1]===O[b]&&(0===O[b-3]||b+3>a||3*O[b-3]>=4*O[b]||3*O[b+3]>=4*O[b])&&(c+=K);return c}function p(){var a,b,c,d,e,f,g,h,i;for(c=e=f=0,i=0;z-1>i;i++)for(h=0;z-1>h;h++)(R[h+z*i]&&R[h+1+z*i]&&R[h+z*(i+1)]&&R[h+1+z*(i+1)]||!(R[h+z*i]||R[h+1+z*i]||R[h+z*(i+1)]||R[h+1+z*(i+1)]))&&(c+=J);for(i=0;z>i;i++){for(O[0]=0,g=a=h=0;z>h;h++)(b=R[h+z*i])===a?O[g]++:O[++g]=1,a=b,e+=a?1:-1;c+=o(g)}for(0>e&&(e=-e),d=e,d+=d<<2,d<<=1;d>z*z;)d-=z*z,f++;for(c+=f*L,h=0;z>h;h++){for(O[0]=0,g=a=i=0;z>i;i++)(b=R[h+z*i])===a?O[g]++:O[++g]=1,a=b;c+=o(g)}return c}function q(a){var b,c,d,e,f,g,h,o;f=a.length,y=0;do if(y++,d=4*(Q-1)+16*(y-1),u=D[d++],v=D[d++],s=D[d++],t=D[d],d=s*(u+v)+v-3+(9>=y),d>=f)break;while(40>y);for(z=17+4*y,g=s+(s+t)*(u+v)+v,f=0;g>f;f++)P[f]=0;for(W=a.slice(0),f=0;z*z>f;f++)R[f]=0;for(f=0;(z*(z+1)+1)/2>f;f++)S[f]=0;for(f=0;3>f;f++){for(d=o=0,1===f&&(d=z-7),2===f&&(o=z-7),R[o+3+z*(d+3)]=1,h=0;6>h;h++)R[o+h+z*d]=1,R[o+z*(d+h+1)]=1,R[o+6+z*(d+h)]=1,R[o+h+1+z*(d+6)]=1;for(h=1;5>h;h++)i(o+h,d+1),i(o+1,d+h+1),i(o+5,d+h),i(o+h+1,d+5);for(h=2;4>h;h++)R[o+h+z*(d+2)]=1,R[o+2+z*(d+h+1)]=1,R[o+4+z*(d+h)]=1,R[o+h+1+z*(d+4)]=1}if(y>1)for(f=A[y],o=z-7;;){for(h=z-7;h>f-3&&(j(h,o),!(f>h));)h-=f;if(f+9>=o)break;o-=f,j(6,o),j(o,6)}for(R[8+z*(z-8)]=1,o=0;7>o;o++)i(7,o),i(z-8,o),i(7,o+z-7);for(h=0;8>h;h++)i(h,7),i(h+z-8,7),i(h,z-8);for(h=0;9>h;h++)i(h,8);for(h=0;8>h;h++)i(h+z-8,8),i(8,h);for(o=0;7>o;o++)i(8,o+z-7);for(h=0;z-14>h;h++)1&h?(i(8+h,6),i(6,8+h)):(R[8+h+6*z]=1,R[6+z*(8+h)]=1);if(y>6)for(f=M[y-7],d=17,h=0;6>h;h++)for(o=0;3>o;o++,d--)1&(d>11?y>>d-12:f>>d)?(R[5-h+z*(2-o+z-11)]=1,R[2-o+z-11+z*(5-h)]=1):(i(5-h,2-o+z-11),i(2-o+z-11,5-h));for(o=0;z>o;o++)for(h=0;o>=h;h++)R[h+z*o]&&i(h,o);for(g=W.length,b=0;g>b;b++)P[b]=W.charCodeAt(b);if(W=P.slice(0),h=s*(u+v)+v,g>=h-2&&(g=h-2,y>9&&g--),b=g,y>9){for(W[b+2]=0,W[b+3]=0;b--;)f=W[b],W[b+3]|=255&f<<4,W[b+2]=f>>4;W[2]|=255&g<<4,W[1]=g>>4,W[0]=64|g>>12}else{for(W[b+1]=0,W[b+2]=0;b--;)f=W[b],W[b+2]|=255&f<<4,W[b+1]=f>>4;W[1]|=255&g<<4,W[0]=64|g>>4}for(b=g+3-(10>y);h>b;)W[b++]=236,W[b++]=17;for(U[0]=1,b=0;t>b;b++){for(U[b+1]=1,c=b;c>0;c--)U[c]=U[c]?U[c-1]^G[k(H[U[c]]+b)]:U[c-1];U[0]=G[k(H[U[0]]+b)]}for(b=0;t>=b;b++)U[b]=H[U[b]];for(d=h,o=0,b=0;u>b;b++)l(o,s,d,t),o+=s,d+=t;for(b=0;v>b;b++)l(o,s+1,d,t),o+=s+1,d+=t;for(o=0,b=0;s>b;b++){for(c=0;u>c;c++)P[o++]=W[b+c*s];for(c=0;v>c;c++)P[o++]=W[u*s+b+c*(s+1)]}for(c=0;v>c;c++)P[o++]=W[u*s+b+c*(s+1)];for(b=0;t>b;b++)for(c=0;u+v>c;c++)P[o++]=W[h+b+c*t];for(W=P,h=o=z-1,d=g=1,e=(s+t)*(u+v)+v,b=0;e>b;b++)for(f=W[b],c=0;8>c;c++,f<<=1){128&f&&(R[h+z*o]=1);do g?h--:(h++,d?0!==o?o--:(h-=2,d=!d,6===h&&(h--,o=9)):o!==z-1?o++:(h-=2,d=!d,6===h&&(h--,o-=8))),g=!g;while(m(h,o))}for(W=R.slice(0),f=0,o=3e4,d=0;8>d&&(n(d),h=p(),o>h&&(o=h,f=d),7!==f);d++)R=W.slice(0);for(f!==d&&n(f),o=F[f+(Q-1<<3)],d=0;8>d;d++,o>>=1)1&o&&(R[z-1-d+8*z]=1,6>d?R[8+z*d]=1:R[8+z*(d+1)]=1);for(d=0;7>d;d++,o>>=1)1&o&&(R[8+z*(z-7+d)]=1,d?R[6-d+8*z]=1:R[7+8*z]=1);return R}var r,s,t,u,v,w,x,y,z,A=[0,11,15,19,23,27,31,16,18,20,22,24,26,28,20,22,24,24,26,28,28,22,24,24,26,26,28,28,24,24,26,26,26,28,28,24,26,26,26,28,28],B="image/png",C="image/octet-stream",D=[1,0,19,7,1,0,16,10,1,0,13,13,1,0,9,17,1,0,34,10,1,0,28,16,1,0,22,22,1,0,16,28,1,0,55,15,1,0,44,26,2,0,17,18,2,0,13,22,1,0,80,20,2,0,32,18,2,0,24,26,4,0,9,16,1,0,108,26,2,0,43,24,2,2,15,18,2,2,11,22,2,0,68,18,4,0,27,16,4,0,19,24,4,0,15,28,2,0,78,20,4,0,31,18,2,4,14,18,4,1,13,26,2,0,97,24,2,2,38,22,4,2,18,22,4,2,14,26,2,0,116,30,3,2,36,22,4,4,16,20,4,4,12,24,2,2,68,18,4,1,43,26,6,2,19,24,6,2,15,28,4,0,81,20,1,4,50,30,4,4,22,28,3,8,12,24,2,2,92,24,6,2,36,22,4,6,20,26,7,4,14,28,4,0,107,26,8,1,37,22,8,4,20,24,12,4,11,22,3,1,115,30,4,5,40,24,11,5,16,20,11,5,12,24,5,1,87,22,5,5,41,24,5,7,24,30,11,7,12,24,5,1,98,24,7,3,45,28,15,2,19,24,3,13,15,30,1,5,107,28,10,1,46,28,1,15,22,28,2,17,14,28,5,1,120,30,9,4,43,26,17,1,22,28,2,19,14,28,3,4,113,28,3,11,44,26,17,4,21,26,9,16,13,26,3,5,107,28,3,13,41,26,15,5,24,30,15,10,15,28,4,4,116,28,17,0,42,26,17,6,22,28,19,6,16,30,2,7,111,28,17,0,46,28,7,16,24,30,34,0,13,24,4,5,121,30,4,14,47,28,11,14,24,30,16,14,15,30,6,4,117,30,6,14,45,28,11,16,24,30,30,2,16,30,8,4,106,26,8,13,47,28,7,22,24,30,22,13,15,30,10,2,114,28,19,4,46,28,28,6,22,28,33,4,16,30,8,4,122,30,22,3,45,28,8,26,23,30,12,28,15,30,3,10,117,30,3,23,45,28,4,31,24,30,11,31,15,30,7,7,116,30,21,7,45,28,1,37,23,30,19,26,15,30,5,10,115,30,19,10,47,28,15,25,24,30,23,25,15,30,13,3,115,30,2,29,46,28,42,1,24,30,23,28,15,30,17,0,115,30,10,23,46,28,10,35,24,30,19,35,15,30,17,1,115,30,14,21,46,28,29,19,24,30,11,46,15,30,13,6,115,30,14,23,46,28,44,7,24,30,59,1,16,30,12,7,121,30,12,26,47,28,39,14,24,30,22,41,15,30,6,14,121,30,6,34,47,28,46,10,24,30,2,64,15,30,17,4,122,30,29,14,46,28,49,10,24,30,24,46,15,30,4,18,122,30,13,32,46,28,48,14,24,30,42,32,15,30,20,4,117,30,40,7,47,28,43,22,24,30,10,67,15,30,19,6,118,30,18,31,47,28,34,34,24,30,20,61,15,30],E={L:1,M:2,Q:3,H:4},F=[30660,29427,32170,30877,26159,25368,27713,26998,21522,20773,24188,23371,17913,16590,20375,19104,13663,12392,16177,14854,9396,8579,11994,11245,5769,5054,7399,6608,1890,597,3340,2107],G=[1,2,4,8,16,32,64,128,29,58,116,232,205,135,19,38,76,152,45,90,180,117,234,201,143,3,6,12,24,48,96,192,157,39,78,156,37,74,148,53,106,212,181,119,238,193,159,35,70,140,5,10,20,40,80,160,93,186,105,210,185,111,222,161,95,190,97,194,153,47,94,188,101,202,137,15,30,60,120,240,253,231,211,187,107,214,177,127,254,225,223,163,91,182,113,226,217,175,67,134,17,34,68,136,13,26,52,104,208,189,103,206,129,31,62,124,248,237,199,147,59,118,236,197,151,51,102,204,133,23,46,92,184,109,218,169,79,158,33,66,132,21,42,84,168,77,154,41,82,164,85,170,73,146,57,114,228,213,183,115,230,209,191,99,198,145,63,126,252,229,215,179,123,246,241,255,227,219,171,75,150,49,98,196,149,55,110,220,165,87,174,65,130,25,50,100,200,141,7,14,28,56,112,224,221,167,83,166,81,162,89,178,121,242,249,239,195,155,43,86,172,69,138,9,18,36,72,144,61,122,244,245,247,243,251,235,203,139,11,22,44,88,176,125,250,233,207,131,27,54,108,216,173,71,142,0],H=[255,0,1,25,2,50,26,198,3,223,51,238,27,104,199,75,4,100,224,14,52,141,239,129,28,193,105,248,200,8,76,113,5,138,101,47,225,36,15,33,53,147,142,218,240,18,130,69,29,181,194,125,106,39,249,185,201,154,9,120,77,228,114,166,6,191,139,98,102,221,48,253,226,152,37,179,16,145,34,136,54,208,148,206,143,150,219,189,241,210,19,92,131,56,70,64,30,66,182,163,195,72,126,110,107,58,40,84,250,133,186,61,202,94,155,159,10,21,121,43,78,212,229,172,115,243,167,87,7,112,192,247,140,128,99,13,103,74,222,237,49,197,254,24,227,165,153,119,38,184,180,124,17,68,146,217,35,32,137,46,55,63,209,91,149,188,207,205,144,135,151,178,220,252,190,97,242,86,211,171,20,42,93,158,132,60,57,83,71,109,65,162,31,45,67,216,183,123,164,118,196,23,73,236,127,12,111,246,108,161,59,82,41,157,85,170,251,96,134,177,187,204,62,90,203,89,95,176,156,169,160,81,11,245,22,235,122,117,44,215,79,174,213,233,230,231,173,232,116,214,244,234,168,80,88,175],I=3,J=3,K=40,L=10,M=[3220,1468,2713,1235,3062,1890,2119,1549,2344,2936,1117,2583,1330,2470,1667,2249,2028,3780,481,4011,142,3098,831,3445,592,2517,1776,2234,1951,2827,1070,2660,1345,3177],N=parseInt("0666",8),O=[],P=[],Q=1,R=[],S=[],T=!1,U=[],V=a.qr,W=[],X={VERSION:"1.1.3",canvas:function(a){a=e(a);var c=a.size>=1&&a.size<=10?a.size:4;c*=25;var d=a.canvas||b(),f=d.getContext("2d");f.canvas.width=c,f.canvas.height=c,f.fillStyle=a.background||"#fff",f.fillRect(0,0,c,c),Q=E[a.level&&a.level.toUpperCase()||"L"];var g=q(a.value||"");f.lineWidth=1;var h=c;h/=z,h=Math.floor(h),f.clearRect(0,0,c,c),f.fillStyle=a.background||"#fff",f.fillRect(0,0,h*(z+8),h*(z+8)),f.fillStyle=a.foreground||"#000";var i,j;for(i=0;z>i;i++)for(j=0;z>j;j++)g[j*z+i]&&f.fillRect(h*i,h*j,h,h);return d},image:function(a){a=e(a);var b=this.canvas(a),d=a.image||c();return d.src=b.toDataURL(a.mime||B),d.height=b.height,d.width=b.width,d},save:function(a,b,c){function f(a){h||(h=!0,c(a))}switch(a=e(a),typeof b){case"function":c=b,b=null;break;case"string":a.path=b}if("function"!=typeof c)throw new TypeError("Invalid callback type: "+typeof c);var h=!1,i=this.canvas(a);T?g(i,a,f):d(i,a,f)},saveSync:function(a,b){a=e(a),"string"==typeof b&&(a.path=b);var c=this.canvas(a);T?h(c,a):d(c,a)},toDataURL:function(a){return a=e(a),this.canvas(a).toDataURL(a.mime||B)},noConflict:function(){return a.qr=V,this}};"undefined"!=typeof exports?(T=!0,"undefined"!=typeof module&&module.exports&&(exports=module.exports=X),exports.qr=X,r=require("canvas"),x=r.Image,w=require("fs")):"function"==typeof define&&define.amd?define(function(){return X}):(a.HTMLCanvasElement||f(X),a.qr=X)}(this);
+//# sourceMappingURL=qr.min.map
\ No newline at end of file
index 52a48208fb0ac3a0e5e7aa76d685bd90151e56e8..28394ed53212c69b30bfabf8464728c01d3a4589 100644 (file)
@@ -1,4 +1,4 @@
-/* CSS Stylsheet for Shaarli - http://sebsauvage.net/wiki/doku.php?id=php:shaarli */
+/* Cascading Stylesheet for Shaarli - http://sebsauvage.net/wiki/doku.php?id=php:shaarli */
 
 /* CSS Reset from Yahoo to cope with browsers CSS inconsistencies. */
 /*
@@ -7,7 +7,7 @@ version: 2.8.2r1
 */
 html{color:#000;background:#FFF;}body,div,dl,dt,dd,ul,ol,li,h1,h2,h3,h4,h5,h6,pre,code,form,fieldset,legend,input,button,textarea,p,blockquote,th,td{margin:0;padding:0;}table{border-collapse:collapse;border-spacing:0;}fieldset,img{border:0;}address,caption,cite,code,dfn,em,strong,th,var,optgroup{font-style:inherit;font-weight:inherit;}del,ins{text-decoration:none;}li{list-style:none;}caption,th{text-align:left;}h1,h2,h3,h4,h5,h6{font-size:100%;font-weight:normal;}q:before,q:after{content:'';}abbr,acronym{border:0;font-variant:normal;}sup{vertical-align:baseline;}sub{vertical-align:baseline;}legend{color:#000;}input,button,textarea,select,optgroup,option{font-family:inherit;font-size:inherit;font-style:inherit;font-weight:inherit;}input,button,textarea,select{*font-size:100%;}
 
-body { font-family: "Trebuchet MS",Verdana,Arial,Helvetica,sans-serif; font-size:10pt; background-color: #ffffff; }
+body { font-family: "Trebuchet MS",Verdana,Arial,Helvetica,sans-serif; font-size:10pt; background-color: #ffffff; word-wrap: break-word; }
 input, textarea {
        background-color: #dedede;
        background: -webkit-gradient(linear, 0 0, 0 bottom, from(#dedede), to(#ffffff));
@@ -232,14 +232,15 @@ cursor:pointer;
 .linktitle { font-size:14pt; font-weight:bold; }
 .linktitle a { text-decoration: none; color:#80AD48; }
 .linktitle a:hover { color:#F57900; }
-.linkdate { font-size:8pt; color:#888; }
-.linkdate a { background-image:url('../images/calendar.png');padding:2px 0 3px 20px;background-repeat:no-repeat;text-decoration: none; color:#E28E3F;  }
-.linkdate a:hover { color: #F57900 }
+.linkdate, .linkarchive { font-size:8pt; color:#888; }
+.linkdate a, .linkarchive a { color:#E28E3F;  }
+.linkdate a {background-image:url('../images/calendar.png');padding:2px 0 3px 20px;background-repeat:no-repeat;text-decoration: none; }
+.linkdate a:hover, .linkarchive a:hover { color: #F57900 }
 .linkurl { font-size:8pt; color:#4BAA74; }
 .linkdescription { color:#000; margin-top:0; margin-bottom:12px; font-weight:normal; max-height:400px; overflow:auto; }
 .linkdescription a { text-decoration: none; color:#3465A4; }
 .linkdescription a:hover { color:#F57900; }
-.linktaglist { padding-top:10px;}
+.linktaglist { padding-top:10px; line-height:200%;}
 .linktag {
 
 font-size:9pt;
@@ -408,13 +409,13 @@ div.dailyEntryDescription
     overflow:auto;
 }
 
-/* Common css screwdriver */
+/* Common CSS screwdriver */
 .clear{
        clear:both;
 }
 
 /* For lazy images loading in picture wall.
-   using http://www.appelsiini.net/projects/lazyload 
+   Using http://www.appelsiini.net/projects/lazyload 
  */
 .lazyimage { display:none; }
 
@@ -451,7 +452,7 @@ a {color:#000!important;text-decoration:none!important;}
 #searchform_value { width:70% !important; }
 #tagfilter_value { width:70% !important; }
 div.qrcode { position:relative; float:left; top:-10px; left:0px; }
-#paging_privatelinks { float;none; }
+#paging_privatelinks { float:none; }
 #paging_linksperpage { float:none; margin-bottom:10px; font-size:smaller;}
 #paging_older,#paging_newer,#paging_linksperpage a { border: 1px solid black; padding:3px 5px 3px 5px; background-color:#666; color:#fff; border-radius: 5px 5px 5px 5px;}
 .thumbnail { float:none; height:auto; margin: 0px; text-align:center;}
index 0465a4e5bbca798b5dc1b0ca0cd26a0801450b14..1d523a33c3622b21b9c0fd481c0d86baff1ff9ae 100644 (file)
--- a/index.php
+++ b/index.php
@@ -1,9 +1,9 @@
 <?php
-// Shaarli 0.0.41 beta - Shaare your links...
-// The personal, minimalist, super-fast, no-database delicious clone. By sebsauvage.net
+// Shaarli 0.0.42 beta - Shaare your links...
+// The personal, minimalist, super-fast, no-database Delicious clone. By sebsauvage.net
 // http://sebsauvage.net/wiki/doku.php?id=php:shaarli
 // Licence: http://www.opensource.org/licenses/zlib-license.php
-// Requires: php 5.1.x  (but autocomplete fields will only work if you have php 5.2.x)
+// Requires: PHP 5.1.x  (but autocomplete fields will only work if you have PHP 5.2.x)
 // -----------------------------------------------------------------------------------------------
 // NEVER TRUST IN PHP.INI
 // Some hosts do not define a default timezone in php.ini,
@@ -21,22 +21,28 @@ $GLOBALS['config']['BAN_AFTER'] = 4;        // Ban IP after this many failures.
 $GLOBALS['config']['BAN_DURATION'] = 1800;  // Ban duration for IP address after login failures (in seconds) (1800 sec. = 30 minutes)
 $GLOBALS['config']['OPEN_SHAARLI'] = false; // If true, anyone can add/edit/delete links without having to login
 $GLOBALS['config']['HIDE_TIMESTAMPS'] = false; // If true, the moment when links were saved are not shown to users that are not logged in.
+$GLOBALS['config']['SHOW_ATOM'] = false; // If true, an extra "ATOM feed" button will be displayed in the toolbar
 $GLOBALS['config']['ENABLE_THUMBNAILS'] = true; // Enable thumbnails in links.
 $GLOBALS['config']['CACHEDIR'] = 'cache'; // Cache directory for thumbnails for SLOW services (like flickr)
 $GLOBALS['config']['PAGECACHE'] = 'pagecache'; // Page cache directory.
-$GLOBALS['config']['ENABLE_LOCALCACHE'] = true; // Enable Shaarli to store thumbnail in a local cache. Disable to reduce webspace usage.
+$GLOBALS['config']['ENABLE_LOCALCACHE'] = true; // Enable Shaarli to store thumbnail in a local cache. Disable to reduce web space usage.
 $GLOBALS['config']['PUBSUBHUB_URL'] = ''; // PubSubHubbub support. Put an empty string to disable, or put your hub url here to enable.
+$GLOBALS['config']['RAINTPL_TMP'] = 'tmp/' ; // Raintpl cache directory  (keep the trailing slash!)
+$GLOBALS['config']['RAINTPL_TPL'] = 'tpl/' ; // Raintpl template directory (keep the trailing slash!)
 $GLOBALS['config']['UPDATECHECK_FILENAME'] = $GLOBALS['config']['DATADIR'].'/lastupdatecheck.txt'; // For updates check of Shaarli.
 $GLOBALS['config']['UPDATECHECK_INTERVAL'] = 86400 ; // Updates check frequency for Shaarli. 86400 seconds=24 hours
                                           // Note: You must have publisher.php in the same directory as Shaarli index.php
+$GLOBALS['config']['ARCHIVE_ORG'] = false; // For each link, add a link to an archived version on archive.org
 // -----------------------------------------------------------------------------------------------
-// You should not touch below (or at your own risks !)
-// Optionnal config file.
+// You should not touch below (or at your own risks!)
+// Optional config file.
 if (is_file($GLOBALS['config']['DATADIR'].'/options.php')) require($GLOBALS['config']['DATADIR'].'/options.php');
 
-define('shaarli_version','0.0.41 beta');
-define('PHPPREFIX','<?php /* '); // Prefix to encapsulate data in php code.
-define('PHPSUFFIX',' */ ?>'); // Suffix to encapsulate data in php code.
+define('shaarli_version','0.0.42 beta');
+define('PHPPREFIX','<?php /* '); // Prefix to encapsulate data in PHP code.
+define('PHPSUFFIX',' */ ?>'); // Suffix to encapsulate data in PHP code.
+// http://server.com/x/shaarli --> /shaarli/
+define('WEB_PATH', substr($_SERVER["REQUEST_URI"], 0, 1+strrpos($_SERVER["REQUEST_URI"], '/', 0)));
 
 // Force cookie path (but do not change lifetime)
 $cookie=session_get_cookie_params();
@@ -46,8 +52,8 @@ session_set_cookie_params($cookie['lifetime'],$cookiedir,$_SERVER['HTTP_HOST']);
 // Set session parameters on server side.
 define('INACTIVITY_TIMEOUT',3600); // (in seconds). If the user does not access any page within this time, his/her session is considered expired.
 ini_set('session.use_cookies', 1);       // Use cookies to store session.
-ini_set('session.use_only_cookies', 1);  // Force cookies for session (phpsessionID forbidden in URL)
-ini_set('session.use_trans_sid', false); // Prevent php to use sessionID in URL if cookies are disabled.
+ini_set('session.use_only_cookies', 1);  // Force cookies for session (phpsessionID forbidden in URL).
+ini_set('session.use_trans_sid', false); // Prevent PHP form using sessionID in URL if cookies are disabled.
 session_name('shaarli');
 if (session_id() == '') session_start();  // Start session if needed (Some server auto-start sessions).
 
@@ -61,9 +67,8 @@ error_reporting(E_ALL^E_WARNING);  // See all error except warnings.
 //error_reporting(-1); // See all errors (for debugging only)
 
 include "inc/rain.tpl.class.php"; //include Rain TPL
-raintpl::$tpl_dir = "tpl/"; // template directory
-if (!is_dir('tmp')) { mkdir('tmp',0705); chmod('tmp',0705); }
-raintpl::$cache_dir = "tmp/"; // cache directory
+raintpl::$tpl_dir = $GLOBALS['config']['RAINTPL_TPL']; // template directory
+raintpl::$cache_dir = $GLOBALS['config']['RAINTPL_TMP']; // cache directory
 
 ob_start();  // Output buffering for the page cache.
 
@@ -83,18 +88,8 @@ header("Cache-Control: no-store, no-cache, must-revalidate");
 header("Cache-Control: post-check=0, pre-check=0", false);
 header("Pragma: no-cache");
 
-// Directories creations (Note that your web host may require differents rights than 705.)
+// Directories creations (Note that your web host may require different rights than 705.)
 if (!is_writable(realpath(dirname(__FILE__)))) die('<pre>ERROR: Shaarli does not have the right to write in its own directory ('.realpath(dirname(__FILE__)).').</pre>');
-if (!is_dir($GLOBALS['config']['DATADIR'])) { mkdir($GLOBALS['config']['DATADIR'],0705); chmod($GLOBALS['config']['DATADIR'],0705); }
-if (!is_dir('tmp')) { mkdir('tmp',0705); chmod('tmp',0705); } // For RainTPL temporary files.
-if (!is_file($GLOBALS['config']['DATADIR'].'/.htaccess')) { file_put_contents($GLOBALS['config']['DATADIR'].'/.htaccess',"Allow from none\nDeny from all\n"); } // Protect data files.
-// Second check to see if Shaarli can write in its directory, because on some hosts is_writable() is not reliable.
-if (!is_file($GLOBALS['config']['DATADIR'].'/.htaccess')) die('<pre>ERROR: Shaarli does not have the right to write in its data directory ('.realpath($GLOBALS['config']['DATADIR']).').</pre>');
-if ($GLOBALS['config']['ENABLE_LOCALCACHE'])
-{
-    if (!is_dir($GLOBALS['config']['CACHEDIR'])) { mkdir($GLOBALS['config']['CACHEDIR'],0705); chmod($GLOBALS['config']['CACHEDIR'],0705); }
-    if (!is_file($GLOBALS['config']['CACHEDIR'].'/.htaccess')) { file_put_contents($GLOBALS['config']['CACHEDIR'].'/.htaccess',"Allow from none\nDeny from all\n"); } // Protect data files.
-}
 
 // Handling of old config file which do not have the new parameters.
 if (empty($GLOBALS['title'])) $GLOBALS['title']='Shared links on '.htmlspecialchars(indexUrl());
@@ -103,6 +98,7 @@ if (empty($GLOBALS['redirector'])) $GLOBALS['redirector']='';
 if (empty($GLOBALS['disablesessionprotection'])) $GLOBALS['disablesessionprotection']=false;
 if (empty($GLOBALS['disablejquery'])) $GLOBALS['disablejquery']=false;
 if (empty($GLOBALS['privateLinkByDefault'])) $GLOBALS['privateLinkByDefault']=false;
+if (empty($GLOBALS['titleLink'])) $GLOBALS['titleLink']='?';
 // I really need to rewrite Shaarli with a proper configuation manager.
 
 // Run config screen if first run:
@@ -110,17 +106,19 @@ if (!is_file($GLOBALS['config']['CONFIG_FILE'])) install();
 
 require $GLOBALS['config']['CONFIG_FILE'];  // Read login/password hash into $GLOBALS.
 
+// a token depending of deployment salt, user password, and the current ip
+define('STAY_SIGNED_IN_TOKEN', sha1($GLOBALS['hash'].$_SERVER["REMOTE_ADDR"].$GLOBALS['salt']));
 
 autoLocale(); // Sniff browser language and set date format accordingly.
 header('Content-Type: text/html; charset=utf-8'); // We use UTF-8 for proper international characters handling.
 
-// Check php version
+// Check PHP version
 function checkphpversion()
 {
     if (version_compare(PHP_VERSION, '5.1.0') < 0)
     {
         header('Content-Type: text/plain; charset=utf-8');
-        echo 'Your server supports php '.PHP_VERSION.'. Shaarli requires at least php 5.1.0, and thus cannot run. Sorry.';
+        echo 'Your server supports PHP '.PHP_VERSION.'. Shaarli requires at least php 5.1.0, and thus cannot run. Sorry.';
         exit;
     }
 }
@@ -137,9 +135,9 @@ function checkUpdate()
     if (!is_file($GLOBALS['config']['UPDATECHECK_FILENAME']) || (filemtime($GLOBALS['config']['UPDATECHECK_FILENAME'])<time()-($GLOBALS['config']['UPDATECHECK_INTERVAL'])))
     {
         $version=shaarli_version;
-        list($httpstatus,$headers,$data) = getHTTP('http://sebsauvage.net/files/shaarli_version.txt',2);
+        list($httpstatus,$headers,$data) = getHTTP('https://raw.githubusercontent.com/shaarli/Shaarli/master/shaarli_version.txt',2);
         if (strpos($httpstatus,'200 OK')!==false) $version=$data;
-        // If failed, nevermind. We don't want to bother the user with that.
+        // If failed, never mind. We don't want to bother the user with that.
         file_put_contents($GLOBALS['config']['UPDATECHECK_FILENAME'],$version); // touch file date
     }
     // Compare versions:
@@ -155,11 +153,11 @@ function checkUpdate()
 class pageCache
 {
     private $url; // Full URL of the page to cache (typically the value returned by pageUrl())
-    private $shouldBeCached; // boolean: Should this url be cached ?
-    private $filename; // Name of the cache file for this url
+    private $shouldBeCached; // boolean: Should this url be cached?
+    private $filename; // Name of the cache file for this url.
 
     /*
-         $url = url (typically the value returned by pageUrl())
+         $url = URL (typically the value returned by pageUrl())
          $shouldBeCached = boolean. If false, the cache will be disabled.
     */
     public function __construct($url,$shouldBeCached)
@@ -182,7 +180,6 @@ class pageCache
     public function cache($page)
     {
         if (!$this->shouldBeCached) return;
-        if (!is_dir($GLOBALS['config']['PAGECACHE'])) { mkdir($GLOBALS['config']['PAGECACHE'],0705); chmod($GLOBALS['config']['PAGECACHE'],0705); }
         file_put_contents($this->filename,$page);
     }
 
@@ -221,8 +218,8 @@ function nl2br_escaped($html)
     return str_replace('>','&gt;',str_replace('<','&lt;',nl2br($html)));
 }
 
-/* Returns the small hash of a string
-   eg. smallHash('20111006_131924') --> yZH23w
+/* Returns the small hash of a string, using RFC 4648 base64url format
+   e.g. smallHash('20111006_131924') --> yZH23w
    Small hashes:
      - are unique (well, as unique as crc32, at last)
      - are always 6 characters long.
@@ -233,13 +230,10 @@ function nl2br_escaped($html)
 function smallHash($text)
 {
     $t = rtrim(base64_encode(hash('crc32',$text,true)),'=');
-    $t = str_replace('+','-',$t); // Get rid of characters which need encoding in URLs.
-    $t = str_replace('/','_',$t);
-    $t = str_replace('=','@',$t);
-    return $t;
+    return strtr($t, '+/', '-_');
 }
 
-// In a string, converts urls to clickable links.
+// In a string, converts URLs to clickable links.
 // Function inspired from http://www.php.net/manual/en/function.preg-replace.php#85722
 function text2clickable($url)
 {
@@ -260,8 +254,8 @@ function keepMultipleSpaces($text)
 function autoLocale()
 {
     $loc='en_US'; // Default if browser does not send HTTP_ACCEPT_LANGUAGE
-    if (isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])) // eg. "fr,fr-fr;q=0.8,en;q=0.5,en-us;q=0.3"
-    {   // (It's a bit crude, but it works very well. Prefered language is always presented first.)
+    if (isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])) // e.g. "fr,fr-fr;q=0.8,en;q=0.5,en-us;q=0.3"
+    {   // (It's a bit crude, but it works very well. Preferred language is always presented first.)
         if (preg_match('/([a-z]{2}(-[a-z]{2})?)/i',$_SERVER['HTTP_ACCEPT_LANGUAGE'],$matches)) $loc=$matches[1];
     }
     setlocale(LC_TIME,$loc);  // LC_TIME = Set local for date/time format only.
@@ -297,16 +291,20 @@ function allIPs()
     return $ip;
 }
 
+function fillSessionInfo() {
+       $_SESSION['uid'] = sha1(uniqid('',true).'_'.mt_rand()); // Generate unique random number (different than phpsessionid)
+       $_SESSION['ip']=allIPs();                // We store IP address(es) of the client to make sure session is not hijacked.
+       $_SESSION['username']=$GLOBALS['login'];
+       $_SESSION['expires_on']=time()+INACTIVITY_TIMEOUT;  // Set session expiration.
+}
+
 // Check that user/password is correct.
 function check_auth($login,$password)
 {
     $hash = sha1($password.$login.$GLOBALS['salt']);
     if ($login==$GLOBALS['login'] && $hash==$GLOBALS['hash'])
     {   // Login/password is correct.
-        $_SESSION['uid'] = sha1(uniqid('',true).'_'.mt_rand()); // generate unique random number (different than phpsessionid)
-        $_SESSION['ip']=allIPs();                // We store IP address(es) of the client to make sure session is not hijacked.
-        $_SESSION['username']=$login;
-        $_SESSION['expires_on']=time()+INACTIVITY_TIMEOUT;  // Set session expiration.
+               fillSessionInfo();
         logm('Login successful');
         return True;
     }
@@ -321,6 +319,11 @@ function isLoggedIn()
 
     if (!isset($GLOBALS['login'])) return false;  // Shaarli is not configured yet.
 
+       if (@$_COOKIE['shaarli_staySignedIn']===STAY_SIGNED_IN_TOKEN)
+       {
+               fillSessionInfo();
+               return true;
+       }
     // If session does not exist on server side, or IP address has changed, or session has expired, logout.
     if (empty($_SESSION['uid']) || ($GLOBALS['disablesessionprotection']==false && $_SESSION['ip']!=allIPs()) || time()>=$_SESSION['expires_on'])
     {
@@ -334,7 +337,9 @@ function isLoggedIn()
 }
 
 // Force logout.
-function logout() { if (isset($_SESSION)) { unset($_SESSION['uid']); unset($_SESSION['ip']); unset($_SESSION['username']); unset($_SESSION['privateonly']); }  }
+function logout() { if (isset($_SESSION)) { unset($_SESSION['uid']); unset($_SESSION['ip']); unset($_SESSION['username']); unset($_SESSION['privateonly']); }  
+setcookie('shaarli_staySignedIn', FALSE, 0, WEB_PATH);
+}
 
 
 // ------------------------------------------------------------------------------------------
@@ -391,17 +396,18 @@ if (isset($_POST['login']))
 {
     if (!ban_canLogin()) die('I said: NO. You are banned for the moment. Go away.');
     if (isset($_POST['password']) && tokenOk($_POST['token']) && (check_auth($_POST['login'], $_POST['password'])))
-    {   // Login/password is ok.
+    {   // Login/password is OK.
         ban_loginOk();
         // If user wants to keep the session cookie even after the browser closes:
         if (!empty($_POST['longlastingsession']))
         {
+                       setcookie('shaarli_staySignedIn', STAY_SIGNED_IN_TOKEN, time()+31536000, WEB_PATH);
             $_SESSION['longlastingsession']=31536000;  // (31536000 seconds = 1 year)
             $_SESSION['expires_on']=time()+$_SESSION['longlastingsession'];  // Set session expiration on server-side.
 
             $cookiedir = ''; if(dirname($_SERVER['SCRIPT_NAME'])!='/') $cookiedir=dirname($_SERVER["SCRIPT_NAME"]).'/';
             session_set_cookie_params($_SESSION['longlastingsession'],$cookiedir,$_SERVER['HTTP_HOST']); // Set session cookie expiration on client side
-            // Note: Never forget the trailing slash on the cookie path !
+            // Note: Never forget the trailing slash on the cookie path!
             session_regenerate_id(true);  // Send cookie with new expiration date to browser.
         }
         else // Standard session expiration (=when browser closes)
@@ -411,7 +417,7 @@ if (isset($_POST['login']))
             session_regenerate_id(true);
         }
         // Optional redirect after login:
-        if (isset($_GET['post'])) { header('Location: ?post='.urlencode($_GET['post']).(!empty($_GET['title'])?'&title='.urlencode($_GET['title']):'').(!empty($_GET['source'])?'&source='.urlencode($_GET['source']):'')); exit; }
+        if (isset($_GET['post'])) { header('Location: ?post='.urlencode($_GET['post']).(!empty($_GET['title'])?'&title='.urlencode($_GET['title']):'').(!empty($_GET['description'])?'&description='.urlencode($_GET['description']):'').(!empty($_GET['source'])?'&source='.urlencode($_GET['source']):'')); exit; }
         if (isset($_POST['returnurl']))
         {
             if (endsWith($_POST['returnurl'],'?do=login')) { header('Location: ?'); exit; } // Prevent loops over login screen.
@@ -423,7 +429,7 @@ if (isset($_POST['login']))
     {
         ban_loginFailed();
         $redir = '';
-        if (isset($_GET['post'])) { $redir = '&post='.urlencode($_GET['post']).(!empty($_GET['title'])?'&title='.urlencode($_GET['title']):'').(!empty($_GET['source'])?'&source='.urlencode($_GET['source']):'');  }
+        if (isset($_GET['post'])) { $redir = '&post='.urlencode($_GET['post']).(!empty($_GET['title'])?'&title='.urlencode($_GET['title']):'').(!empty($_GET['description'])?'&description='.urlencode($_GET['description']):'').(!empty($_GET['source'])?'&source='.urlencode($_GET['source']):''); }
         echo '<script language="JavaScript">alert("Wrong login/password.");document.location=\'?do=login'.$redir.'\';</script>'; // Redirect to login screen.
         exit;
     }
@@ -433,7 +439,7 @@ if (isset($_POST['login']))
 // Misc utility functions:
 
 // Returns the server URL (including port and http/https), without path.
-// eg. "http://myserver.com:8080"
+// e.g. "http://myserver.com:8080"
 // You can append $_SERVER['SCRIPT_NAME'] to get the current script URL.
 function serverUrl()
 {
@@ -443,24 +449,24 @@ function serverUrl()
 }
 
 // Returns the absolute URL of current script, without the query.
-// (eg. http://sebsauvage.net/links/)
+// (e.g. http://sebsauvage.net/links/)
 function indexUrl()
 {
     $scriptname = $_SERVER["SCRIPT_NAME"];
     // If the script is named 'index.php', we remove it (for better looking URLs,
-    // eg. http://mysite.com/shaarli/?abcde instead of http://mysite.com/shaarli/index.php?abcde)
+    // e.g. http://mysite.com/shaarli/?abcde instead of http://mysite.com/shaarli/index.php?abcde)
     if (endswith($scriptname,'index.php')) $scriptname = substr($scriptname,0,strlen($scriptname)-9);
     return serverUrl() . $scriptname;
 }
 
 // Returns the absolute URL of current script, WITH the query.
-// (eg. http://sebsauvage.net/links/?toto=titi&spamspamspam=humbug)
+// (e.g. http://sebsauvage.net/links/?toto=titi&spamspamspam=humbug)
 function pageUrl()
 {
     return indexUrl().(!empty($_SERVER["QUERY_STRING"]) ? '?'.$_SERVER["QUERY_STRING"] : '');
 }
 
-// Convert post_max_size/upload_max_filesize (eg.'16M') parameters to bytes.
+// Convert post_max_size/upload_max_filesize (e.g. '16M') parameters to bytes.
 function return_bytes($val)
 {
     $val = trim($val); $last=strtolower($val[strlen($val)-1]);
@@ -481,7 +487,7 @@ function getMaxFileSize()
     $size2 = return_bytes(ini_get('upload_max_filesize'));
     // Return the smaller of two:
     $maxsize = min($size1,$size2);
-    // FIXME: Then convert back to readable notations ? (eg. 2M instead of 2000000)
+    // FIXME: Then convert back to readable notations ? (e.g. 2M instead of 2000000)
     return $maxsize;
 }
 
@@ -529,7 +535,7 @@ function linkdate2iso8601($linkdate)
 function linkdate2locale($linkdate)
 {
     return utf8_encode(strftime('%c',linkdate2timestamp($linkdate))); // %c is for automatic date format according to locale.
-    // Note that if you use a local which is not installed on your webserver,
+    // Note that if you use a locale which is not installed on your webserver,
     // the date will not be displayed in the chosen locale, but probably in US notation.
 }
 
@@ -551,10 +557,10 @@ function http_parse_headers_shaarli( $headers )
 }
 
 /* GET an URL.
-   Input: $url : url to get (http://...)
+   Input: $url : URL to get (http://...)
           $timeout : Network timeout (will wait this many seconds for an anwser before giving up).
-   Output: An array.  [0] = HTTP status message (eg. "HTTP/1.1 200 OK") or error message
-                      [1] = associative array containing HTTP response headers (eg. echo getHTTP($url)[1]['Content-Type'])
+   Output: An array.  [0] = HTTP status message (e.g. "HTTP/1.1 200 OK") or error message
+                      [1] = associative array containing HTTP response headers (e.g. echo getHTTP($url)[1]['Content-Type'])
                       [2] = data
     Example: list($httpstatus,$headers,$data) = getHTTP('http://sebauvage.net/');
              if (strpos($httpstatus,'200 OK')!==false)
@@ -570,11 +576,11 @@ function getHTTP($url,$timeout=30)
         $context = stream_context_create($options);
         $data=file_get_contents($url,false,$context,-1, 4000000); // We download at most 4 Mb from source.
         if (!$data) { return array('HTTP Error',array(),''); }
-        $httpStatus=$http_response_header[0]; // eg. "HTTP/1.1 200 OK"
+        $httpStatus=$http_response_header[0]; // e.g. "HTTP/1.1 200 OK"
         $responseHeaders=http_parse_headers_shaarli($http_response_header);
         return array($httpStatus,$responseHeaders,$data);
     }
-    catch (Exception $e)  // getHTTP *can* fail silentely (we don't care if the title cannot be fetched)
+    catch (Exception $e)  // getHTTP *can* fail silently (we don't care if the title cannot be fetched)
     {
         return array($e->getMessage(),'','');
     }
@@ -600,14 +606,14 @@ function getToken()
     return $rnd;
 }
 
-// Tells if a token is ok. Using this function will destroy the token.
-// true=token is ok.
+// Tells if a token is OK. Using this function will destroy the token.
+// true=token is OK.
 function tokenOk($token)
 {
     if (isset($_SESSION['tokens'][$token]))
     {
         unset($_SESSION['tokens'][$token]); // Token is used: destroy it.
-        return true; // Token is ok.
+        return true; // Token is OK.
     }
     return false; // Wrong token, or already used.
 }
@@ -642,8 +648,9 @@ class pageBuilder
         $this->tpl->assign('version',shaarli_version);
         $this->tpl->assign('scripturl',indexUrl());
         $this->tpl->assign('pagetitle','Shaarli');
-        $this->tpl->assign('privateonly',!empty($_SESSION['privateonly'])); // Show only private links ?
+        $this->tpl->assign('privateonly',!empty($_SESSION['privateonly'])); // Show only private links?
         if (!empty($GLOBALS['title'])) $this->tpl->assign('pagetitle',$GLOBALS['title']);
+        if (!empty($GLOBALS['titleLink'])) $this->tpl->assign('titleLink',$GLOBALS['titleLink']);
         if (!empty($GLOBALS['pagetitle'])) $this->tpl->assign('pagetitle',$GLOBALS['pagetitle']);
         $this->tpl->assign('shaarlititle',empty($GLOBALS['title']) ? 'Shaarli': $GLOBALS['title'] );
         return;
@@ -657,7 +664,7 @@ class pageBuilder
     }
 
     // Render a specific page (using a template).
-    // eg. pb.renderPage('picwall')
+    // e.g. pb.renderPage('picwall')
     public function renderPage($page)
     {
         if ($this->tpl===false) $this->initialize(); // Lazy initialization
@@ -676,10 +683,10 @@ class pageBuilder
 
    Available keys:
        title : Title of the link
-       url : URL of the link. Can be absolute or relative. Relative URLs are permalinks (eg.'?m-ukcw')
+       url : URL of the link. Can be absolute or relative. Relative URLs are permalinks (e.g.'?m-ukcw')
        description : description of the entry
-       private : Is this link private ? 0=no, other value=yes
-       linkdate : date of the creation of this entry, in the form YYYYMMDD_HHMMSS (eg.'20110914_192317')
+       private : Is this link private? 0=no, other value=yes
+       linkdate : date of the creation of this entry, in the form YYYYMMDD_HHMMSS (e.g.'20110914_192317')
        tags : tags attached to this entry (separated by spaces)
 
    We implement 3 interfaces:
@@ -689,15 +696,15 @@ class pageBuilder
 */
 class linkdb implements Iterator, Countable, ArrayAccess
 {
-    private $links; // List of links (associative array. Key=linkdate (eg. "20110823_124546"), value= associative array (keys:title,description...)
+    private $links; // List of links (associative array. Key=linkdate (e.g. "20110823_124546"), value= associative array (keys:title,description...)
     private $urls;  // List of all recorded URLs (key=url, value=linkdate) for fast reserve search (url-->linkdate)
     private $keys;  // List of linkdate keys (for the Iterator interface implementation)
     private $position; // Position in the $this->keys array. (for the Iterator interface implementation.)
-    private $loggedin; // Is the used logged in ? (used to filter private links)
+    private $loggedin; // Is the user logged in? (used to filter private links)
 
     // Constructor:
     function __construct($isLoggedIn)
-    // Input : $isLoggedIn : is the used logged in ?
+    // Input : $isLoggedIn : is the user logged in?
     {
         $this->loggedin = $isLoggedIn;
         $this->checkdb(); // Make sure data file exists.
@@ -711,7 +718,7 @@ class linkdb implements Iterator, Countable, ArrayAccess
     public function offsetSet($offset, $value)
     {
         if (!$this->loggedin) die('You are not authorized to add a link.');
-        if (empty($value['linkdate']) || empty($value['url'])) die('Internal Error: A link should always have a linkdate and url.');
+        if (empty($value['linkdate']) || empty($value['url'])) die('Internal Error: A link should always have a linkdate and URL.');
         if (empty($offset)) die('You must specify a key.');
         $this->links[$offset] = $value;
         $this->urls[$value['url']]=$offset;
@@ -774,19 +781,19 @@ class linkdb implements Iterator, Countable, ArrayAccess
         invalidateCaches();
     }
 
-    // Returns the link for a given URL (if it exists). false it does not exist.
+    // Returns the link for a given URL (if it exists). False if it does not exist.
     public function getLinkFromUrl($url)
     {
         if (isset($this->urls[$url])) return $this->links[$this->urls[$url]];
         return false;
     }
 
-    // Case insentitive search among links (in url, title and description). Returns filtered list of links.
-    // eg. print_r($mydb->filterFulltext('hollandais'));
+    // Case insensitive search among links (in the URLs, title and description). Returns filtered list of links.
+    // e.g. print_r($mydb->filterFulltext('hollandais'));
     public function filterFulltext($searchterms)
     {
         // FIXME: explode(' ',$searchterms) and perform a AND search.
-        // FIXME: accept double-quotes to search for a string "as is" ?
+        // FIXME: accept double-quotes to search for a string "as is"?
         $filtered=array();
         $s = strtolower($searchterms);
         foreach($this->links as $l)
@@ -803,7 +810,7 @@ class linkdb implements Iterator, Countable, ArrayAccess
 
     // Filter by tag.
     // You can specify one or more tags (tags can be separated by space or comma).
-    // eg. print_r($mydb->filterTags('linux programming'));
+    // e.g. print_r($mydb->filterTags('linux programming'));
     public function filterTags($tags,$casesensitive=false)
     {
         $t = str_replace(',',' ',($casesensitive?$tags:strtolower($tags)));
@@ -819,9 +826,9 @@ class linkdb implements Iterator, Countable, ArrayAccess
         return $filtered;
     }
 
-    // Filter by day. Day must be in the form 'YYYYMMDD' (eg. '20120125')
+    // Filter by day. Day must be in the form 'YYYYMMDD' (e.g. '20120125')
     // Sort order is: older articles first.
-    // eg. print_r($mydb->filterDay('20120125'));
+    // e.g. print_r($mydb->filterDay('20120125'));
     public function filterDay($day)
     {
         $filtered=array();
@@ -876,13 +883,13 @@ class linkdb implements Iterator, Countable, ArrayAccess
 }
 
 // ------------------------------------------------------------------------------------------
-// Ouput the last N links in RSS 2.0 format.
+// Output the last N links in RSS 2.0 format.
 function showRSS()
 {
     header('Content-Type: application/rss+xml; charset=utf-8');
 
     // $usepermalink : If true, use permalink instead of final link.
-    // User just has to add 'permalink' in URL parameters. eg. http://mysite.com/shaarli/?do=rss&permalinks
+    // User just has to add 'permalink' in URL parameters. e.g. http://mysite.com/shaarli/?do=rss&permalinks
     $usepermalinks = isset($_GET['permalinks']);
 
     // Cache system
@@ -891,9 +898,9 @@ function showRSS()
     $cached = $cache->cachedVersion(); if (!empty($cached)) { echo $cached; exit; }
 
     // If cached was not found (or not usable), then read the database and build the response:
-    $LINKSDB=new linkdb(isLoggedIn() || $GLOBALS['config']['OPEN_SHAARLI']);  // Read links from database (and filter private links if used it not logged in).
+    $LINKSDB=new linkdb(isLoggedIn() || $GLOBALS['config']['OPEN_SHAARLI']);  // Read links from database (and filter private links if user it not logged in).
 
-    // Optionnaly filter the results:
+    // Optionally filter the results:
     $linksToDisplay=array();
     if (!empty($_GET['searchterm'])) $linksToDisplay = $LINKSDB->filterFulltext($_GET['searchterm']);
     elseif (!empty($_GET['searchtags']))   $linksToDisplay = $LINKSDB->filterTags(trim($_GET['searchtags']));
@@ -942,7 +949,7 @@ function showRSS()
         echo '<description><![CDATA['.nl2br(keepMultipleSpaces(text2clickable(htmlspecialchars($link['description'])))).$descriptionlink.']]></description>'."\n</item>\n";
         $i++;
     }
-    echo '</channel></rss><!-- Cached version of '.pageUrl().' -->';
+    echo '</channel></rss><!-- Cached version of '.htmlspecialchars(pageUrl()).' -->';
 
     $cache->cache(ob_get_contents());
     ob_end_flush();
@@ -950,13 +957,13 @@ function showRSS()
 }
 
 // ------------------------------------------------------------------------------------------
-// Ouput the last N links in ATOM format.
+// Output the last N links in ATOM format.
 function showATOM()
 {
     header('Content-Type: application/atom+xml; charset=utf-8');
 
     // $usepermalink : If true, use permalink instead of final link.
-    // User just has to add 'permalink' in URL parameters. eg. http://mysite.com/shaarli/?do=atom&permalinks
+    // User just has to add 'permalink' in URL parameters. e.g. http://mysite.com/shaarli/?do=atom&permalinks
     $usepermalinks = isset($_GET['permalinks']);
 
     // Cache system
@@ -968,7 +975,7 @@ function showATOM()
     $LINKSDB=new linkdb(isLoggedIn() || $GLOBALS['config']['OPEN_SHAARLI']);  // Read links from database (and filter private links if used it not logged in).
 
 
-    // Optionnaly filter the results:
+    // Optionally filter the results:
     $linksToDisplay=array();
     if (!empty($_GET['searchterm'])) $linksToDisplay = $LINKSDB->filterFulltext($_GET['searchterm']);
     elseif (!empty($_GET['searchtags']))   $linksToDisplay = $LINKSDB->filterTags(trim($_GET['searchtags']));
@@ -1027,7 +1034,7 @@ function showATOM()
     $feed.='<author><name>'.htmlspecialchars($pageaddr).'</name><uri>'.htmlspecialchars($pageaddr).'</uri></author>';
     $feed.='<id>'.htmlspecialchars($pageaddr).'</id>'."\n\n"; // Yes, I know I should use a real IRI (RFC3987), but the site URL will do.
     $feed.=$entries;
-    $feed.='</feed><!-- Cached version of '.pageUrl().' -->';
+    $feed.='</feed><!-- Cached version of '.htmlspecialchars(pageUrl()).' -->';
     echo $feed;
 
     $cache->cache(ob_get_contents());
@@ -1064,7 +1071,7 @@ function showDailyRSS()
             if (empty($days[$day])) $days[$day]=array();
             $days[$day][]=$linkdate;
         }
-        if (count($days)>$nb_of_days) break; // Have we collected enough days ?
+        if (count($days)>$nb_of_days) break; // Have we collected enough days?
     }
 
     // Build the RSS feed.
@@ -1104,7 +1111,7 @@ function showDailyRSS()
         echo '<description><![CDATA['.$html.']]></description>'."\n</item>\n\n";
 
     }
-    echo '</channel></rss><!-- Cached version of '.pageUrl().' -->';
+    echo '</channel></rss><!-- Cached version of '.htmlspecialchars(pageUrl()).' -->';
 
     $cache->cache(ob_get_contents());
     ob_end_flush();
@@ -1143,7 +1150,7 @@ function showDaily()
     }
 
     /* We need to spread the articles on 3 columns.
-       I did not want to use a javascript lib like http://masonry.desandro.com/
+       I did not want to use a JavaScript lib like http://masonry.desandro.com/
        so I manually spread entries with a simple method: I roughly evaluate the
        height of a div according to title and description length.
     */
@@ -1154,7 +1161,7 @@ function showDaily()
         // Roughly estimate length of entry (by counting characters)
         // Title: 30 chars = 1 line. 1 line is 30 pixels height.
         // Description: 836 characters gives roughly 342 pixel height.
-        // This is not perfect, but it's usually ok.
+        // This is not perfect, but it's usually OK.
         $length=strlen($link['title'])+(342*strlen($link['description']))/836;
         if ($link['thumbnail']) $length +=100; // 1 thumbnails roughly takes 100 pixels height.
         // Then put in column which is the less filled:
@@ -1207,7 +1214,7 @@ function renderPage()
     // -------- Picture wall
     if (isset($_SERVER["QUERY_STRING"]) && startswith($_SERVER["QUERY_STRING"],'do=picwall'))
     {
-        // Optionnaly filter the results:
+        // Optionally filter the results:
         $links=array();
         if (!empty($_GET['searchterm'])) $links = $LINKSDB->filterFulltext($_GET['searchterm']);
         elseif (!empty($_GET['searchtags']))   $links = $LINKSDB->filterTags(trim($_GET['searchtags']));
@@ -1287,7 +1294,7 @@ function renderPage()
     if (isset($_GET['linksperpage']))
     {
         if (is_numeric($_GET['linksperpage'])) { $_SESSION['LINKS_PER_PAGE']=abs(intval($_GET['linksperpage'])); }
-        // Make sure the referer is from Shaarli itself.
+        // Make sure the referrer is Shaarli itself.
         $referer = '?';
         if (!empty($_SERVER['HTTP_REFERER']) && strcmp(parse_url($_SERVER['HTTP_REFERER'],PHP_URL_HOST),$_SERVER['HTTP_HOST'])==0)
             $referer = $_SERVER['HTTP_REFERER'];
@@ -1306,7 +1313,7 @@ function renderPage()
         {
             unset($_SESSION['privateonly']); // See all links
         }
-        // Make sure the referer is from Shaarli itself.
+        // Make sure the referrer is Shaarli itself.
         $referer = '?';
         if (!empty($_SERVER['HTTP_REFERER']) && strcmp(parse_url($_SERVER['HTTP_REFERER'],PHP_URL_HOST),$_SERVER['HTTP_HOST'])==0)
             $referer = $_SERVER['HTTP_REFERER'];
@@ -1317,17 +1324,17 @@ function renderPage()
     // -------- Handle other actions allowed for non-logged in users:
     if (!isLoggedIn())
     {
-        // User tries to post new link but is not loggedin:
+        // User tries to post new link but is not logged in:
         // Show login screen, then redirect to ?post=...
         if (isset($_GET['post']))
         {
-            header('Location: ?do=login&post='.urlencode($_GET['post']).(!empty($_GET['title'])?'&title='.urlencode($_GET['title']):'').(!empty($_GET['source'])?'&source='.urlencode($_GET['source']):'')); // Redirect to login page, then back to post link.
+            header('Location: ?do=login&post='.urlencode($_GET['post']).(!empty($_GET['title'])?'&title='.urlencode($_GET['title']):'').(!empty($_GET['description'])?'&description='.urlencode($_GET['description']):'').(!empty($_GET['source'])?'&source='.urlencode($_GET['source']):'')); // Redirect to login page, then back to post link.
             exit;
         }
         $PAGE = new pageBuilder;
         buildLinkList($PAGE,$LINKSDB); // Compute list of links to display
         $PAGE->renderPage('linklist');
-        exit; // Never remove this one ! All operations below are reserved for logged in user.
+        exit; // Never remove this one! All operations below are reserved for logged in user.
     }
 
     // -------- All other functions are reserved for the registered user:
@@ -1348,7 +1355,7 @@ function renderPage()
         if ($GLOBALS['config']['OPEN_SHAARLI']) die('You are not supposed to change a password on an Open Shaarli.');
         if (!empty($_POST['setpassword']) && !empty($_POST['oldpassword']))
         {
-            if (!tokenOk($_POST['token'])) die('Wrong token.'); // Go away !
+            if (!tokenOk($_POST['token'])) die('Wrong token.'); // Go away!
 
             // Make sure old password is correct.
             $oldhash = sha1($_POST['oldpassword'].$GLOBALS['login'].$GLOBALS['salt']);
@@ -1375,13 +1382,14 @@ function renderPage()
     {
         if (!empty($_POST['title']) )
         {
-            if (!tokenOk($_POST['token'])) die('Wrong token.'); // Go away !
+            if (!tokenOk($_POST['token'])) die('Wrong token.'); // Go away!
             $tz = 'UTC';
             if (!empty($_POST['continent']) && !empty($_POST['city']))
                 if (isTZvalid($_POST['continent'],$_POST['city']))
                     $tz = $_POST['continent'].'/'.$_POST['city'];
             $GLOBALS['timezone'] = $tz;
             $GLOBALS['title']=$_POST['title'];
+            $GLOBALS['titleLink']=$_POST['titleLink'];
             $GLOBALS['redirector']=$_POST['redirector'];
             $GLOBALS['disablesessionprotection']=!empty($_POST['disablesessionprotection']);
             $GLOBALS['disablejquery']=!empty($_POST['disablejquery']);
@@ -1398,7 +1406,7 @@ function renderPage()
             $PAGE->assign('title',htmlspecialchars( empty($GLOBALS['title']) ? '' : $GLOBALS['title'] , ENT_QUOTES));
             $PAGE->assign('redirector',htmlspecialchars( empty($GLOBALS['redirector']) ? '' : $GLOBALS['redirector'] , ENT_QUOTES));
             list($timezone_form,$timezone_js) = templateTZform($GLOBALS['timezone']);
-            $PAGE->assign('timezone_form',$timezone_form); // FIXME: put entire tz form generation in template ?
+            $PAGE->assign('timezone_form',$timezone_form); // FIXME: Put entire tz form generation in template?
             $PAGE->assign('timezone_js',$timezone_js);
             $PAGE->renderPage('configure');
             exit;
@@ -1422,7 +1430,7 @@ function renderPage()
         if (!empty($_POST['deletetag']) && !empty($_POST['fromtag']))
         {
             $needle=trim($_POST['fromtag']);
-            $linksToAlter = $LINKSDB->filterTags($needle,true); // true for case-sensitive tag search.
+            $linksToAlter = $LINKSDB->filterTags($needle,true); // True for case-sensitive tag search.
             foreach($linksToAlter as $key=>$value)
             {
                 $tags = explode(' ',trim($value['tags']));
@@ -1430,7 +1438,7 @@ function renderPage()
                 $value['tags']=trim(implode(' ',$tags));
                 $LINKSDB[$key]=$value;
             }
-            $LINKSDB->savedb(); // save to disk
+            $LINKSDB->savedb(); // Save to disk.
             echo '<script language="JavaScript">alert("Tag was removed from '.count($linksToAlter).' links.");document.location=\'?\';</script>';
             exit;
         }
@@ -1443,17 +1451,17 @@ function renderPage()
             foreach($linksToAlter as $key=>$value)
             {
                 $tags = explode(' ',trim($value['tags']));
-                $tags[array_search($needle,$tags)] = trim($_POST['totag']); // Remplace tags value.
+                $tags[array_search($needle,$tags)] = trim($_POST['totag']); // Replace tags value.
                 $value['tags']=trim(implode(' ',$tags));
                 $LINKSDB[$key]=$value;
             }
-            $LINKSDB->savedb(); // save to disk
+            $LINKSDB->savedb(); // Save to disk.
             echo '<script language="JavaScript">alert("Tag was renamed in '.count($linksToAlter).' links.");document.location=\'?searchtags='.urlencode($_POST['totag']).'\';</script>';
             exit;
         }
     }
 
-    // -------- User wants to add a link without using the bookmarklet: show form.
+    // -------- User wants to add a link without using the bookmarklet: Show form.
     if (isset($_SERVER["QUERY_STRING"]) && startswith($_SERVER["QUERY_STRING"],'do=addlink'))
     {
         $PAGE = new pageBuilder;
@@ -1465,7 +1473,7 @@ function renderPage()
     // -------- User clicked the "Save" button when editing a link: Save link to database.
     if (isset($_POST['save_edit']))
     {
-        if (!tokenOk($_POST['token'])) die('Wrong token.'); // Go away !
+        if (!tokenOk($_POST['token'])) die('Wrong token.'); // Go away!
         $tags = trim(preg_replace('/\s\s+/',' ', $_POST['lf_tags'])); // Remove multiple spaces.
         $linkdate=$_POST['lf_linkdate'];
         $url = trim($_POST['lf_url']);
@@ -1475,7 +1483,7 @@ function renderPage()
                       'linkdate'=>$linkdate,'tags'=>str_replace(',',' ',$tags));
         if ($link['title']=='') $link['title']=$link['url']; // If title is empty, use the URL as title.
         $LINKSDB[$linkdate] = $link;
-        $LINKSDB->savedb(); // save to disk
+        $LINKSDB->savedb(); // Save to disk.
         pubsubhub();
 
         // If we are called from the bookmarklet, we must close the popup:
@@ -1489,7 +1497,7 @@ function renderPage()
     // -------- User clicked the "Cancel" button when editing a link.
     if (isset($_POST['cancel_edit']))
     {
-        // If we are called from the bookmarklet, we must close the popup;
+        // If we are called from the bookmarklet, we must close the popup:
         if (isset($_GET['source']) && $_GET['source']=='bookmarklet') { echo '<script language="JavaScript">self.close();</script>'; exit; }
         $returnurl = ( isset($_POST['returnurl']) ? $_POST['returnurl'] : '?' );
         $returnurl .= '#'.smallHash($_POST['lf_linkdate']);  // Scroll to the link which has been edited.
@@ -1497,12 +1505,12 @@ function renderPage()
         exit;
     }
 
-    // -------- User clicked the "Delete" button when editing a link : Delete link from database.
+    // -------- User clicked the "Delete" button when editing a link: Delete link from database.
     if (isset($_POST['delete_link']))
     {
         if (!tokenOk($_POST['token'])) die('Wrong token.');
         // We do not need to ask for confirmation:
-        // - confirmation is handled by javascript
+        // - confirmation is handled by JavaScript
         // - we are protected from XSRF by the token.
         $linkdate=$_POST['lf_linkdate'];
         unset($LINKSDB[$linkdate]);
@@ -1552,7 +1560,7 @@ function renderPage()
             $tags = (empty($_GET['tags']) ? '' : $_GET['tags'] ); // Get tags if it was provided in URL
             $private = (!empty($_GET['private']) && $_GET['private'] === "1" ? 1 : 0); // Get private if it was provided in URL 
             if (($url!='') && parse_url($url,PHP_URL_SCHEME)=='') $url = 'http://'.$url;
-            // If this is an HTTP link, we try go get the page to extact the title (otherwise we will to straight to the edit form.)
+            // If this is an HTTP link, we try go get the page to extract the title (otherwise we will to straight to the edit form.)
             if (empty($title) && parse_url($url,PHP_URL_SCHEME)=='http')
             {
                 list($status,$headers,$data) = getHTTP($url,4); // Short timeout to keep the application responsive.
@@ -1581,7 +1589,11 @@ function renderPage()
                                                }
                                        }
             }
-            if ($url=='') $url='?'.smallHash($linkdate); // In case of empty URL, this is just a text (with a link that point to itself)
+            if ($url=='') // In case of empty URL, this is just a text (with a link that points to itself)
+            {
+                $url='?'.smallHash($linkdate);
+                $title='Note: ';
+            }
             $link = array('linkdate'=>$linkdate,'title'=>$title,'url'=>$url,'description'=>$description,'tags'=>$tags,'private'=>$private);
         }
 
@@ -1606,7 +1618,7 @@ function renderPage()
             exit;
         }
         $exportWhat=$_GET['what'];
-        if (!array_intersect(array('all','public','private'),array($exportWhat))) die('What are you trying to export ???');
+        if (!array_intersect(array('all','public','private'),array($exportWhat))) die('What are you trying to export???');
 
         header('Content-Type: text/html; charset=utf-8');
         header('Content-disposition: attachment; filename=bookmarks_'.$exportWhat.'_'.strval(date('Ymd_His')).'.html');
@@ -1679,8 +1691,8 @@ function importFile()
     $filename=$_FILES['filetoupload']['name'];
     $filesize=$_FILES['filetoupload']['size'];
     $data=file_get_contents($_FILES['filetoupload']['tmp_name']);
-    $private = (empty($_POST['private']) ? 0 : 1); // Should the links be imported as private ?
-    $overwrite = !empty($_POST['overwrite']) ; // Should the imported links overwrite existing ones ?
+    $private = (empty($_POST['private']) ? 0 : 1); // Should the links be imported as private?
+    $overwrite = !empty($_POST['overwrite']) ; // Should the imported links overwrite existing ones?
     $import_count=0;
 
     // Sniff file type:
@@ -1691,7 +1703,7 @@ function importFile()
     if ($type=='netscape')
     {
         // This is a standard Netscape-style bookmark file.
-        // This format is supported by all browsers (except IE, of course), also delicious, diigo and others.
+        // This format is supported by all browsers (except IE, of course), also Delicious, Diigo and others.
         foreach(explode('<DT>',$data) as $html) // explode is very fast
         {
             $link = array('linkdate'=>'','title'=>'','url'=>'','description'=>'','tags'=>'','private'=>0);
@@ -1725,14 +1737,14 @@ function importFile()
 
                        // Make sure date/time is not already used by another link.
                        // (Some bookmark files have several different links with the same ADD_DATE)
-                       // We increment date by 1 second until we find a date which is not used in db.
+                       // We increment date by 1 second until we find a date which is not used in DB.
                        // (so that links that have the same date/time are more or less kept grouped by date, but do not conflict.)
                        while (!empty($LINKSDB[date('Ymd_His',$raw_add_date)])) { $raw_add_date++; }// Yes, I know it's ugly.
                        $link['linkdate']=date('Ymd_His',$raw_add_date);
                        $LINKSDB[$link['linkdate']] = $link;
                        $import_count++;
                     }
-                    else // link already present in database.
+                    else // Link already present in database.
                     {
                         if ($overwrite)
                         {   // If overwrite is required, we import link data, except date/time.
@@ -1747,11 +1759,11 @@ function importFile()
         }
         $LINKSDB->savedb();
 
-        echo '<script language="JavaScript">alert("File '.$filename.' ('.$filesize.' bytes) was successfully processed: '.$import_count.' links imported.");document.location=\'?\';</script>';
+        echo '<script language="JavaScript">alert("File '.json_encode($filename).' ('.$filesize.' bytes) was successfully processed: '.$import_count.' links imported.");document.location=\'?\';</script>';
     }
     else
     {
-        echo '<script language="JavaScript">alert("File '.$filename.' ('.$filesize.' bytes) has an unknown file format. Nothing was imported.");document.location=\'?\';</script>';
+        echo '<script language="JavaScript">alert("File '.json_encode($filename).' ('.$filesize.' bytes) has an unknown file format. Nothing was imported.");document.location=\'?\';</script>';
     }
 }
 
@@ -1783,13 +1795,13 @@ function buildLinkList($PAGE,$LINKSDB)
         {
             header($_SERVER["SERVER_PROTOCOL"]." 404 Not Found");
             echo '<h1>404 Not found.</h1>Oh crap. The link you are trying to reach does not exist or has been deleted.';
-            echo '<br>You would mind <a href="?">clicking here</a> ?';
+            echo '<br>You would mind <a href="?">clicking here</a>?';
             exit;
         }
         $search_type='permalink';
     }
     else
-        $linksToDisplay = $LINKSDB;  // otherwise, display without filtering.
+        $linksToDisplay = $LINKSDB;  // Otherwise, display without filtering.
 
     // Option: Show only private links
     if (!empty($_SESSION['privateonly']))
@@ -1803,11 +1815,11 @@ function buildLinkList($PAGE,$LINKSDB)
     }
 
     // ---- Handle paging.
-    /* Can someone explain to me why you get the following error when using array_keys() on an object which implements the interface ArrayAccess ???
+    /* Can someone explain to me why you get the following error when using array_keys() on an object which implements the interface ArrayAccess???
        "Warning: array_keys() expects parameter 1 to be array, object given in ... "
        If my class implements ArrayAccess, why won't array_keys() accept it ?  ( $keys=array_keys($linksToDisplay); )
     */
-    $keys=array(); foreach($linksToDisplay as $key=>$value) { $keys[]=$key; } // Stupid and ugly. Thanks php.
+    $keys=array(); foreach($linksToDisplay as $key=>$value) { $keys[]=$key; } // Stupid and ugly. Thanks PHP.
 
     // If there is only a single link, we change on-the-fly the title of the page.
     if (count($linksToDisplay)==1) $GLOBALS['pagetitle'] = $linksToDisplay[$keys[0]]['title'].' - '.$GLOBALS['title'];
@@ -1854,7 +1866,7 @@ function buildLinkList($PAGE,$LINKSDB)
     $PAGE->assign('result_count',count($linksToDisplay));
     $PAGE->assign('search_type',$search_type);
     $PAGE->assign('search_crits',$search_crits);
-    $PAGE->assign('redirector',empty($GLOBALS['redirector']) ? '' : $GLOBALS['redirector']); // optional redirector URL
+    $PAGE->assign('redirector',empty($GLOBALS['redirector']) ? '' : $GLOBALS['redirector']); // Optional redirector URL.
     $PAGE->assign('token',$token);
     $PAGE->assign('links',$linkDisp);
     return;
@@ -1862,9 +1874,9 @@ function buildLinkList($PAGE,$LINKSDB)
 
 // Compute the thumbnail for a link.
 //
-// with a link to the original URL.
+// With a link to the original URL.
 // Understands various services (youtube.com...)
-// Input: $url = url for which the thumbnail must be found.
+// Input: $url = URL for which the thumbnail must be found.
 //        $href = if provided, this URL will be followed instead of $url
 // Returns an associative array with thumbnail attributes (src,href,width,height,style,alt)
 // Some of them may be missing.
@@ -1875,19 +1887,19 @@ function computeThumbnail($url,$href=false)
     if ($href==false) $href=$url;
 
     // For most hosts, the URL of the thumbnail can be easily deduced from the URL of the link.
-    // (eg. http://www.youtube.com/watch?v=spVypYk4kto --->  http://img.youtube.com/vi/spVypYk4kto/default.jpg )
+    // (e.g. http://www.youtube.com/watch?v=spVypYk4kto --->  http://img.youtube.com/vi/spVypYk4kto/default.jpg )
     //                                     ^^^^^^^^^^^                                 ^^^^^^^^^^^
     $domain = parse_url($url,PHP_URL_HOST);
     if ($domain=='youtube.com' || $domain=='www.youtube.com')
     {
         parse_str(parse_url($url,PHP_URL_QUERY), $params); // Extract video ID and get thumbnail
-        if (!empty($params['v'])) return array('src'=>'http://img.youtube.com/vi/'.$params['v'].'/default.jpg',
+        if (!empty($params['v'])) return array('src'=>'https://img.youtube.com/vi/'.$params['v'].'/default.jpg',
                                                'href'=>$href,'width'=>'120','height'=>'90','alt'=>'YouTube thumbnail');
     }
     if ($domain=='youtu.be') // Youtube short links
     {
         $path = parse_url($url,PHP_URL_PATH);
-        return array('src'=>'http://img.youtube.com/vi'.$path.'/default.jpg',
+        return array('src'=>'https://img.youtube.com/vi'.$path.'/default.jpg',
                      'href'=>$href,'width'=>'120','height'=>'90','alt'=>'YouTube thumbnail');
     }
     if ($domain=='pix.toile-libre.org') // pix.toile-libre.org image hosting
@@ -1901,18 +1913,18 @@ function computeThumbnail($url,$href=false)
     {
         $path = parse_url($url,PHP_URL_PATH);
         if (startsWith($path,'/a/')) return array(); // Thumbnails for albums are not available.
-        if (startsWith($path,'/r/')) return array('src'=>'http://i.imgur.com/'.basename($path).'s.jpg',
+        if (startsWith($path,'/r/')) return array('src'=>'https://i.imgur.com/'.basename($path).'s.jpg',
                                                   'href'=>$href,'width'=>'90','height'=>'90','alt'=>'imgur.com thumbnail');
-        if (startsWith($path,'/gallery/')) return array('src'=>'http://i.imgur.com'.substr($path,8).'s.jpg',
+        if (startsWith($path,'/gallery/')) return array('src'=>'https://i.imgur.com'.substr($path,8).'s.jpg',
                                                         'href'=>$href,'width'=>'90','height'=>'90','alt'=>'imgur.com thumbnail');
 
-        if (substr_count($path,'/')==1) return array('src'=>'http://i.imgur.com/'.substr($path,1).'s.jpg',
+        if (substr_count($path,'/')==1) return array('src'=>'https://i.imgur.com/'.substr($path,1).'s.jpg',
                                                      'href'=>$href,'width'=>'90','height'=>'90','alt'=>'imgur.com thumbnail');
     }
     if ($domain=='i.imgur.com')
     {
         $pi = pathinfo(parse_url($url,PHP_URL_PATH));
-        if (!empty($pi['filename'])) return array('src'=>'http://i.imgur.com/'.$pi['filename'].'s.jpg',
+        if (!empty($pi['filename'])) return array('src'=>'https://i.imgur.com/'.$pi['filename'].'s.jpg',
                                                   'href'=>$href,'width'=>'90','height'=>'90','alt'=>'imgur.com thumbnail');
     }
     if ($domain=='dailymotion.com' || $domain=='www.dailymotion.com')
@@ -1948,17 +1960,17 @@ function computeThumbnail($url,$href=false)
     )
     {
         if ($domain=='vimeo.com')
-        {   // Make sure this vimeo url points to a video (/xxx... where xxx is numeric)
+        {   // Make sure this vimeo URL points to a video (/xxx... where xxx is numeric)
             $path = parse_url($url,PHP_URL_PATH);
             if (!preg_match('!/\d+.+?!',$path)) return array(); // This is not a single video URL.
         }
         if ($domain=='xkcd.com' || endsWith($domain,'.xkcd.com'))
-        {   // Make sure this url points to a single comic (/xxx... where xxx is numeric)
+        {   // Make sure this URL points to a single comic (/xxx... where xxx is numeric)
             $path = parse_url($url,PHP_URL_PATH);
             if (!preg_match('!/\d+.+?!',$path)) return array();
         }
         if ($domain=='ted.com' || endsWith($domain,'.ted.com'))
-        {   // Make sure this TED url points to a video (/talks/...)
+        {   // Make sure this TED URL points to a video (/talks/...)
             $path = parse_url($url,PHP_URL_PATH);
             if ("/talks/" !== substr($path,0,7)) return array(); // This is not a single video URL.
         }
@@ -1985,7 +1997,7 @@ function computeThumbnail($url,$href=false)
 // Returns the HTML code to display a thumbnail for a link
 // with a link to the original URL.
 // Understands various services (youtube.com...)
-// Input: $url = url for which the thumbnail must be found.
+// Input: $url = URL for which the thumbnail must be found.
 //        $href = if provided, this URL will be followed instead of $url
 // Returns '' if no thumbnail available.
 function thumbnail($url,$href=false)
@@ -2006,7 +2018,7 @@ function thumbnail($url,$href=false)
 // Returns the HTML code to display a thumbnail for a link
 // for the picture wall (using lazy image loading)
 // Understands various services (youtube.com...)
-// Input: $url = url for which the thumbnail must be found.
+// Input: $url = URL for which the thumbnail must be found.
 //        $href = if provided, this URL will be followed instead of $url
 // Returns '' if no thumbnail available.
 function lazyThumbnail($url,$href=false)
@@ -2016,7 +2028,7 @@ function lazyThumbnail($url,$href=false)
 
     $html='<a href="'.htmlspecialchars($t['href']).'">';
 
-    // Lazy image (only loaded by javascript when in the viewport).
+    // Lazy image (only loaded by JavaScript when in the viewport).
     if (!empty($GLOBALS['disablejquery'])) // (except if jQuery is disabled)
         $html.='<img class="lazyimage" src="'.htmlspecialchars($t['src']).'"';
     else
@@ -2028,7 +2040,7 @@ function lazyThumbnail($url,$href=false)
     if (!empty($t['alt']))    $html.=' alt="'.htmlspecialchars($t['alt']).'"';
     $html.='>';
 
-    // No-javascript fallback.
+    // No-JavaScript fallback.
     $html.='<noscript><img src="'.htmlspecialchars($t['src']).'"';
     if (!empty($t['width']))  $html.=' width="'.htmlspecialchars($t['width']).'"';
     if (!empty($t['height'])) $html.=' height="'.htmlspecialchars($t['height']).'"';
@@ -2056,7 +2068,9 @@ function install()
     {   // Step 2: Check if data in session is correct.
         echo '<pre>Sessions do not seem to work correctly on your server.<br>';
         echo 'Make sure the variable session.save_path is set correctly in your php config, and that you have write access to it.<br>';
-        echo 'It currently points to '.session_save_path().'<br><br><a href="?">Click to try again.</a></pre>';
+        echo 'It currently points to '.session_save_path().'<br>';
+        echo 'Check that the hostname used to access Shaarli contains a dot. On some browsers, accessing your server via a hostname like \'localhost\' or any custom hostname without a dot causes cookie storage to fail. We recommend accessing your server via it\'s IP address or Fully Qualified Domain Name.<br>';
+        echo '<br><a href="?">Click to try again.</a></pre>';
         die;
     }
     if (!isset($_SESSION['session_tested']))
@@ -2065,7 +2079,7 @@ function install()
         header('Location: '.indexUrl().'?test_session');  // Redirect to check stored data.
     }
     if (isset($_GET['test_session']))
-    {   // Step 3: Sessions are ok. Remove test parameter from URL.
+    {   // Step 3: Sessions are OK. Remove test parameter from URL.
         header('Location: '.indexUrl());
     }
 
@@ -2083,7 +2097,7 @@ function install()
         $GLOBALS['hash'] = sha1($_POST['setpassword'].$GLOBALS['login'].$GLOBALS['salt']);
         $GLOBALS['title'] = (empty($_POST['title']) ? 'Shared links on '.htmlspecialchars(indexUrl()) : $_POST['title'] );
         writeConfig();
-        echo '<script language="JavaScript">alert("Shaarli is now configured. Please enter your login/password and start shaaring your links !");document.location=\'?do=login\';</script>';
+        echo '<script language="JavaScript">alert("Shaarli is now configured. Please enter your login/password and start shaaring your links!");document.location=\'?do=login\';</script>';
         exit;
     }
 
@@ -2098,14 +2112,14 @@ function install()
     exit;
 }
 
-// Generates the timezone selection form and javascript.
+// Generates the timezone selection form and JavaScript.
 // Input: (optional) current timezone (can be 'UTC/UTC'). It will be pre-selected.
 // Output: array(html,js)
 // Example: list($htmlform,$js) = templateTZform('Europe/Paris');  // Europe/Paris pre-selected.
-// Returns array('','') if server does not support timezones list. (eg. php 5.1 on free.fr)
+// Returns array('','') if server does not support timezones list. (e.g. PHP 5.1 on free.fr)
 function templateTZform($ptz=false)
 {
-    if (function_exists('timezone_identifiers_list')) // because of old php version (5.1) which can be found on free.fr
+    if (function_exists('timezone_identifiers_list')) // because of old PHP version (5.1) which can be found on free.fr
     {
         // Try to split the provided timezone.
         if ($ptz==false) { $l=timezone_identifiers_list(); $ptz=$l[0]; }
@@ -2114,7 +2128,7 @@ function templateTZform($ptz=false)
         // Display config form:
         $timezone_form = '';
         $timezone_js = '';
-        // The list is in the forme "Europe/Paris", "America/Argentina/Buenos_Aires"...
+        // The list is in the form "Europe/Paris", "America/Argentina/Buenos_Aires"...
         // We split the list in continents/cities.
         $continents = array();
         $cities = array();
@@ -2152,9 +2166,9 @@ function templateTZform($ptz=false)
 function isTZvalid($continent,$city)
 {
     $tz = $continent.'/'.$city;
-    if (function_exists('timezone_identifiers_list')) // because of old php version (5.1) which can be found on free.fr
+    if (function_exists('timezone_identifiers_list')) // because of old PHP version (5.1) which can be found on free.fr
     {
-        if (in_array($tz, timezone_identifiers_list())) // it's a valid timezone ?
+        if (in_array($tz, timezone_identifiers_list())) // it's a valid timezone?
                     return true;
     }
     return false;
@@ -2197,7 +2211,7 @@ if (!function_exists('json_encode')) {
 }
 
 // Webservices (for use with jQuery/jQueryUI)
-// eg.  index.php?ws=tags&term=minecr
+// e.g. index.php?ws=tags&term=minecr
 function processWS()
 {
     if (empty($_GET['ws']) || empty($_GET['term'])) return;
@@ -2205,7 +2219,7 @@ function processWS()
     $LINKSDB=new linkdb(isLoggedIn() || $GLOBALS['config']['OPEN_SHAARLI']);  // Read links from database (and filter private links if used it not logged in).
     header('Content-Type: application/json; charset=utf-8');
 
-    // Search in tags (case insentitive, cumulative search)
+    // Search in tags (case insensitive, cumulative search)
     if ($_GET['ws']=='tags')
     {
         $tags=explode(' ',str_replace(',',' ',$term)); $last = array_pop($tags); // Get the last term ("a b c d" ==> "a b c", "d")
@@ -2221,7 +2235,7 @@ function processWS()
         exit;
     }
 
-    // Search a single tag (case sentitive, single tag search)
+    // Search a single tag (case sensitive, single tag search)
     if ($_GET['ws']=='singletag')
     {
         /* To speed up things, we store list of tags in session */
@@ -2237,13 +2251,14 @@ function processWS()
 
 // Re-write configuration file according to globals.
 // Requires some $GLOBALS to be set (login,hash,salt,title).
-// If the config file cannot be saved, an error message is dislayed and the user is redirected to "Tools" menu.
+// If the config file cannot be saved, an error message is displayed and the user is redirected to "Tools" menu.
 // (otherwise, the function simply returns.)
 function writeConfig()
 {
     if (is_file($GLOBALS['config']['CONFIG_FILE']) && !isLoggedIn()) die('You are not authorized to alter config.'); // Only logged in user can alter config.
     $config='<?php $GLOBALS[\'login\']='.var_export($GLOBALS['login'],true).'; $GLOBALS[\'hash\']='.var_export($GLOBALS['hash'],true).'; $GLOBALS[\'salt\']='.var_export($GLOBALS['salt'],true).'; ';
     $config .='$GLOBALS[\'timezone\']='.var_export($GLOBALS['timezone'],true).'; date_default_timezone_set('.var_export($GLOBALS['timezone'],true).'); $GLOBALS[\'title\']='.var_export($GLOBALS['title'],true).';';
+    $config .= '$GLOBALS[\'titleLink\']='.var_export($GLOBALS['titleLink'],true).'; ';
     $config .= '$GLOBALS[\'redirector\']='.var_export($GLOBALS['redirector'],true).'; ';
     $config .= '$GLOBALS[\'disablesessionprotection\']='.var_export($GLOBALS['disablesessionprotection'],true).'; ';
     $config .= '$GLOBALS[\'disablejquery\']='.var_export($GLOBALS['disablejquery'],true).'; ';
@@ -2256,12 +2271,12 @@ function writeConfig()
     }
 }
 
-/* Because some f*cking services like Flickr require an extra HTTP request to get the thumbnail URL,
+/* Because some f*cking services like flickr require an extra HTTP request to get the thumbnail URL,
    I have deported the thumbnail URL code generation here, otherwise this would slow down page generation.
-   The following function takes the URL a link (eg. a flickr page) and return the proper thumbnail.
-   This function is called by passing the url:
+   The following function takes the URL a link (e.g. a flickr page) and return the proper thumbnail.
+   This function is called by passing the URL:
    http://mywebsite.com/shaarli/?do=genthumbnail&hmac=[HMAC]&url=[URL]
-   [URL] is the URL of the link (eg. a flickr page)
+   [URL] is the URL of the link (e.g. a flickr page)
    [HMAC] is the signature for the [URL] (so that these URL cannot be forged).
    The function below will fetch the image from the webservice and store it in the cache.
 */
@@ -2269,7 +2284,7 @@ function genThumbnail()
 {
     // Make sure the parameters in the URL were generated by us.
     $sign = hash_hmac('sha256', $_GET['url'], $GLOBALS['salt']);
-    if ($sign!=$_GET['hmac']) die('Naughty boy !');
+    if ($sign!=$_GET['hmac']) die('Naughty boy!');
 
     // Let's see if we don't already have the image for this URL in the cache.
     $thumbname=hash('sha1',$_GET['url']).'.jpg';
@@ -2294,22 +2309,22 @@ function genThumbnail()
 
     if ($domain=='flickr.com' || endsWith($domain,'.flickr.com'))
     {
-        // Crude replacement to handle new Flickr domain policy (They prefer www. now)
+        // Crude replacement to handle new flickr domain policy (They prefer www. now)
         $url = str_replace('http://flickr.com/','http://www.flickr.com/',$url);
 
         // Is this a link to an image, or to a flickr page ?
         $imageurl='';
         if (endswith(parse_url($url,PHP_URL_PATH),'.jpg'))
-        {  // This is a direct link to an image. eg. http://farm1.staticflickr.com/5/5921913_ac83ed27bd_o.jpg
+        {  // This is a direct link to an image. e.g. http://farm1.staticflickr.com/5/5921913_ac83ed27bd_o.jpg
             preg_match('!(http://farm\d+\.staticflickr\.com/\d+/\d+_\w+_)\w.jpg!',$url,$matches);
             if (!empty($matches[1])) $imageurl=$matches[1].'m.jpg';
         }
-        else // this is a flickr page (html)
+        else // This is a flickr page (html)
         {
             list($httpstatus,$headers,$data) = getHTTP($url,20); // Get the flickr html page.
             if (strpos($httpstatus,'200 OK')!==false)
             {
-                // Flickr now nicely provides the URL of the thumbnail in each flickr page.
+                // flickr now nicely provides the URL of the thumbnail in each flickr page.
                 preg_match('!<link rel=\"image_src\" href=\"(.+?)\"!',$data,$matches);
                 if (!empty($matches[1])) $imageurl=$matches[1];
 
@@ -2340,9 +2355,9 @@ function genThumbnail()
     elseif ($domain=='vimeo.com' )
     {
         // This is more complex: we have to perform a HTTP request, then parse the result.
-        // Maybe we should deport this to javascript ? Example: http://stackoverflow.com/questions/1361149/get-img-thumbnails-from-vimeo/4285098#4285098
+        // Maybe we should deport this to JavaScript ? Example: http://stackoverflow.com/questions/1361149/get-img-thumbnails-from-vimeo/4285098#4285098
         $vid = substr(parse_url($url,PHP_URL_PATH),1);
-        list($httpstatus,$headers,$data) = getHTTP('http://vimeo.com/api/v2/video/'.htmlspecialchars($vid).'.php',5);
+        list($httpstatus,$headers,$data) = getHTTP('https://vimeo.com/api/v2/video/'.htmlspecialchars($vid).'.php',5);
         if (strpos($httpstatus,'200 OK')!==false)
         {
             $t = unserialize($data);
@@ -2476,7 +2491,7 @@ function resizeImage($filepath)
 }
 
 // Invalidate caches when the database is changed or the user logs out.
-// (eg. tags cache).
+// (e.g. tags cache).
 function invalidateCaches()
 {
     unset($_SESSION['tags']);  // Purge cache attached to session.
diff --git a/pagecache/.htaccess b/pagecache/.htaccess
new file mode 100644 (file)
index 0000000..b584d98
--- /dev/null
@@ -0,0 +1,2 @@
+Allow from none
+Deny from all
diff --git a/shaarli_version.txt b/shaarli_version.txt
new file mode 100644 (file)
index 0000000..5404909
--- /dev/null
@@ -0,0 +1 @@
+0.0.42 beta
diff --git a/tmp/.htaccess b/tmp/.htaccess
new file mode 100644 (file)
index 0000000..b584d98
--- /dev/null
@@ -0,0 +1,2 @@
+Allow from none
+Deny from all
index 62296cb2f5d96a9b7480dd0db94be0675499d438..645107aecfe32fa1da9097f6f58da58ea3a66081 100644 (file)
@@ -11,6 +11,7 @@
 
            <tr><td><b>Page title:</b></td><td><input type="text" name="title" id="title" size="50" value="{$title}"></td></tr>
 
+           <tr><td><b>Title link:</b></td><td><input type="text" name="titleLink" id="titleLink" size="50" value="{$titleLink}"><br/><label for="titleLink">(default value is: ?)</label></td></tr>
            <tr><td valign="top"><b>Timezone:</b></td><td valign="top">{$timezone_form}</td></tr>
 
            <tr><td valign="top"><b>Redirector</b></td><td><input type="text" name="redirector" id="redirector" size="50" value="{$redirector}"><br>(e.g. <i>http://anonym.to/?</i> will mask the HTTP_REFERER)</td></tr>
@@ -18,7 +19,7 @@
         <tr><td valign="top"><b>Security:</b></td><td><input type="checkbox" name="disablesessionprotection" id="disablesessionprotection" {if="!empty($GLOBALS['disablesessionprotection'])"}checked{/if}><label for="disablesessionprotection">&nbsp;Disable session cookie hijacking protection (Check this if you get disconnected often or if your IP address changes often.)</label></td></tr>
 
         <tr><td valign="top"><b>Features:</b></td><td>
-               <input type="checkbox" name="disablejquery" id="disablejquery" {if="!empty($GLOBALS['disablejquery'])"}checked{/if}><label for="disablejquery">&nbsp;Disable jQuery and all heavy javascript (for example: Autocomplete in tags. Useful for slow computers.)</label>
+               <input type="checkbox" name="disablejquery" id="disablejquery" {if="!empty($GLOBALS['disablejquery'])"}checked{/if}><label for="disablejquery">&nbsp;Disable jQuery and all heavy JavaScript (for example: Autocomplete in tags. Useful for slow computers.)</label>
         </td></tr>
         <tr><td valign="top"><b>New link:</b></td><td>
                <input type="checkbox" name="privateLinkByDefault" id="privateLinkByDefault" {if="!empty($GLOBALS['privateLinkByDefault'])"}checked{/if}/><label for="privateLinkByDefault">&nbsp;All new link are private by default</label></td>
index 9e581fc9632b96f2912b75c87f04c83f31788656..259e56ee0af9d16df1c837f37f93ede8124c8c6c 100644 (file)
@@ -5,7 +5,7 @@
 <div id="pageheader">
        {include="page.header"}
        <div id="uploaddiv">
-       Import Netscape html bookmarks (as exported from Firefox/Chrome/Opera/delicious/diigo...) (Max: {$maxfilesize|htmlspecialchars} bytes).
+       Import Netscape HTML bookmarks (as exported from Firefox/Chrome/Opera/Delicious/Diigo...) (Max: {$maxfilesize|htmlspecialchars} bytes).
        <form method="POST" action="?do=upload" enctype="multipart/form-data" name="uploadform" id="uploadform">
            <input type="hidden" name="token" value="{$token}">
            <input type="file" name="filetoupload" size="80">
index 5a742737031e4208e90a33922e16b49a157e351a..acb4bab062a272ea926b59be8d9162595d34d082 100644 (file)
@@ -48,6 +48,9 @@
                 {else}
                     <span class="linkdate" title="Short link here"><a href="?{$value.linkdate|smallHash}">permalink</a> - </span>
                 {/if}
+                {if="$GLOBALS['config']['ARCHIVE_ORG']"}
+                <span class="linkarchive"><a href="https://web.archive.org/web/{$value.url|htmlspecialchars}">archive</a> - </span>
+                {/if}
                 <div style="position:relative;display:inline;"><a href="http://qrfree.kaywa.com/?l=1&s=8&d={$scripturl|urlencode}%3F{$value.linkdate|smallHash}" 
                     onclick="showQrCode(this); return false;" class="qrcode" data-permalink="{$scripturl}?{$value.linkdate|smallHash}"><img src="images/qrcode.png#" width="13" height="13" title="QR-Code"></a></div> - 
                 <span class="linkurl" title="Short link">{$value.url|htmlspecialchars}</span><br>
index 125b365b12e4bb9a7d0c56fe6535e98c5caa61b5..654a551d5887221442e8021c1285a9ed2d17efa1 100644 (file)
@@ -2,7 +2,7 @@
     <div id="logo" title="Share your links !" onclick="document.location='?';"></div>
     <div style="float:right; font-style:italic; color:#bbb; text-align:right; padding:0 5 0 0;" class="nomobile">Shaare your links...<br>
         {if="!empty($linkcount)"}{$linkcount} links{/if}</div>
-    <span id="shaarli_title"><a href="?">{$shaarlititle|htmlspecialchars}</a></span>
+    <span id="shaarli_title"><a href="{$titleLink}">{$shaarlititle|htmlspecialchars}</a></span>
   
 {if="!empty($_GET['source']) && $_GET['source']=='bookmarklet'"}
     {ignore} When called as a popup from bookmarklet, do not display menu. {/ignore}
@@ -16,7 +16,9 @@
         <a href="?do=login">Login</a>
     {/if}
     <a href="{$feedurl}?do=rss{$searchcrits}" class="nomobile">RSS Feed</a>
-    <a href="{$feedurl}?do=atom{$searchcrits}" style="padding-left:10px;" class="nomobile">ATOM Feed</a>
+    {if="$GLOBALS['config']['SHOW_ATOM']"}
+        <a href="{$feedurl}?do=atom{$searchcrits}" style="padding-left:10px;" class="nomobile">ATOM Feed</a>
+    {/if}
     <a href="?do=tagcloud">Tag cloud</a>
     <a href="?do=picwall{$searchcrits}">Picture wall</a>
     <a href="?do=daily">Daily</a>
index 6918c7be96d68421594325c6bbb3a2fc3e2c1d5f..0dd2c0d36f64d6afd1c02708f11a14bc4e3a1ccd 100644 (file)
@@ -6,7 +6,7 @@
 <center>
 <div id="cloudtag">
     {loop="tags"}
-    <span style="color:#99f; font-size:9pt; padding-left:5px; padding-right:2px;">{$value.count}</span><a href="?searchtags={$key|htmlspecialchars}" style="font-size:{$value.size}pt; font-weight:bold; color:black; text-decoration:none">{$key|htmlspecialchars}</a>
+    <span style="color:#99f; font-size:9pt; padding-left:5px; padding-right:2px;">{$value.count}</span><a href="?searchtags={$key|urlencode}" style="font-size:{$value.size}pt; font-weight:bold; color:black; text-decoration:none">{$key|htmlspecialchars}</a>
     {/loop}
 </div>
 </center>
index 48ecc97eb61bf00f7baeed9ba53fbe6e95fc1b87..ba1c1e8eccff8ffad08966751dfd45ad10539dcf 100644 (file)
@@ -10,7 +10,7 @@
            <a href="?do=changetag"><b>Rename/delete tags</b> <span>:  Rename or delete a tag in all links</span></a><br><br>
            <a href="?do=import"><b>Import</b> <span>:  Import Netscape html bookmarks (as exported from Firefox, Chrome, Opera, delicious...)</span></a> <br><br>
            <a href="?do=export"><b>Export</b> <span>:  Export Netscape html bookmarks (which can be imported in Firefox, Chrome, Opera, delicious...)</span></a><br><br>
-       <a class="smallbutton" onclick="alert('Drag this link to your bookmarks toolbar, or right-click it and choose Bookmark This Link...');return false;" href="javascript:javascript:(function(){var%20url%20=%20location.href;var%20title%20=%20document.title%20||%20url;window.open('{$pageabsaddr}?post='%20+%20encodeURIComponent(url)+'&amp;title='%20+%20encodeURIComponent(title)+'&amp;source=bookmarklet','_blank','menubar=no,height=390,width=600,toolbar=no,scrollbars=no,status=no,dialog=1');})();"><b>Shaare link</b></a> <a href="#" style="clear:none;"><span>&#x21D0; Drag this link to your bookmarks toolbar (or right-click it and choose Bookmark This Link....).<br>&nbsp;&nbsp;&nbsp;&nbsp;Then click "Shaare link" button in any page you want to share.</span></a><br><br>
+       <a class="smallbutton" onclick="alert('Drag this link to your bookmarks toolbar, or right-click it and choose Bookmark This Link...');return false;" href="javascript:javascript:(function(){var%20url%20=%20location.href;var%20title%20=%20document.title%20||%20url;window.open('{$pageabsaddr}?post='%20+%20encodeURIComponent(url)+'&amp;title='%20+%20encodeURIComponent(title)+'&amp;description='%20+%20encodeURIComponent(document.getSelection())+'&amp;source=bookmarklet','_blank','menubar=no,height=390,width=600,toolbar=no,scrollbars=no,status=no,dialog=1');})();"><b>Shaare link</b></a> <a href="#" style="clear:none;"><span>&#x21D0; Drag this link to your bookmarks toolbar (or right-click it and choose Bookmark This Link....).<br>&nbsp;&nbsp;&nbsp;&nbsp;Then click "Shaare link" button in any page you want to share.</span></a><br><br>
            <div class="clear"></div>
        </div>
 </div>