aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorArthurHoaro <arthur@hoa.ro>2015-07-15 11:47:12 +0200
committerArthurHoaro <arthur@hoa.ro>2015-11-07 15:27:22 +0100
commit567967fdf94b2b8ba2b2fc9c8836e89ac23c8c71 (patch)
tree6a8b3b2706b253a782a0b094e977524dccdf68da
parent0aec972a8bee1689ae79a436ae6e4236022b52ef (diff)
downloadShaarli-567967fdf94b2b8ba2b2fc9c8836e89ac23c8c71.tar.gz
Shaarli-567967fdf94b2b8ba2b2fc9c8836e89ac23c8c71.tar.zst
Shaarli-567967fdf94b2b8ba2b2fc9c8836e89ac23c8c71.zip
Template upgrade to handle plugin zones
Add a bunch of plugin placeholders in templates
-rw-r--r--COPYING14
-rw-r--r--application/PluginManager.php2
-rw-r--r--images/qrcode.pngbin321 -> 0 bytes
-rw-r--r--inc/qr-1.1.3.js1215
-rw-r--r--inc/qr-1.1.3.min.js5
-rw-r--r--inc/shaarli.css8
-rw-r--r--tpl/configure.html1
-rw-r--r--tpl/daily.html57
-rw-r--r--tpl/editlink.html5
-rw-r--r--tpl/includes.html3
-rw-r--r--tpl/linklist.html90
-rw-r--r--tpl/linklist.paging.html5
-rw-r--r--tpl/page.footer.html7
-rw-r--r--tpl/page.header.html5
-rw-r--r--tpl/picwall.html19
-rw-r--r--tpl/tagcloud.html18
-rw-r--r--tpl/tools.html3
17 files changed, 146 insertions, 1311 deletions
diff --git a/COPYING b/COPYING
index 24b97e94..1044a3b0 100644
--- a/COPYING
+++ b/COPYING
@@ -1,4 +1,4 @@
1Files: * 1Files: *
2License: zlib/libpng 2License: zlib/libpng
3Copyright: (c) 2011-2015 Sébastien SAUVAGE <sebsauvage@sebsauvage.net> 3Copyright: (c) 2011-2015 Sébastien SAUVAGE <sebsauvage@sebsauvage.net>
4 (c) 2011-2015 Alexandre Alapetite <alexandre@alapetite.fr> 4 (c) 2011-2015 Alexandre Alapetite <alexandre@alapetite.fr>
@@ -31,9 +31,9 @@ Copyright: (c) 2011-2015 Sébastien SAUVAGE <sebsauvage@sebsauvage.net>
31 31
32Files: inc/reset.css 32Files: inc/reset.css
33License: BSD (http://opensource.org/licenses/BSD-3-Clause) 33License: BSD (http://opensource.org/licenses/BSD-3-Clause)
34Copyright: (c) 2010, Yahoo! Inc. 34Copyright: (c) 2010, Yahoo! Inc.
35 35
36Files: 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 36Files: 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/tag_blue.png
37License: CC-BY (http://creativecommons.org/licenses/by/3.0/) 37License: CC-BY (http://creativecommons.org/licenses/by/3.0/)
38Copyright: (c) 2014 Yusuke Kamiyamane 38Copyright: (c) 2014 Yusuke Kamiyamane
39Source: http://p.yusukekamiyamane.com/ 39Source: http://p.yusukekamiyamane.com/
@@ -59,10 +59,6 @@ Files: inc/blazy*.js
59License: MIT License (http://opensource.org/licenses/MIT) 59License: MIT License (http://opensource.org/licenses/MIT)
60Copyright: (C) Bjoern Klinggaard - @bklinggaard - http://dinbror.dk/blazy 60Copyright: (C) Bjoern Klinggaard - @bklinggaard - http://dinbror.dk/blazy
61 61
62Files: inc/qr.js
63License: GPLv3 License (http://opensource.org/licenses/gpl-3.0)
64Copyright: (C) 2014 Alasdair Mercer, http://neocotic.com, https://github.com/neocotic/qr.js
65
66Files: inc/rain.tpl.class.php 62Files: inc/rain.tpl.class.php
67Copyright: 2011-2012, Federico Ulfo <rainelemental@gmail.com> 63Copyright: 2011-2012, Federico Ulfo <rainelemental@gmail.com>
68 2011-2012, The Rain Team <hello@raintm.com> 64 2011-2012, The Rain Team <hello@raintm.com>
@@ -80,10 +76,10 @@ In no event will the authors be held liable for any damages arising from
80the use of this software. 76the use of this software.
81 77
82Permission is granted to anyone to use this software for any purpose, 78Permission is granted to anyone to use this software for any purpose,
83including commercial applications, and to alter it and redistribute it 79including commercial applications, and to alter it and redistribute it
84freely, subject to the following restrictions: 80freely, subject to the following restrictions:
85 81
86 1. The origin of this software must not be misrepresented; you must not 82 1. The origin of this software must not be misrepresented; you must not
87 claim that you wrote the original software. If you use this software 83 claim that you wrote the original software. If you use this software
88 in a product, an acknowledgment in the product documentation would 84 in a product, an acknowledgment in the product documentation would
89 be appreciated but is not required. 85 be appreciated but is not required.
diff --git a/application/PluginManager.php b/application/PluginManager.php
index e572ff7c..803f11b4 100644
--- a/application/PluginManager.php
+++ b/application/PluginManager.php
@@ -99,7 +99,7 @@ class PluginManager
99 * @param string $hook name of the hook to trigger. 99 * @param string $hook name of the hook to trigger.
100 * @param array $data list of data to manipulate passed by reference. 100 * @param array $data list of data to manipulate passed by reference.
101 * @param array $params additional parameters such as page target. 101 * @param array $params additional parameters such as page target.
102 * 102 *
103 * @return void 103 * @return void
104 */ 104 */
105 public function executeHooks($hook, &$data, $params = array()) 105 public function executeHooks($hook, &$data, $params = array())
diff --git a/images/qrcode.png b/images/qrcode.png
deleted file mode 100644
index c2cfa476..00000000
--- a/images/qrcode.png
+++ /dev/null
Binary files differ
diff --git a/inc/qr-1.1.3.js b/inc/qr-1.1.3.js
deleted file mode 100644
index 4f127e6e..00000000
--- a/inc/qr-1.1.3.js
+++ /dev/null
@@ -1,1215 +0,0 @@
1// [qr.js](http://neocotic.com/qr.js)
2// (c) 2014 Alasdair Mercer
3// Licensed under the GPL Version 3 license.
4// Based on [jsqrencode](http://code.google.com/p/jsqrencode/)
5// (c) 2010 tz@execpc.com
6// Licensed under the GPL Version 3 license.
7// For all details and documentation:
8// <http://neocotic.com/qr.js>
9
10(function (root) {
11
12 'use strict';
13
14 // Private constants
15 // -----------------
16
17 // Alignment pattern.
18 var ALIGNMENT_DELTA = [
19 0, 11, 15, 19, 23, 27, 31,
20 16, 18, 20, 22, 24, 26, 28, 20, 22, 24, 24, 26, 28, 28, 22, 24, 24,
21 26, 26, 28, 28, 24, 24, 26, 26, 26, 28, 28, 24, 26, 26, 26, 28, 28
22 ];
23 // Default MIME type.
24 var DEFAULT_MIME = 'image/png';
25 // MIME used to initiate a browser download prompt when `qr.save` is called.
26 var DOWNLOAD_MIME = 'image/octet-stream';
27 // There are four elements per version. The first two indicate the number of blocks, then the
28 // data width, and finally the ECC width.
29 var ECC_BLOCKS = [
30 1, 0, 19, 7, 1, 0, 16, 10, 1, 0, 13, 13, 1, 0, 9, 17,
31 1, 0, 34, 10, 1, 0, 28, 16, 1, 0, 22, 22, 1, 0, 16, 28,
32 1, 0, 55, 15, 1, 0, 44, 26, 2, 0, 17, 18, 2, 0, 13, 22,
33 1, 0, 80, 20, 2, 0, 32, 18, 2, 0, 24, 26, 4, 0, 9, 16,
34 1, 0, 108, 26, 2, 0, 43, 24, 2, 2, 15, 18, 2, 2, 11, 22,
35 2, 0, 68, 18, 4, 0, 27, 16, 4, 0, 19, 24, 4, 0, 15, 28,
36 2, 0, 78, 20, 4, 0, 31, 18, 2, 4, 14, 18, 4, 1, 13, 26,
37 2, 0, 97, 24, 2, 2, 38, 22, 4, 2, 18, 22, 4, 2, 14, 26,
38 2, 0, 116, 30, 3, 2, 36, 22, 4, 4, 16, 20, 4, 4, 12, 24,
39 2, 2, 68, 18, 4, 1, 43, 26, 6, 2, 19, 24, 6, 2, 15, 28,
40 4, 0, 81, 20, 1, 4, 50, 30, 4, 4, 22, 28, 3, 8, 12, 24,
41 2, 2, 92, 24, 6, 2, 36, 22, 4, 6, 20, 26, 7, 4, 14, 28,
42 4, 0, 107, 26, 8, 1, 37, 22, 8, 4, 20, 24, 12, 4, 11, 22,
43 3, 1, 115, 30, 4, 5, 40, 24, 11, 5, 16, 20, 11, 5, 12, 24,
44 5, 1, 87, 22, 5, 5, 41, 24, 5, 7, 24, 30, 11, 7, 12, 24,
45 5, 1, 98, 24, 7, 3, 45, 28, 15, 2, 19, 24, 3, 13, 15, 30,
46 1, 5, 107, 28, 10, 1, 46, 28, 1, 15, 22, 28, 2, 17, 14, 28,
47 5, 1, 120, 30, 9, 4, 43, 26, 17, 1, 22, 28, 2, 19, 14, 28,
48 3, 4, 113, 28, 3, 11, 44, 26, 17, 4, 21, 26, 9, 16, 13, 26,
49 3, 5, 107, 28, 3, 13, 41, 26, 15, 5, 24, 30, 15, 10, 15, 28,
50 4, 4, 116, 28, 17, 0, 42, 26, 17, 6, 22, 28, 19, 6, 16, 30,
51 2, 7, 111, 28, 17, 0, 46, 28, 7, 16, 24, 30, 34, 0, 13, 24,
52 4, 5, 121, 30, 4, 14, 47, 28, 11, 14, 24, 30, 16, 14, 15, 30,
53 6, 4, 117, 30, 6, 14, 45, 28, 11, 16, 24, 30, 30, 2, 16, 30,
54 8, 4, 106, 26, 8, 13, 47, 28, 7, 22, 24, 30, 22, 13, 15, 30,
55 10, 2, 114, 28, 19, 4, 46, 28, 28, 6, 22, 28, 33, 4, 16, 30,
56 8, 4, 122, 30, 22, 3, 45, 28, 8, 26, 23, 30, 12, 28, 15, 30,
57 3, 10, 117, 30, 3, 23, 45, 28, 4, 31, 24, 30, 11, 31, 15, 30,
58 7, 7, 116, 30, 21, 7, 45, 28, 1, 37, 23, 30, 19, 26, 15, 30,
59 5, 10, 115, 30, 19, 10, 47, 28, 15, 25, 24, 30, 23, 25, 15, 30,
60 13, 3, 115, 30, 2, 29, 46, 28, 42, 1, 24, 30, 23, 28, 15, 30,
61 17, 0, 115, 30, 10, 23, 46, 28, 10, 35, 24, 30, 19, 35, 15, 30,
62 17, 1, 115, 30, 14, 21, 46, 28, 29, 19, 24, 30, 11, 46, 15, 30,
63 13, 6, 115, 30, 14, 23, 46, 28, 44, 7, 24, 30, 59, 1, 16, 30,
64 12, 7, 121, 30, 12, 26, 47, 28, 39, 14, 24, 30, 22, 41, 15, 30,
65 6, 14, 121, 30, 6, 34, 47, 28, 46, 10, 24, 30, 2, 64, 15, 30,
66 17, 4, 122, 30, 29, 14, 46, 28, 49, 10, 24, 30, 24, 46, 15, 30,
67 4, 18, 122, 30, 13, 32, 46, 28, 48, 14, 24, 30, 42, 32, 15, 30,
68 20, 4, 117, 30, 40, 7, 47, 28, 43, 22, 24, 30, 10, 67, 15, 30,
69 19, 6, 118, 30, 18, 31, 47, 28, 34, 34, 24, 30, 20, 61, 15, 30
70 ];
71 // Map of human-readable ECC levels.
72 var ECC_LEVELS = {
73 L: 1,
74 M: 2,
75 Q: 3,
76 H: 4
77 };
78 // Final format bits with mask (level << 3 | mask).
79 var FINAL_FORMAT = [
80 0x77c4, 0x72f3, 0x7daa, 0x789d, 0x662f, 0x6318, 0x6c41, 0x6976, /* L */
81 0x5412, 0x5125, 0x5e7c, 0x5b4b, 0x45f9, 0x40ce, 0x4f97, 0x4aa0, /* M */
82 0x355f, 0x3068, 0x3f31, 0x3a06, 0x24b4, 0x2183, 0x2eda, 0x2bed, /* Q */
83 0x1689, 0x13be, 0x1ce7, 0x19d0, 0x0762, 0x0255, 0x0d0c, 0x083b /* H */
84 ];
85 // Galois field exponent table.
86 var GALOIS_EXPONENT = [
87 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1d, 0x3a, 0x74, 0xe8, 0xcd, 0x87, 0x13, 0x26,
88 0x4c, 0x98, 0x2d, 0x5a, 0xb4, 0x75, 0xea, 0xc9, 0x8f, 0x03, 0x06, 0x0c, 0x18, 0x30, 0x60, 0xc0,
89 0x9d, 0x27, 0x4e, 0x9c, 0x25, 0x4a, 0x94, 0x35, 0x6a, 0xd4, 0xb5, 0x77, 0xee, 0xc1, 0x9f, 0x23,
90 0x46, 0x8c, 0x05, 0x0a, 0x14, 0x28, 0x50, 0xa0, 0x5d, 0xba, 0x69, 0xd2, 0xb9, 0x6f, 0xde, 0xa1,
91 0x5f, 0xbe, 0x61, 0xc2, 0x99, 0x2f, 0x5e, 0xbc, 0x65, 0xca, 0x89, 0x0f, 0x1e, 0x3c, 0x78, 0xf0,
92 0xfd, 0xe7, 0xd3, 0xbb, 0x6b, 0xd6, 0xb1, 0x7f, 0xfe, 0xe1, 0xdf, 0xa3, 0x5b, 0xb6, 0x71, 0xe2,
93 0xd9, 0xaf, 0x43, 0x86, 0x11, 0x22, 0x44, 0x88, 0x0d, 0x1a, 0x34, 0x68, 0xd0, 0xbd, 0x67, 0xce,
94 0x81, 0x1f, 0x3e, 0x7c, 0xf8, 0xed, 0xc7, 0x93, 0x3b, 0x76, 0xec, 0xc5, 0x97, 0x33, 0x66, 0xcc,
95 0x85, 0x17, 0x2e, 0x5c, 0xb8, 0x6d, 0xda, 0xa9, 0x4f, 0x9e, 0x21, 0x42, 0x84, 0x15, 0x2a, 0x54,
96 0xa8, 0x4d, 0x9a, 0x29, 0x52, 0xa4, 0x55, 0xaa, 0x49, 0x92, 0x39, 0x72, 0xe4, 0xd5, 0xb7, 0x73,
97 0xe6, 0xd1, 0xbf, 0x63, 0xc6, 0x91, 0x3f, 0x7e, 0xfc, 0xe5, 0xd7, 0xb3, 0x7b, 0xf6, 0xf1, 0xff,
98 0xe3, 0xdb, 0xab, 0x4b, 0x96, 0x31, 0x62, 0xc4, 0x95, 0x37, 0x6e, 0xdc, 0xa5, 0x57, 0xae, 0x41,
99 0x82, 0x19, 0x32, 0x64, 0xc8, 0x8d, 0x07, 0x0e, 0x1c, 0x38, 0x70, 0xe0, 0xdd, 0xa7, 0x53, 0xa6,
100 0x51, 0xa2, 0x59, 0xb2, 0x79, 0xf2, 0xf9, 0xef, 0xc3, 0x9b, 0x2b, 0x56, 0xac, 0x45, 0x8a, 0x09,
101 0x12, 0x24, 0x48, 0x90, 0x3d, 0x7a, 0xf4, 0xf5, 0xf7, 0xf3, 0xfb, 0xeb, 0xcb, 0x8b, 0x0b, 0x16,
102 0x2c, 0x58, 0xb0, 0x7d, 0xfa, 0xe9, 0xcf, 0x83, 0x1b, 0x36, 0x6c, 0xd8, 0xad, 0x47, 0x8e, 0x00
103 ];
104 // Galois field log table.
105 var GALOIS_LOG = [
106 0xff, 0x00, 0x01, 0x19, 0x02, 0x32, 0x1a, 0xc6, 0x03, 0xdf, 0x33, 0xee, 0x1b, 0x68, 0xc7, 0x4b,
107 0x04, 0x64, 0xe0, 0x0e, 0x34, 0x8d, 0xef, 0x81, 0x1c, 0xc1, 0x69, 0xf8, 0xc8, 0x08, 0x4c, 0x71,
108 0x05, 0x8a, 0x65, 0x2f, 0xe1, 0x24, 0x0f, 0x21, 0x35, 0x93, 0x8e, 0xda, 0xf0, 0x12, 0x82, 0x45,
109 0x1d, 0xb5, 0xc2, 0x7d, 0x6a, 0x27, 0xf9, 0xb9, 0xc9, 0x9a, 0x09, 0x78, 0x4d, 0xe4, 0x72, 0xa6,
110 0x06, 0xbf, 0x8b, 0x62, 0x66, 0xdd, 0x30, 0xfd, 0xe2, 0x98, 0x25, 0xb3, 0x10, 0x91, 0x22, 0x88,
111 0x36, 0xd0, 0x94, 0xce, 0x8f, 0x96, 0xdb, 0xbd, 0xf1, 0xd2, 0x13, 0x5c, 0x83, 0x38, 0x46, 0x40,
112 0x1e, 0x42, 0xb6, 0xa3, 0xc3, 0x48, 0x7e, 0x6e, 0x6b, 0x3a, 0x28, 0x54, 0xfa, 0x85, 0xba, 0x3d,
113 0xca, 0x5e, 0x9b, 0x9f, 0x0a, 0x15, 0x79, 0x2b, 0x4e, 0xd4, 0xe5, 0xac, 0x73, 0xf3, 0xa7, 0x57,
114 0x07, 0x70, 0xc0, 0xf7, 0x8c, 0x80, 0x63, 0x0d, 0x67, 0x4a, 0xde, 0xed, 0x31, 0xc5, 0xfe, 0x18,
115 0xe3, 0xa5, 0x99, 0x77, 0x26, 0xb8, 0xb4, 0x7c, 0x11, 0x44, 0x92, 0xd9, 0x23, 0x20, 0x89, 0x2e,
116 0x37, 0x3f, 0xd1, 0x5b, 0x95, 0xbc, 0xcf, 0xcd, 0x90, 0x87, 0x97, 0xb2, 0xdc, 0xfc, 0xbe, 0x61,
117 0xf2, 0x56, 0xd3, 0xab, 0x14, 0x2a, 0x5d, 0x9e, 0x84, 0x3c, 0x39, 0x53, 0x47, 0x6d, 0x41, 0xa2,
118 0x1f, 0x2d, 0x43, 0xd8, 0xb7, 0x7b, 0xa4, 0x76, 0xc4, 0x17, 0x49, 0xec, 0x7f, 0x0c, 0x6f, 0xf6,
119 0x6c, 0xa1, 0x3b, 0x52, 0x29, 0x9d, 0x55, 0xaa, 0xfb, 0x60, 0x86, 0xb1, 0xbb, 0xcc, 0x3e, 0x5a,
120 0xcb, 0x59, 0x5f, 0xb0, 0x9c, 0xa9, 0xa0, 0x51, 0x0b, 0xf5, 0x16, 0xeb, 0x7a, 0x75, 0x2c, 0xd7,
121 0x4f, 0xae, 0xd5, 0xe9, 0xe6, 0xe7, 0xad, 0xe8, 0x74, 0xd6, 0xf4, 0xea, 0xa8, 0x50, 0x58, 0xaf
122 ];
123 // *Badness* coefficients.
124 var N1 = 3;
125 var N2 = 3;
126 var N3 = 40;
127 var N4 = 10;
128 // Version pattern.
129 var VERSION_BLOCK = [
130 0xc94, 0x5bc, 0xa99, 0x4d3, 0xbf6, 0x762, 0x847, 0x60d, 0x928, 0xb78, 0x45d, 0xa17, 0x532,
131 0x9a6, 0x683, 0x8c9, 0x7ec, 0xec4, 0x1e1, 0xfab, 0x08e, 0xc1a, 0x33f, 0xd75, 0x250, 0x9d5,
132 0x6f0, 0x8ba, 0x79f, 0xb0b, 0x42e, 0xa64, 0x541, 0xc69
133 ];
134 // Mode for node.js file system file writes.
135 var WRITE_MODE = parseInt('0666', 8);
136
137 // Private variables
138 // -----------------
139
140 // Run lengths for badness.
141 var badBuffer = [];
142 // Constructor for `canvas` elements in the node.js environment.
143 var Canvas;
144 // Data block.
145 var dataBlock;
146 // ECC data blocks and tables.
147 var eccBlock, neccBlock1, neccBlock2;
148 // ECC buffer.
149 var eccBuffer = [];
150 // ECC level (defaults to **L**).
151 var eccLevel = 1;
152 // Image buffer.
153 var frameBuffer = [];
154 // Fixed part of the image.
155 var frameMask = [];
156 // File system within the node.js environment.
157 var fs;
158 // Constructor for `img` elements in the node.js environment.
159 var Image;
160 // Indicates whether or not this script is running in node.js.
161 var inNode = false;
162 // Generator polynomial.
163 var polynomial = [];
164 // Save the previous value of the `qr` variable.
165 var previousQr = root.qr;
166 // Data input buffer.
167 var stringBuffer = [];
168 // Version for the data.
169 var version;
170 // Data width is based on `version`.
171 var width;
172
173 // Private functions
174 // -----------------
175
176 // Create a new canvas using `document.createElement` unless script is running in node.js, in
177 // which case the `canvas` module is used.
178 function createCanvas() {
179 return inNode ? new Canvas() : root.document.createElement('canvas');
180 }
181
182 // Create a new image using `document.createElement` unless script is running in node.js, in
183 // which case the `canvas` module is used.
184 function createImage() {
185 return inNode ? new Image() : root.document.createElement('img');
186 }
187
188 // Force the canvas image to be downloaded in the browser.
189 // Optionally, a `callback` function can be specified which will be called upon completed. Since
190 // this is not an asynchronous operation, this is merely convenient and helps simplify the
191 // calling code.
192 function download(cvs, data, callback) {
193 var mime = data.mime || DEFAULT_MIME;
194
195 root.location.href = cvs.toDataURL(mime).replace(mime, DOWNLOAD_MIME);
196
197 if (typeof callback === 'function') callback();
198 }
199
200 // Normalize the `data` that is provided to the main API.
201 function normalizeData(data) {
202 if (typeof data === 'string') data = { value: data };
203 return data || {};
204 }
205
206 // Override the `qr` API methods that require HTML5 canvas support to throw a relevant error.
207 function overrideAPI(qr) {
208 var methods = [ 'canvas', 'image', 'save', 'saveSync', 'toDataURL' ];
209 var i;
210
211 function overrideMethod(name) {
212 qr[name] = function () {
213 throw new Error(name + ' requires HTML5 canvas element support');
214 };
215 }
216
217 for (i = 0; i < methods.length; i++) {
218 overrideMethod(methods[i]);
219 }
220 }
221
222 // Asynchronously write the data of the rendered canvas to a given file path.
223 function writeFile(cvs, data, callback) {
224 if (typeof data.path !== 'string') {
225 return callback(new TypeError('Invalid path type: ' + typeof data.path));
226 }
227
228 var fd, buff;
229
230 // Write the buffer to the open file stream once both prerequisites are met.
231 function writeBuffer() {
232 fs.write(fd, buff, 0, buff.length, 0, function (error) {
233 fs.close(fd);
234
235 callback(error);
236 });
237 }
238
239 // Create a buffer of the canvas' data.
240 cvs.toBuffer(function (error, _buff) {
241 if (error) return callback(error);
242
243 buff = _buff;
244 if (fd) {
245 writeBuffer();
246 }
247 });
248
249 // Open a stream for the file to be written.
250 fs.open(data.path, 'w', WRITE_MODE, function (error, _fd) {
251 if (error) return callback(error);
252
253 fd = _fd;
254 if (buff) {
255 writeBuffer();
256 }
257 });
258 }
259
260 // Write the data of the rendered canvas to a given file path.
261 function writeFileSync(cvs, data) {
262 if (typeof data.path !== 'string') {
263 throw new TypeError('Invalid path type: ' + typeof data.path);
264 }
265
266 var buff = cvs.toBuffer();
267 var fd = fs.openSync(data.path, 'w', WRITE_MODE);
268
269 try {
270 fs.writeSync(fd, buff, 0, buff.length, 0);
271 } finally {
272 fs.closeSync(fd);
273 }
274 }
275
276 // Set bit to indicate cell in frame is immutable (symmetric around diagonal).
277 function setMask(x, y) {
278 var bit;
279
280 if (x > y) {
281 bit = x;
282 x = y;
283 y = bit;
284 }
285
286 bit = y;
287 bit *= y;
288 bit += y;
289 bit >>= 1;
290 bit += x;
291
292 frameMask[bit] = 1;
293 }
294
295 // Enter alignment pattern. Foreground colour to frame, background to mask. Frame will be merged
296 // with mask later.
297 function addAlignment(x, y) {
298 var i;
299
300 frameBuffer[x + width * y] = 1;
301
302 for (i = -2; i < 2; i++) {
303 frameBuffer[(x + i) + width * (y - 2)] = 1;
304 frameBuffer[(x - 2) + width * (y + i + 1)] = 1;
305 frameBuffer[(x + 2) + width * (y + i)] = 1;
306 frameBuffer[(x + i + 1) + width * (y + 2)] = 1;
307 }
308
309 for (i = 0; i < 2; i++) {
310 setMask(x - 1, y + i);
311 setMask(x + 1, y - i);
312 setMask(x - i, y - 1);
313 setMask(x + i, y + 1);
314 }
315 }
316
317 // Exponentiation mod N.
318 function modN(x) {
319 while (x >= 255) {
320 x -= 255;
321 x = (x >> 8) + (x & 255);
322 }
323
324 return x;
325 }
326
327 // Calculate and append `ecc` data to the `data` block. If block is in the string buffer the
328 // indices to buffers are used.
329 function appendData(data, dataLength, ecc, eccLength) {
330 var bit, i, j;
331
332 for (i = 0; i < eccLength; i++) {
333 stringBuffer[ecc + i] = 0;
334 }
335
336 for (i = 0; i < dataLength; i++) {
337 bit = GALOIS_LOG[stringBuffer[data + i] ^ stringBuffer[ecc]];
338
339 if (bit !== 255) {
340 for (j = 1; j < eccLength; j++) {
341 stringBuffer[ecc + j - 1] = stringBuffer[ecc + j] ^
342 GALOIS_EXPONENT[modN(bit + polynomial[eccLength - j])];
343 }
344 } else {
345 for (j = ecc; j < ecc + eccLength; j++) {
346 stringBuffer[j] = stringBuffer[j + 1];
347 }
348 }
349
350 stringBuffer[ecc + eccLength - 1] = bit === 255 ? 0 :
351 GALOIS_EXPONENT[modN(bit + polynomial[0])];
352 }
353 }
354
355 // Check mask since symmetricals use half.
356 function isMasked(x, y) {
357 var bit;
358
359 if (x > y) {
360 bit = x;
361 x = y;
362 y = bit;
363 }
364
365 bit = y;
366 bit += y * y;
367 bit >>= 1;
368 bit += x;
369
370 return frameMask[bit] === 1;
371 }
372
373 // Apply the selected mask out of the 8 options.
374 function applyMask(mask) {
375 var x, y, r3x, r3y;
376
377 switch (mask) {
378 case 0:
379 for (y = 0; y < width; y++) {
380 for (x = 0; x < width; x++) {
381 if (!((x + y) & 1) && !isMasked(x, y)) {
382 frameBuffer[x + y * width] ^= 1;
383 }
384 }
385 }
386
387 break;
388 case 1:
389 for (y = 0; y < width; y++) {
390 for (x = 0; x < width; x++) {
391 if (!(y & 1) && !isMasked(x, y)) {
392 frameBuffer[x + y * width] ^= 1;
393 }
394 }
395 }
396
397 break;
398 case 2:
399 for (y = 0; y < width; y++) {
400 for (r3x = 0, x = 0; x < width; x++, r3x++) {
401 if (r3x === 3) r3x = 0;
402
403 if (!r3x && !isMasked(x, y)) {
404 frameBuffer[x + y * width] ^= 1;
405 }
406 }
407 }
408
409 break;
410 case 3:
411 for (r3y = 0, y = 0; y < width; y++, r3y++) {
412 if (r3y === 3) r3y = 0;
413
414 for (r3x = r3y, x = 0; x < width; x++, r3x++) {
415 if (r3x === 3) r3x = 0;
416
417 if (!r3x && !isMasked(x, y)) {
418 frameBuffer[x + y * width] ^= 1;
419 }
420 }
421 }
422
423 break;
424 case 4:
425 for (y = 0; y < width; y++) {
426 for (r3x = 0, r3y = ((y >> 1) & 1), x = 0; x < width; x++, r3x++) {
427 if (r3x === 3) {
428 r3x = 0;
429 r3y = !r3y;
430 }
431
432 if (!r3y && !isMasked(x, y)) {
433 frameBuffer[x + y * width] ^= 1;
434 }
435 }
436 }
437
438 break;
439 case 5:
440 for (r3y = 0, y = 0; y < width; y++, r3y++) {
441 if (r3y === 3) r3y = 0;
442
443 for (r3x = 0, x = 0; x < width; x++, r3x++) {
444 if (r3x === 3) r3x = 0;
445
446 if (!((x & y & 1) + !(!r3x | !r3y)) && !isMasked(x, y)) {
447 frameBuffer[x + y * width] ^= 1;
448 }
449 }
450 }
451
452 break;
453 case 6:
454 for (r3y = 0, y = 0; y < width; y++, r3y++) {
455 if (r3y === 3) r3y = 0;
456
457 for (r3x = 0, x = 0; x < width; x++, r3x++) {
458 if (r3x === 3) r3x = 0;
459
460 if (!(((x & y & 1) + (r3x && (r3x === r3y))) & 1) && !isMasked(x, y)) {
461 frameBuffer[x + y * width] ^= 1;
462 }
463 }
464 }
465
466 break;
467 case 7:
468 for (r3y = 0, y = 0; y < width; y++, r3y++) {
469 if (r3y === 3) r3y = 0;
470
471 for (r3x = 0, x = 0; x < width; x++, r3x++) {
472 if (r3x === 3) r3x = 0;
473
474 if (!(((r3x && (r3x === r3y)) + ((x + y) & 1)) & 1) && !isMasked(x, y)) {
475 frameBuffer[x + y * width] ^= 1;
476 }
477 }
478 }
479
480 break;
481 }
482 }
483
484 // Using the table for the length of each run, calculate the amount of bad image. Long runs or
485 // those that look like finders are called twice; once for X and Y.
486 function getBadRuns(length) {
487 var badRuns = 0;
488 var i;
489
490 for (i = 0; i <= length; i++) {
491 if (badBuffer[i] >= 5) {
492 badRuns += N1 + badBuffer[i] - 5;
493 }
494 }
495
496 // FBFFFBF as in finder.
497 for (i = 3; i < length - 1; i += 2) {
498 if (badBuffer[i - 2] === badBuffer[i + 2] &&
499 badBuffer[i + 2] === badBuffer[i - 1] &&
500 badBuffer[i - 1] === badBuffer[i + 1] &&
501 badBuffer[i - 1] * 3 === badBuffer[i] &&
502 // Background around the foreground pattern? Not part of the specs.
503 (badBuffer[i - 3] === 0 || i + 3 > length ||
504 badBuffer[i - 3] * 3 >= badBuffer[i] * 4 ||
505 badBuffer[i + 3] * 3 >= badBuffer[i] * 4)) {
506 badRuns += N3;
507 }
508 }
509
510 return badRuns;
511 }
512
513 // Calculate how bad the masked image is (e.g. blocks, imbalance, runs, or finders).
514 function checkBadness() {
515 var b, b1, bad, big, bw, count, h, x, y;
516 bad = bw = count = 0;
517
518 // Blocks of same colour.
519 for (y = 0; y < width - 1; y++) {
520 for (x = 0; x < width - 1; x++) {
521 // All foreground colour.
522 if ((frameBuffer[x + width * y] &&
523 frameBuffer[(x + 1) + width * y] &&
524 frameBuffer[x + width * (y + 1)] &&
525 frameBuffer[(x + 1) + width * (y + 1)]) ||
526 // All background colour.
527 !(frameBuffer[x + width * y] ||
528 frameBuffer[(x + 1) + width * y] ||
529 frameBuffer[x + width * (y + 1)] ||
530 frameBuffer[(x + 1) + width * (y + 1)])) {
531 bad += N2;
532 }
533 }
534 }
535
536 // X runs.
537 for (y = 0; y < width; y++) {
538 badBuffer[0] = 0;
539
540 for (h = b = x = 0; x < width; x++) {
541 if ((b1 = frameBuffer[x + width * y]) === b) {
542 badBuffer[h]++;
543 } else {
544 badBuffer[++h] = 1;
545 }
546
547 b = b1;
548 bw += b ? 1 : -1;
549 }
550
551 bad += getBadRuns(h);
552 }
553
554 if (bw < 0) bw = -bw;
555
556 big = bw;
557 big += big << 2;
558 big <<= 1;
559
560 while (big > width * width) {
561 big -= width * width;
562 count++;
563 }
564
565 bad += count * N4;
566
567 // Y runs.
568 for (x = 0; x < width; x++) {
569 badBuffer[0] = 0;
570
571 for (h = b = y = 0; y < width; y++) {
572 if ((b1 = frameBuffer[x + width * y]) === b) {
573 badBuffer[h]++;
574 } else {
575 badBuffer[++h] = 1;
576 }
577
578 b = b1;
579 }
580
581 bad += getBadRuns(h);
582 }
583
584 return bad;
585 }
586
587 // Generate the encoded QR image for the string provided.
588 function generateFrame(str) {
589 var i, j, k, m, t, v, x, y;
590
591 // Find the smallest version that fits the string.
592 t = str.length;
593
594 version = 0;
595
596 do {
597 version++;
598
599 k = (eccLevel - 1) * 4 + (version - 1) * 16;
600
601 neccBlock1 = ECC_BLOCKS[k++];
602 neccBlock2 = ECC_BLOCKS[k++];
603 dataBlock = ECC_BLOCKS[k++];
604 eccBlock = ECC_BLOCKS[k];
605
606 k = dataBlock * (neccBlock1 + neccBlock2) + neccBlock2 - 3 + (version <= 9);
607
608 if (t <= k) break;
609 } while (version < 40);
610
611 // FIXME: Ensure that it fits insted of being truncated.
612 width = 17 + 4 * version;
613
614 // Allocate, clear and setup data structures.
615 v = dataBlock + (dataBlock + eccBlock) * (neccBlock1 + neccBlock2) + neccBlock2;
616
617 for (t = 0; t < v; t++) {
618 eccBuffer[t] = 0;
619 }
620
621 stringBuffer = str.slice(0);
622
623 for (t = 0; t < width * width; t++) {
624 frameBuffer[t] = 0;
625 }
626
627 for (t = 0; t < (width * (width + 1) + 1) / 2; t++) {
628 frameMask[t] = 0;
629 }
630
631 // Insert finders: Foreground colour to frame and background to mask.
632 for (t = 0; t < 3; t++) {
633 k = y = 0;
634
635 if (t === 1) k = (width - 7);
636 if (t === 2) y = (width - 7);
637
638 frameBuffer[(y + 3) + width * (k + 3)] = 1;
639
640 for (x = 0; x < 6; x++) {
641 frameBuffer[(y + x) + width * k] = 1;
642 frameBuffer[y + width * (k + x + 1)] = 1;
643 frameBuffer[(y + 6) + width * (k + x)] = 1;
644 frameBuffer[(y + x + 1) + width * (k + 6)] = 1;
645 }
646
647 for (x = 1; x < 5; x++) {
648 setMask(y + x, k + 1);
649 setMask(y + 1, k + x + 1);
650 setMask(y + 5, k + x);
651 setMask(y + x + 1, k + 5);
652 }
653
654 for (x = 2; x < 4; x++) {
655 frameBuffer[(y + x) + width * (k + 2)] = 1;
656 frameBuffer[(y + 2) + width * (k + x + 1)] = 1;
657 frameBuffer[(y + 4) + width * (k + x)] = 1;
658 frameBuffer[(y + x + 1) + width * (k + 4)] = 1;
659 }
660 }
661
662 // Alignment blocks.
663 if (version > 1) {
664 t = ALIGNMENT_DELTA[version];
665 y = width - 7;
666
667 for (;;) {
668 x = width - 7;
669
670 while (x > t - 3) {
671 addAlignment(x, y);
672
673 if (x < t) break;
674
675 x -= t;
676 }
677
678 if (y <= t + 9) break;
679
680 y -= t;
681
682 addAlignment(6, y);
683 addAlignment(y, 6);
684 }
685 }
686
687 // Single foreground cell.
688 frameBuffer[8 + width * (width - 8)] = 1;
689
690 // Timing gap (mask only).
691 for (y = 0; y < 7; y++) {
692 setMask(7, y);
693 setMask(width - 8, y);
694 setMask(7, y + width - 7);
695 }
696
697 for (x = 0; x < 8; x++) {
698 setMask(x, 7);
699 setMask(x + width - 8, 7);
700 setMask(x, width - 8);
701 }
702
703 // Reserve mask, format area.
704 for (x = 0; x < 9; x++) {
705 setMask(x, 8);
706 }
707
708 for (x = 0; x < 8; x++) {
709 setMask(x + width - 8, 8);
710 setMask(8, x);
711 }
712
713 for (y = 0; y < 7; y++) {
714 setMask(8, y + width - 7);
715 }
716
717 // Timing row/column.
718 for (x = 0; x < width - 14; x++) {
719 if (x & 1) {
720 setMask(8 + x, 6);
721 setMask(6, 8 + x);
722 } else {
723 frameBuffer[(8 + x) + width * 6] = 1;
724 frameBuffer[6 + width * (8 + x)] = 1;
725 }
726 }
727
728 // Version block.
729 if (version > 6) {
730 t = VERSION_BLOCK[version - 7];
731 k = 17;
732
733 for (x = 0; x < 6; x++) {
734 for (y = 0; y < 3; y++, k--) {
735 if (1 & (k > 11 ? version >> (k - 12) : t >> k)) {
736 frameBuffer[(5 - x) + width * (2 - y + width - 11)] = 1;
737 frameBuffer[(2 - y + width - 11) + width * (5 - x)] = 1;
738 } else {
739 setMask(5 - x, 2 - y + width - 11);
740 setMask(2 - y + width - 11, 5 - x);
741 }
742 }
743 }
744 }
745
746 // Sync mask bits. Only set above for background cells, so now add the foreground.
747 for (y = 0; y < width; y++) {
748 for (x = 0; x <= y; x++) {
749 if (frameBuffer[x + width * y]) {
750 setMask(x, y);
751 }
752 }
753 }
754
755 // Convert string to bit stream. 8-bit data to QR-coded 8-bit data (numeric, alphanum, or kanji
756 // not supported).
757 v = stringBuffer.length;
758
759 // String to array.
760 for (i = 0; i < v; i++) {
761 eccBuffer[i] = stringBuffer.charCodeAt(i);
762 }
763
764 stringBuffer = eccBuffer.slice(0);
765
766 // Calculate max string length.
767 x = dataBlock * (neccBlock1 + neccBlock2) + neccBlock2;
768
769 if (v >= x - 2) {
770 v = x - 2;
771
772 if (version > 9) v--;
773 }
774
775 // Shift and re-pack to insert length prefix.
776 i = v;
777
778 if (version > 9) {
779 stringBuffer[i + 2] = 0;
780 stringBuffer[i + 3] = 0;
781
782 while (i--) {
783 t = stringBuffer[i];
784
785 stringBuffer[i + 3] |= 255 & (t << 4);
786 stringBuffer[i + 2] = t >> 4;
787 }
788
789 stringBuffer[2] |= 255 & (v << 4);
790 stringBuffer[1] = v >> 4;
791 stringBuffer[0] = 0x40 | (v >> 12);
792 } else {
793 stringBuffer[i + 1] = 0;
794 stringBuffer[i + 2] = 0;
795
796 while (i--) {
797 t = stringBuffer[i];
798
799 stringBuffer[i + 2] |= 255 & (t << 4);
800 stringBuffer[i + 1] = t >> 4;
801 }
802
803 stringBuffer[1] |= 255 & (v << 4);
804 stringBuffer[0] = 0x40 | (v >> 4);
805 }
806
807 // Fill to end with pad pattern.
808 i = v + 3 - (version < 10);
809
810 while (i < x) {
811 stringBuffer[i++] = 0xec;
812 stringBuffer[i++] = 0x11;
813 }
814
815 // Calculate generator polynomial.
816 polynomial[0] = 1;
817
818 for (i = 0; i < eccBlock; i++) {
819 polynomial[i + 1] = 1;
820
821 for (j = i; j > 0; j--) {
822 polynomial[j] = polynomial[j] ? polynomial[j - 1] ^
823 GALOIS_EXPONENT[modN(GALOIS_LOG[polynomial[j]] + i)] : polynomial[j - 1];
824 }
825
826 polynomial[0] = GALOIS_EXPONENT[modN(GALOIS_LOG[polynomial[0]] + i)];
827 }
828
829 // Use logs for generator polynomial to save calculation step.
830 for (i = 0; i <= eccBlock; i++) {
831 polynomial[i] = GALOIS_LOG[polynomial[i]];
832 }
833
834 // Append ECC to data buffer.
835 k = x;
836 y = 0;
837
838 for (i = 0; i < neccBlock1; i++) {
839 appendData(y, dataBlock, k, eccBlock);
840
841 y += dataBlock;
842 k += eccBlock;
843 }
844
845 for (i = 0; i < neccBlock2; i++) {
846 appendData(y, dataBlock + 1, k, eccBlock);
847
848 y += dataBlock + 1;
849 k += eccBlock;
850 }
851
852 // Interleave blocks.
853 y = 0;
854
855 for (i = 0; i < dataBlock; i++) {
856 for (j = 0; j < neccBlock1; j++) {
857 eccBuffer[y++] = stringBuffer[i + j * dataBlock];
858 }
859
860 for (j = 0; j < neccBlock2; j++) {
861 eccBuffer[y++] = stringBuffer[(neccBlock1 * dataBlock) + i + (j * (dataBlock + 1))];
862 }
863 }
864
865 for (j = 0; j < neccBlock2; j++) {
866 eccBuffer[y++] = stringBuffer[(neccBlock1 * dataBlock) + i + (j * (dataBlock + 1))];
867 }
868
869 for (i = 0; i < eccBlock; i++) {
870 for (j = 0; j < neccBlock1 + neccBlock2; j++) {
871 eccBuffer[y++] = stringBuffer[x + i + j * eccBlock];
872 }
873 }
874
875 stringBuffer = eccBuffer;
876
877 // Pack bits into frame avoiding masked area.
878 x = y = width - 1;
879 k = v = 1;
880
881 // inteleaved data and ECC codes.
882 m = (dataBlock + eccBlock) * (neccBlock1 + neccBlock2) + neccBlock2;
883
884 for (i = 0; i < m; i++) {
885 t = stringBuffer[i];
886
887 for (j = 0; j < 8; j++, t <<= 1) {
888 if (0x80 & t) {
889 frameBuffer[x + width * y] = 1;
890 }
891
892 // Find next fill position.
893 do {
894 if (v) {
895 x--;
896 } else {
897 x++;
898
899 if (k) {
900 if (y !== 0) {
901 y--;
902 } else {
903 x -= 2;
904 k = !k;
905
906 if (x === 6) {
907 x--;
908 y = 9;
909 }
910 }
911 } else {
912 if (y !== width - 1) {
913 y++;
914 } else {
915 x -= 2;
916 k = !k;
917
918 if (x === 6) {
919 x--;
920 y -= 8;
921 }
922 }
923 }
924 }
925
926 v = !v;
927 } while (isMasked(x, y));
928 }
929 }
930
931 // Save pre-mask copy of frame.
932 stringBuffer = frameBuffer.slice(0);
933
934 t = 0;
935 y = 30000;
936
937 // Using `for` instead of `while` since in original Arduino code if an early mask was *good
938 // enough* it wouldn't try for a better one since they get more complex and take longer.
939 for (k = 0; k < 8; k++) {
940 // Returns foreground-background imbalance.
941 applyMask(k);
942
943 x = checkBadness();
944
945 // Is current mask better than previous best?
946 if (x < y) {
947 y = x;
948 t = k;
949 }
950
951 // Don't increment `i` to a void redoing mask.
952 if (t === 7) break;
953
954 // Reset for next pass.
955 frameBuffer = stringBuffer.slice(0);
956 }
957
958 // Redo best mask as none were *good enough* (i.e. last wasn't `t`).
959 if (t !== k) {
960 applyMask(t);
961 }
962
963 // Add in final mask/ECC level bytes.
964 y = FINAL_FORMAT[t + ((eccLevel - 1) << 3)];
965
966 // Low byte.
967 for (k = 0; k < 8; k++, y >>= 1) {
968 if (y & 1) {
969 frameBuffer[(width - 1 - k) + width * 8] = 1;
970
971 if (k < 6) {
972 frameBuffer[8 + width * k] = 1;
973 } else {
974 frameBuffer[8 + width * (k + 1)] = 1;
975 }
976 }
977 }
978
979 // High byte.
980 for (k = 0; k < 7; k++, y >>= 1) {
981 if (y & 1) {
982 frameBuffer[8 + width * (width - 7 + k)] = 1;
983
984 if (k) {
985 frameBuffer[(6 - k) + width * 8] = 1;
986 } else {
987 frameBuffer[7 + width * 8] = 1;
988 }
989 }
990 }
991
992 // Finally, return the image data.
993 return frameBuffer;
994 }
995
996 // qr.js setup
997 // -----------
998
999 // Build the publicly exposed API.
1000 var qr = {
1001
1002 // Constants
1003 // ---------
1004
1005 // Current version of `qr`.
1006 VERSION: '1.1.3',
1007
1008 // QR functions
1009 // ------------
1010
1011 // Generate the QR code using the data provided and render it on to a `<canvas>` element.
1012 // If no `<canvas>` element is specified in the argument provided a new one will be created and
1013 // used.
1014 // ECC (error correction capacity) determines how many intential errors are contained in the QR
1015 // code.
1016 canvas: function(data) {
1017 data = normalizeData(data);
1018
1019 // Module size of the generated QR code (i.e. 1-10).
1020 var size = data.size >= 1 && data.size <= 10 ? data.size : 4;
1021 // Actual size of the QR code symbol and is scaled to 25 pixels (e.g. 1 = 25px, 3 = 75px).
1022 size *= 25;
1023
1024 // `<canvas>` element used to render the QR code.
1025 var cvs = data.canvas || createCanvas();
1026 // Retreive the 2D context of the canvas.
1027 var c2d = cvs.getContext('2d');
1028 // Ensure the canvas has the correct dimensions.
1029 c2d.canvas.width = size;
1030 c2d.canvas.height = size;
1031 // Fill the canvas with the correct background colour.
1032 c2d.fillStyle = data.background || '#fff';
1033 c2d.fillRect(0, 0, size, size);
1034
1035 // Determine the ECC level to be applied.
1036 eccLevel = ECC_LEVELS[(data.level && data.level.toUpperCase()) || 'L'];
1037
1038 // Generate the image frame for the given `value`.
1039 var frame = generateFrame(data.value || '');
1040
1041 c2d.lineWidth = 1;
1042
1043 // Determine the *pixel* size.
1044 var px = size;
1045 px /= width;
1046 px = Math.floor(px);
1047
1048 // Draw the QR code.
1049 c2d.clearRect(0, 0, size, size);
1050 c2d.fillStyle = data.background || '#fff';
1051 c2d.fillRect(0, 0, px * (width + 8), px * (width + 8));
1052 c2d.fillStyle = data.foreground || '#000';
1053
1054 var i, j;
1055
1056 for (i = 0; i < width; i++) {
1057 for (j = 0; j < width; j++) {
1058 if (frame[j * width + i]) {
1059 c2d.fillRect(px * i, px * j, px, px);
1060 }
1061 }
1062 }
1063
1064 return cvs;
1065 },
1066
1067 // Generate the QR code using the data provided and render it on to a `<img>` element.
1068 // If no `<img>` element is specified in the argument provided a new one will be created and
1069 // used.
1070 // ECC (error correction capacity) determines how many intential errors are contained in the QR
1071 // code.
1072 image: function(data) {
1073 data = normalizeData(data);
1074
1075 // `<canvas>` element only which the QR code is rendered.
1076 var cvs = this.canvas(data);
1077 // `<img>` element used to display the QR code.
1078 var img = data.image || createImage();
1079
1080 // Apply the QR code to `img`.
1081 img.src = cvs.toDataURL(data.mime || DEFAULT_MIME);
1082 img.height = cvs.height;
1083 img.width = cvs.width;
1084
1085 return img;
1086 },
1087
1088 // Generate the QR code using the data provided and render it on to a `<canvas>` element and
1089 // save it as an image file.
1090 // If no `<canvas>` element is specified in the argument provided a new one will be created and
1091 // used.
1092 // ECC (error correction capacity) determines how many intential errors are contained in the QR
1093 // code.
1094 // If called in a browser the `path` property/argument is ignored and will simply prompt the
1095 // user to choose a location and file name. However, if called within node.js the file will be
1096 // saved to specified path.
1097 // A `callback` function must be provided which will be called once the saving process has
1098 // started. If an error occurs it will be passed as the first argument to this function,
1099 // otherwise this argument will be `null`.
1100 save: function(data, path, callback) {
1101 data = normalizeData(data);
1102
1103 switch (typeof path) {
1104 case 'function':
1105 callback = path;
1106 path = null;
1107 break;
1108 case 'string':
1109 data.path = path;
1110 break;
1111 }
1112
1113 // Callback function is required.
1114 if (typeof callback !== 'function') {
1115 throw new TypeError('Invalid callback type: ' + typeof callback);
1116 }
1117
1118 var completed = false;
1119 // `<canvas>` element only which the QR code is rendered.
1120 var cvs = this.canvas(data);
1121
1122 // Simple function to try and ensure that the `callback` function is only called once.
1123 function done(error) {
1124 if (!completed) {
1125 completed = true;
1126
1127 callback(error);
1128 }
1129 }
1130
1131 if (inNode) {
1132 writeFile(cvs, data, done);
1133 } else {
1134 download(cvs, data, done);
1135 }
1136 },
1137
1138 // Generate the QR code using the data provided and render it on to a `<canvas>` element and
1139 // save it as an image file.
1140 // If no `<canvas>` element is specified in the argument provided a new one will be created and
1141 // used.
1142 // ECC (error correction capacity) determines how many intential errors are contained in the QR
1143 // code.
1144 // If called in a browser the `path` property/argument is ignored and will simply prompt the
1145 // user to choose a location and file name. However, if called within node.js the file will be
1146 // saved to specified path.
1147 saveSync: function(data, path) {
1148 data = normalizeData(data);
1149
1150 if (typeof path === 'string') data.path = path;
1151
1152 // `<canvas>` element only which the QR code is rendered.
1153 var cvs = this.canvas(data);
1154
1155 if (inNode) {
1156 writeFileSync(cvs, data);
1157 } else {
1158 download(cvs, data);
1159 }
1160 },
1161
1162 // Generate the QR code using the data provided and render it on to a `<canvas>` element before
1163 // returning its data URI.
1164 // If no `<canvas>` element is specified in the argument provided a new one will be created and
1165 // used.
1166 // ECC (error correction capacity) determines how many intential errors are contained in the QR
1167 // code.
1168 toDataURL: function(data) {
1169 data = normalizeData(data);
1170
1171 return this.canvas(data).toDataURL(data.mime || DEFAULT_MIME);
1172 },
1173
1174 // Utility functions
1175 // -----------------
1176
1177 // Run qr.js in *noConflict* mode, returning the `qr` variable to its previous owner.
1178 // Returns a reference to `qr`.
1179 noConflict: function() {
1180 root.qr = previousQr;
1181 return this;
1182 }
1183
1184 };
1185
1186 // Support
1187 // -------
1188
1189 // Export `qr` for node.js and CommonJS.
1190 if (typeof exports !== 'undefined') {
1191 inNode = true;
1192
1193 if (typeof module !== 'undefined' && module.exports) {
1194 exports = module.exports = qr;
1195 }
1196 exports.qr = qr;
1197
1198 // Import required node.js modules.
1199 Canvas = require('canvas');
1200 Image = Canvas.Image;
1201 fs = require('fs');
1202 } else if (typeof define === 'function' && define.amd) {
1203 define(function () {
1204 return qr;
1205 });
1206 } else {
1207 // In non-HTML5 browser so strip base functionality.
1208 if (!root.HTMLCanvasElement) {
1209 overrideAPI(qr);
1210 }
1211
1212 root.qr = qr;
1213 }
1214
1215})(this);
diff --git a/inc/qr-1.1.3.min.js b/inc/qr-1.1.3.min.js
deleted file mode 100644
index 19d704e1..00000000
--- a/inc/qr-1.1.3.min.js
+++ /dev/null
@@ -1,5 +0,0 @@
1/*! qr-js v1.1.3 | (c) 2014 Alasdair Mercer | GPL v3 License
2jsqrencode | (c) 2010 tz@execpc.com | GPL v3 License
3*/
4!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);
5//# sourceMappingURL=qr.min.map \ No newline at end of file
diff --git a/inc/shaarli.css b/inc/shaarli.css
index 78bcfd34..d1193488 100644
--- a/inc/shaarli.css
+++ b/inc/shaarli.css
@@ -405,12 +405,12 @@ h1 {
405} 405}
406*/ 406*/
407 407
408.linkdate, .linkarchive { 408.linkdate {
409 font-size:8pt; 409 font-size:8pt;
410 color:#888; 410 color:#888;
411} 411}
412 412
413.linkdate a, .linkarchive a { 413.linkdate a {
414 color:#E28E3F; 414 color:#E28E3F;
415} 415}
416 416
@@ -451,12 +451,12 @@ a.qrcode img {
451 color: #F57900; 451 color: #F57900;
452} 452}
453 453
454.linkdate, .linkarchive { 454.linkdate {
455 font-size: 8pt; 455 font-size: 8pt;
456 color: #888; 456 color: #888;
457} 457}
458 458
459.linkdate a, .linkarchive a { 459.linkdate a {
460 background-image: url('../images/calendar.png'); 460 background-image: url('../images/calendar.png');
461 padding: 2px 0 3px 20px; 461 padding: 2px 0 3px 20px;
462 background-repeat: no-repeat; 462 background-repeat: no-repeat;
diff --git a/tpl/configure.html b/tpl/configure.html
index e4bd0760..9c725a51 100644
--- a/tpl/configure.html
+++ b/tpl/configure.html
@@ -38,6 +38,7 @@
38 <input type="checkbox" name="updateCheck" id="updateCheck" {if="!empty($GLOBALS['config']['ENABLE_UPDATECHECK'])"}checked{/if}/> 38 <input type="checkbox" name="updateCheck" id="updateCheck" {if="!empty($GLOBALS['config']['ENABLE_UPDATECHECK'])"}checked{/if}/>
39 <label for="updateCheck">&nbsp;Notify me when a new release is ready</label></td> 39 <label for="updateCheck">&nbsp;Notify me when a new release is ready</label></td>
40 </tr> 40 </tr>
41
41 <tr><td></td><td class="right"><input type="submit" name="Save" value="Save config" class="bigbutton"></td></tr> 42 <tr><td></td><td class="right"><input type="submit" name="Save" value="Save config" class="bigbutton"></td></tr>
42 </table> 43 </table>
43 </form> 44 </form>
diff --git a/tpl/daily.html b/tpl/daily.html
index 38aa4012..93a3ab45 100644
--- a/tpl/daily.html
+++ b/tpl/daily.html
@@ -2,18 +2,43 @@
2<html> 2<html>
3<head>{include="includes"}</head> 3<head>{include="includes"}</head>
4<body> 4<body>
5<div id="pageheader">{include="page.header"}</div> 5<div id="pageheader">
6 {include="page.header"}
7</div>
6<div class="daily"> 8<div class="daily">
9 <div id="plugin_zone_start_picwall" class="plugin_zone">
10 {loop="$plugin_start_zone"}
11 {$value}
12 {/loop}
13 </div>
14
7 <div class="dailyAbout"> 15 <div class="dailyAbout">
8 All links of one day<br>in a single page.<br> 16 All links of one day<br>in a single page.<br>
9 {if="$previousday"} <a href="?do=daily&amp;day={$previousday}"><b>&lt;</b>Previous day</a>{else}<b>&lt;</b>Previous day{/if} 17 {if="$previousday"} <a href="?do=daily&amp;day={$previousday}"><b>&lt;</b>Previous day</a>{else}<b>&lt;</b>Previous day{/if}
10 - 18 -
11 {if="$nextday"}<a href="?do=daily&amp;day={$nextday}">Next day<b>&gt;</b></a>{else}Next day<b>&gt;</b>{/if} 19 {if="$nextday"}<a href="?do=daily&amp;day={$nextday}">Next day<b>&gt;</b></a>{else}Next day<b>&gt;</b>{/if}
12 <br><br> 20 <br>
13 <a href="?do=dailyrss" title="1 RSS entry per day"><img src="images/feed-icon-14x14.png#" alt="rss_feed">Daily RSS Feed</a> 21
22 {loop="$daily_about_plugin"}
23 {$value}
24 {/loop}
25
26 <br>
27 <a href="?do=dailyrss" title="1 RSS entry per day"><img src="images/feed-icon-14x14.png#" alt="rss_feed">Daily RSS Feed</a>
14 </div> 28 </div>
15 <div class="dailyTitle"><img src="../images/floral_left.png" width="51" height="50" class="nomobile" alt="floral_left"> The Daily Shaarli <img src="../images/floral_right.png" width="51" height="50" class="nomobile" alt="floral_right"></div> 29
16 <div class="dailyDate"><span class="nomobile">&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;</span> {function="strftime('%A %d, %B %Y', $day)"} <span class="nomobile">&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;</span></div> 30 <div class="dailyTitle">
31 <img src="../images/floral_left.png" width="51" height="50" class="nomobile" alt="floral_left">
32 The Daily Shaarli
33 <img src="../images/floral_right.png" width="51" height="50" class="nomobile" alt="floral_right">
34 </div>
35
36 <div class="dailyDate">
37 <span class="nomobile">&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;</span>
38 {function="strftime('%A %d, %B %Y', $day)"}
39 <span class="nomobile">&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;</span>
40 </div>
41
17 <div class="clear"></div> 42 <div class="clear"></div>
18 43
19 {if="$linksToDisplay"} 44 {if="$linksToDisplay"}
@@ -47,6 +72,12 @@
47 <div class="dailyEntryThumbnail">{$link.thumbnail}</div> 72 <div class="dailyEntryThumbnail">{$link.thumbnail}</div>
48 {/if} 73 {/if}
49 <div class="dailyEntryDescription">{$link.formatedDescription}</div> 74 <div class="dailyEntryDescription">{$link.formatedDescription}</div>
75
76 <div class="dailyEntryFooter">
77 {loop="$link.link_plugin"}
78 {$value}
79 {/loop}
80 </div>
50 </div> 81 </div>
51 {/loop} 82 {/loop}
52 </div> 83 </div>
@@ -55,6 +86,14 @@
55 {else} 86 {else}
56 <div class="dailyNoEntry">No articles on this day.</div> 87 <div class="dailyNoEntry">No articles on this day.</div>
57 {/if} 88 {/if}
89
90 <div class="clear"></div>
91
92 <div id="plugin_zone_end_picwall" class="plugin_zone">
93 {loop="$plugin_end_zone"}
94 {$value}
95 {/loop}
96 </div>
58 <div id="closing"><img src="../images/squiggle_closing.png" width="66" height="61" alt="-"></div> 97 <div id="closing"><img src="../images/squiggle_closing.png" width="66" height="61" alt="-"></div>
59</div> 98</div>
60{include="page.footer"} 99{include="page.footer"}
diff --git a/tpl/editlink.html b/tpl/editlink.html
index 3733ca21..889d913d 100644
--- a/tpl/editlink.html
+++ b/tpl/editlink.html
@@ -21,6 +21,11 @@
21 <label for="lf_tags"><i>Tags</i></label><br> 21 <label for="lf_tags"><i>Tags</i></label><br>
22 <input type="text" name="lf_tags" id="lf_tags" value="{$link.tags}" class="lf_input" 22 <input type="text" name="lf_tags" id="lf_tags" value="{$link.tags}" class="lf_input"
23 data-list="{loop="$tags"}{$key}, {/loop}" data-multiple autocomplete="off" ><br> 23 data-list="{loop="$tags"}{$key}, {/loop}" data-multiple autocomplete="off" ><br>
24
25 {loop="$edit_link_plugin"}
26 {$value}
27 {/loop}
28
24 {if="($link_is_new && $GLOBALS['privateLinkByDefault']==true) || $link.private == true"} 29 {if="($link_is_new && $GLOBALS['privateLinkByDefault']==true) || $link.private == true"}
25 <input type="checkbox" checked="checked" name="lf_private" id="lf_private"> 30 <input type="checkbox" checked="checked" name="lf_private" id="lf_private">
26 &nbsp;<label for="lf_private"><i>Private</i></label><br> 31 &nbsp;<label for="lf_private"><i>Private</i></label><br>
diff --git a/tpl/includes.html b/tpl/includes.html
index 623e19ed..bdf3a07d 100644
--- a/tpl/includes.html
+++ b/tpl/includes.html
@@ -8,3 +8,6 @@
8<link type="text/css" rel="stylesheet" href="../inc/reset.css" /> 8<link type="text/css" rel="stylesheet" href="../inc/reset.css" />
9<link type="text/css" rel="stylesheet" href="../inc/shaarli.css" /> 9<link type="text/css" rel="stylesheet" href="../inc/shaarli.css" />
10{if="is_file('inc/user.css')"}<link type="text/css" rel="stylesheet" href="../inc/user.css" />{/if} 10{if="is_file('inc/user.css')"}<link type="text/css" rel="stylesheet" href="../inc/user.css" />{/if}
11{loop="$plugins_includes.css_files"}
12<link type="text/css" rel="stylesheet" href="{$value}#"/>
13{/loop} \ No newline at end of file
diff --git a/tpl/linklist.html b/tpl/linklist.html
index daf87060..9ed28853 100644
--- a/tpl/linklist.html
+++ b/tpl/linklist.html
@@ -17,6 +17,9 @@
17 </datalist> 17 </datalist>
18 <input type="submit" value="Search" class="bigbutton"> 18 <input type="submit" value="Search" class="bigbutton">
19 </form> 19 </form>
20 {loop="$plugins_header.fields_toolbar"}
21 {$value}
22 {/loop}
20 </div> 23 </div>
21</div> 24</div>
22 25
@@ -24,6 +27,12 @@
24 27
25 {include="linklist.paging"} 28 {include="linklist.paging"}
26 29
30 <div id="plugin_zone_start_linklist" class="plugin_zone">
31 {loop="$plugin_start_zone"}
32 {$value}
33 {/loop}
34 </div>
35
27 {if="count($links)==0"} 36 {if="count($links)==0"}
28 <div id="searchcriteria">Nothing found.</i></div> 37 <div id="searchcriteria">Nothing found.</i></div>
29 {else} 38 {else}
@@ -40,7 +49,7 @@
40 <ul> 49 <ul>
41 {loop="links"} 50 {loop="links"}
42 <li{if="$value.class"} class="{$value.class}"{/if}> 51 <li{if="$value.class"} class="{$value.class}"{/if}>
43 <a id="{$value.linkdate|smallHash}"></a> 52 <a id="{$value.shorturl}"></a>
44 <div class="thumbnail">{$value.url|thumbnail}</div> 53 <div class="thumbnail">{$value.url|thumbnail}</div>
45 <div class="linkcontainer"> 54 <div class="linkcontainer">
46 {if="isLoggedIn()"} 55 {if="isLoggedIn()"}
@@ -56,89 +65,38 @@
56 {if="!$GLOBALS['config']['HIDE_TIMESTAMPS'] || isLoggedIn()"} 65 {if="!$GLOBALS['config']['HIDE_TIMESTAMPS'] || isLoggedIn()"}
57 <span class="linkdate" title="Permalink"><a href="?{$value.linkdate|smallHash}">{function="strftime('%c', $value.timestamp)"} - permalink</a> - </span> 66 <span class="linkdate" title="Permalink"><a href="?{$value.linkdate|smallHash}">{function="strftime('%c', $value.timestamp)"} - permalink</a> - </span>
58 {else} 67 {else}
59 <span class="linkdate" title="Short link here"><a href="?{$value.linkdate|smallHash}">permalink</a> - </span> 68 <span class="linkdate" title="Short link here"><a href="?{$value.shorturl}">permalink</a> - </span>
60 {/if} 69 {/if}
61 {if="$GLOBALS['config']['ARCHIVE_ORG']"} 70
62 <span class="linkarchive"><a href="https://web.archive.org/web/{$value.url}">archive</a> - </span> 71 {loop="$value.link_plugin"}
63 {/if} 72 <span>{$value}</span> -
64 <div class="linkqrcode"><a href="http://qrfree.kaywa.com/?l=1&amp;s=8&amp;d={$scripturl|urlencode}%3F{$value.linkdate|smallHash}" 73 {/loop}
65 onclick="return showQrCode(this);" class="qrcode" data-permalink="{$scripturl}?{$value.linkdate|smallHash}"> 74
66 <img src="images/qrcode.png#" alt="QR-Code" title="{function="strftime('%c', $value.timestamp)"}"></a></div> -
67 <a href="{$value.url}"><span class="linkurl" title="Short link">{$value.url}</span></a><br> 75 <a href="{$value.url}"><span class="linkurl" title="Short link">{$value.url}</span></a><br>
68 {if="$value.tags"} 76 {if="$value.tags"}
69 <div class="linktaglist"> 77 <div class="linktaglist">
70 {loop="value.taglist"}<span class="linktag" title="Add tag"><a href="?addtag={$value|urlencode}">{$value}</a></span> {/loop} 78 {loop="value.taglist"}<span class="linktag" title="Add tag"><a href="?addtag={$value|urlencode}">{$value}</a></span> {/loop}
71 </div> 79 </div>
72 {/if} 80 {/if}
81
82
73 </div> 83 </div>
74 </li> 84 </li>
75 {/loop} 85 {/loop}
76 </ul> 86 </ul>
77 87
88 <div id="plugin_zone_end_linklist" class="plugin_zone">
89 {loop="$plugin_end_zone"}
90 {$value}
91 {/loop}
92 </div>
93
78 {include="linklist.paging"} 94 {include="linklist.paging"}
79 95
80</div> 96</div>
81 97
82 {include="page.footer"} 98 {include="page.footer"}
83 99
84<script>
85// Remove any displayed QR-Code
86function remove_qrcode()
87{
88 var elem = document.getElementById("permalinkQrcode");
89 if (elem) elem.parentNode.removeChild(elem);
90 return false;
91}
92
93function isCanvasSupported(){
94 var elem = document.createElement('canvas');
95 return !!(elem.getContext && elem.getContext('2d'));
96}
97
98// Show the QR-Code of a permalink (when the QR-Code icon is clicked).
99function showQrCode(caller,loading)
100{
101 if( !isCanvasSupported() ) return true;
102
103 // Dynamic javascript lib loading: We only load qr.js if the QR code icon is clicked:
104 if (typeof(qr)=='undefined') // Load qr.js only if not present.
105 {
106 loading = typeof loading !== 'undefined' ? loading : false;
107 if (!loading) // If javascript lib is still loading, do not append script to body.
108 {
109 var element = document.createElement("script");
110 element.src = "inc/qr-1.1.3.min.js";
111 document.body.appendChild(element);
112 }
113 setTimeout(function() { showQrCode(caller,true);}, 200); // Retry in 200 milliseconds.
114 return false;
115 }
116
117 // Remove previous qrcode if present.
118 remove_qrcode();
119
120 // Build the div which contains the QR-Code:
121 var element = document.createElement('div');
122 element.id="permalinkQrcode";
123
124 // Make QR-Code div commit sepuku when clicked:
125 element.addEventListener('click', remove_qrcode ); // Works on every canvas supported browser
126
127 // Build the QR-Code:
128 var image = qr.image({size: 8,value: caller.getAttribute('data-permalink')});
129 if (image)
130 {
131 element.appendChild(image);
132 element.innerHTML+= "<br>Click to close";
133 caller.parentNode.appendChild(element);
134 }
135 else
136 {
137 element.innerHTML="Your browser does not seem to be HTML5 compatible.";
138 }
139 return false;
140}
141</script>
142<script src="inc/awesomplete.min.js#"></script> 100<script src="inc/awesomplete.min.js#"></script>
143</body> 101</body>
144</html> 102</html>
diff --git a/tpl/linklist.paging.html b/tpl/linklist.paging.html
index 848541cd..e91c8f86 100644
--- a/tpl/linklist.paging.html
+++ b/tpl/linklist.paging.html
@@ -8,8 +8,13 @@
8 <img src="images/private_16x16.png#" width="16" height="16" title="Click to see only private links" alt="Click to see only private links"> 8 <img src="images/private_16x16.png#" width="16" height="16" title="Click to see only private links" alt="Click to see only private links">
9 {/if} 9 {/if}
10 </a> 10 </a>
11
12
11 </div> 13 </div>
12{/if} 14{/if}
15 {loop="$action_plugin"}
16 {$value}
17 {/loop}
13 <div class="paging_linksperpage"> 18 <div class="paging_linksperpage">
14 Links per page: <a href="?linksperpage=20">20</a> <a href="?linksperpage=50">50</a> <a href="?linksperpage=100">100</a> 19 Links per page: <a href="?linksperpage=20">20</a> <a href="?linksperpage=50">50</a> <a href="?linksperpage=100">100</a>
15 <form method="GET" class="linksperpage"><input type="text" name="linksperpage" size="2"></form> 20 <form method="GET" class="linksperpage"><input type="text" name="linksperpage" size="2"></form>
diff --git a/tpl/page.footer.html b/tpl/page.footer.html
index 8143669d..6c29850f 100644
--- a/tpl/page.footer.html
+++ b/tpl/page.footer.html
@@ -1,5 +1,8 @@
1<div id="footer"> 1<div id="footer">
2 <b><a href="https://github.com/shaarli/Shaarli">Shaarli</a></b> - The personal, minimalist, super-fast, no-database delicious clone by the <a href="https://github.com/shaarli/Shaarli">Shaarli</a> community - <a href="doc/Home.html">Help/documentation</a> 2 <b><a href="https://github.com/shaarli/Shaarli">Shaarli</a></b> - The personal, minimalist, super-fast, no-database delicious clone by the <a href="https://github.com/shaarli/Shaarli">Shaarli</a> community - <a href="doc/Home.html">Help/documentation</a>
3 {loop="$plugins_footer.text"}
4 {$value}
5 {/loop}
3</div> 6</div>
4{if="$newversion"} 7{if="$newversion"}
5 <div id="newversion"><span id="version_id">&#x25CF;</span> Shaarli {$newversion} is <a href="https://github.com/shaarli/Shaarli/releases">available</a>.</div> 8 <div id="newversion"><span id="version_id">&#x25CF;</span> Shaarli {$newversion} is <a href="https://github.com/shaarli/Shaarli/releases">available</a>.</div>
@@ -7,3 +10,7 @@
7{if="isLoggedIn()"} 10{if="isLoggedIn()"}
8<script>function confirmDeleteLink() { var agree=confirm("Are you sure you want to delete this link ?"); if (agree) return true ; else return false ; }</script> 11<script>function confirmDeleteLink() { var agree=confirm("Are you sure you want to delete this link ?"); if (agree) return true ; else return false ; }</script>
9{/if} 12{/if}
13
14{loop="$plugins_footer.js_files"}
15 <script src="{$value}#"></script>
16{/loop}
diff --git a/tpl/page.header.html b/tpl/page.header.html
index 2d186aa2..1d46d80c 100644
--- a/tpl/page.header.html
+++ b/tpl/page.header.html
@@ -11,7 +11,7 @@
11 <a href="{$titleLink}">{$shaarlititle}</a> 11 <a href="{$titleLink}">{$shaarlititle}</a>
12 </span> 12 </span>
13 </li> 13 </li>
14 14
15{if="!empty($_GET['source']) && $_GET['source']=='bookmarklet'"} 15{if="!empty($_GET['source']) && $_GET['source']=='bookmarklet'"}
16 {ignore} When called as a popup from bookmarklet, do not display menu. {/ignore} 16 {ignore} When called as a popup from bookmarklet, do not display menu. {/ignore}
17{else} 17{else}
@@ -33,6 +33,9 @@
33 <li><a href="?do=tagcloud">Tag cloud</a></li> 33 <li><a href="?do=tagcloud">Tag cloud</a></li>
34 <li><a href="?do=picwall{$searchcrits}">Picture wall</a></li> 34 <li><a href="?do=picwall{$searchcrits}">Picture wall</a></li>
35 <li><a href="?do=daily">Daily</a></li> 35 <li><a href="?do=daily">Daily</a></li>
36 {loop="$plugins_header.buttons_toolbar"}
37 {$value}
38 {/loop}
36{/if} 39{/if}
37 </ul> 40 </ul>
38</div> 41</div>
diff --git a/tpl/picwall.html b/tpl/picwall.html
index f59685cf..97d5efdf 100644
--- a/tpl/picwall.html
+++ b/tpl/picwall.html
@@ -5,15 +5,34 @@
5</head> 5</head>
6<body> 6<body>
7<div id="pageheader">{include="page.header"}</div> 7<div id="pageheader">{include="page.header"}</div>
8
9<div id="plugin_zone_start_picwall" class="plugin_zone">
10 {loop="$plugin_start_zone"}
11 {$value}
12 {/loop}
13</div>
14
8<div class="center"> 15<div class="center">
9 <div id="picwall_container"> 16 <div id="picwall_container">
10 {loop="linksToDisplay"} 17 {loop="linksToDisplay"}
11 <div class="picwall_pictureframe"> 18 <div class="picwall_pictureframe">
12 {$value.thumbnail}<a href="{$value.url}"><span class="info">{$value.title}</span></a> 19 {$value.thumbnail}<a href="{$value.url}"><span class="info">{$value.title}</span></a>
20 {loop="$value.picwall_plugin"}
21 {$value}
22 {/loop}
13 </div> 23 </div>
14 {/loop} 24 {/loop}
15 </div> 25 </div>
16</div> 26</div>
27
28<div class="clear"></div>
29
30<div id="plugin_zone_end_picwall" class="plugin_zone">
31 {loop="$plugin_end_zone"}
32 {$value}
33 {/loop}
34</div>
35
17{include="page.footer"} 36{include="page.footer"}
18 37
19<script> 38<script>
diff --git a/tpl/tagcloud.html b/tpl/tagcloud.html
index 092f2294..5891cd25 100644
--- a/tpl/tagcloud.html
+++ b/tpl/tagcloud.html
@@ -4,9 +4,25 @@
4<body> 4<body>
5 <div id="pageheader">{include="page.header"}</div> 5 <div id="pageheader">{include="page.header"}</div>
6<div class="center"> 6<div class="center">
7 <div id="plugin_zone_start_tagcloud" class="plugin_zone">
8 {loop="$plugin_start_zone"}
9 {$value}
10 {/loop}
11 </div>
12
7 <div id="cloudtag"> 13 <div id="cloudtag">
8 {loop="tags"} 14 {loop="tags"}
9 <span class="count">{$value.count}</span><a href="?searchtags={$key|urlencode}" style="font-size:{$value.size}pt;">{$key}</a> 15 <span class="count">{$value.count}</span>
16 <a href="?searchtags={$key|urlencode}" style="font-size:{$value.size}pt;">{$key}</a>
17 {loop="$value.tag_plugin"}
18 {$value}
19 {/loop}
20 {/loop}
21 </div>
22
23 <div id="plugin_zone_end_tagcloud" class="plugin_zone">
24 {loop="$plugin_end_zone"}
25 {$value}
10 {/loop} 26 {/loop}
11 </div> 27 </div>
12</div> 28</div>
diff --git a/tpl/tools.html b/tpl/tools.html
index a3b469de..c9ada4ac 100644
--- a/tpl/tools.html
+++ b/tpl/tools.html
@@ -13,6 +13,9 @@
13 <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> 13 <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>
14 <a class="smallbutton" onclick="alert('Drag this link to your bookmarks toolbar, or right-click it and choose Bookmark This Link...');return false;" href="?private=1&post="><b>✚Add Note</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 "✚Add Note" button anytime to start composing a (default private) Note (text post) to your Shaarli.</span></a><br><br> 14 <a class="smallbutton" onclick="alert('Drag this link to your bookmarks toolbar, or right-click it and choose Bookmark This Link...');return false;" href="?private=1&post="><b>✚Add Note</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 "✚Add Note" button anytime to start composing a (default private) Note (text post) to your Shaarli.</span></a><br><br>
15 <a class="smallbutton" onclick="activateFirefoxSocial(this)"><b>✚Add to Firefox social</b></a> <a href="#" style="clear:none;"><span>&#x21D0; Click on this button to add Shaarli to the "Share this page" button in Firefox.</span></a><br><br> 15 <a class="smallbutton" onclick="activateFirefoxSocial(this)"><b>✚Add to Firefox social</b></a> <a href="#" style="clear:none;"><span>&#x21D0; Click on this button to add Shaarli to the "Share this page" button in Firefox.</span></a><br><br>
16 {loop="$tools_plugin"}
17 {$value}
18 {/loop}
16 <div class="clear"></div> 19 <div class="clear"></div>
17 20
18 <script> 21 <script>