Theming:
- Introduce a new theme
- Allow selecting themes/templates from the configuration page
+ - New/Edit link form can be submitted using CTRL+Enter in the textarea
+ - Shaarli version is displayed in the footer when logged in
- Add plugin placeholders to Atom/RSS feed templates
- Add OpenSearch to feed templates
- Add `campaign_` to the URL cleanup pattern list
- Add an AUTHORS file and Makefile target to list authors from Git commit data
+- Link imports are now logged in `data/` folder, and can be debug using `dev.debug=true` setting.
### Changed
- Docker: enable nginx URL rewriting for the REST API
- Move `user.css` to the `data` folder
- Move default template files to a subfolder (`default`)
- Rename the legacy theme to `vintage`
+ - Private only filter is now displayed as a search parameter
+ - Autocomplete: pre-select the first element
- Move PubSubHub to a dedicated plugin
- Coding style:
- explicit method visibility
- remove unused variables
- The updater now keeps custom theme preferences
- Simplify the COPYING information
+- Improved client locale detection
+- Improved date time display depending on the locale
+- Partial namespace support for Shaarli classes
### Removed
- PHP < 5.5 compatibility
- Fix redirection after link deletion
- Do not access LinkDB links by ID before the Updater applies migrations
- Remove extra spaces in the bookmarklet's name
+- Piwik plugin: Piwik URL protocol can now be set (http or https)
+- All inline JS has been moved to dedicated JS files
### Security
- Markdown plugin: escape HTML entities by default
License: MIT License (http://opensource.org/licenses/MIT)
Copyright: (C) 2015 Nicolas Lœuillet - https://github.com/wallabag/wallabag
+Files: tpl/default/sad_star.png
+License: MIT License (http://opensource.org/licenses/MIT)
+Copyright: (C) 2015 kalvn - https://github.com/kalvn/Shaarli-Material
+
----------------------------------------------------
ZLIB/LIBPNG LICENSE
- [Bugs/Feature requests/Discussion](https://github.com/shaarli/Shaarli/issues/)
### Demo
-You can use this [public demo instance of Shaarli](http://shaarlidemo.tuxfamily.org/Shaarli).
+You can use this [public demo instance of Shaarli](https://demo.shaarli.org).
It runs the latest development version of Shaarli and is updated/reset daily.
Login: `demo`; Password: `demo`
<?php
+use Psr\Log\LogLevel;
+use Shaarli\Config\ConfigManager;
+use Shaarli\NetscapeBookmarkParser\NetscapeBookmarkParser;
+use Katzgrau\KLogger\Logger;
+
/**
* Utilities to import and export bookmarks using the Netscape format
+ * TODO: Not static, use a container.
*/
class NetscapeBookmarkUtils
{
/**
* Imports Web bookmarks from an uploaded Netscape bookmark dump
*
- * @param array $post Server $_POST parameters
- * @param array $files Server $_FILES parameters
- * @param LinkDB $linkDb Loaded LinkDB instance
- * @param string $pagecache Page cache
+ * @param array $post Server $_POST parameters
+ * @param array $files Server $_FILES parameters
+ * @param LinkDB $linkDb Loaded LinkDB instance
+ * @param ConfigManager $conf instance
*
* @return string Summary of the bookmark import status
*/
- public static function import($post, $files, $linkDb, $pagecache)
+ public static function import($post, $files, $linkDb, $conf)
{
$filename = $files['filetoupload']['name'];
$filesize = $files['filetoupload']['size'];
$defaultPrivacy = 0;
$parser = new NetscapeBookmarkParser(
- true, // nested tag support
- $defaultTags, // additional user-specified tags
- strval(1 - $defaultPrivacy) // defaultPub = 1 - defaultPrivacy
+ true, // nested tag support
+ $defaultTags, // additional user-specified tags
+ strval(1 - $defaultPrivacy), // defaultPub = 1 - defaultPrivacy
+ $conf->get('resource.data_dir') // log path, will be overridden
+ );
+ $logger = new Logger(
+ $conf->get('resource.data_dir'),
+ ! $conf->get('dev.debug') ? LogLevel::INFO : LogLevel::DEBUG,
+ [
+ 'prefix' => 'import.',
+ 'extension' => 'log',
+ ]
);
+ $parser->setLogger($logger);
$bookmarks = $parser->parseString($data);
$importCount = 0;
$importCount++;
}
- $linkDb->save($pagecache);
+ $linkDb->save($conf->get('resource.page_cache'));
return self::importStatus(
$filename,
$filesize,
$data = str_replace(self::getPhpSuffix(), '', $data);
$data = json_decode($data, true);
if ($data === null) {
- $error = json_last_error();
- throw new \Exception('An error occurred while parsing JSON file: error code #'. $error);
+ $errorCode = json_last_error();
+ $error = 'An error occurred while parsing JSON configuration file ('. $filepath .'): error code #';
+ $error .= $errorCode. '<br>➜ <code>' . json_last_error_msg() .'</code>';
+ if ($errorCode === JSON_ERROR_SYNTAX) {
+ $error .= '<br>Please check your JSON syntax (without PHP comment tags) using a JSON lint tool such as ';
+ $error .= '<a href="http://jsonlint.com/">jsonlint.com</a>.';
+ }
+ throw new \Exception($error);
}
return $data;
}
*/
protected function load()
{
- $this->loadedConfig = $this->configIO->read($this->getConfigFileExt());
+ try {
+ $this->loadedConfig = $this->configIO->read($this->getConfigFileExt());
+ } catch (\Exception $e) {
+ die($e->getMessage());
+ }
$this->setDefaultValues();
}
"keywords": ["bookmark", "link", "share", "web"],
"require": {
"php": ">=5.5",
- "shaarli/netscape-bookmark-parser": "1.*",
+ "shaarli/netscape-bookmark-parser": "^2.0",
"erusev/parsedown": "1.6",
"slim/slim": "^3.0",
"pubsubhubbub/publisher": "dev-master"
+/** @licstart The following is the entire license notice for the
+ * JavaScript code in this page.
+ *
+ * Copyright: (c) 2011-2015 Sébastien SAUVAGE <sebsauvage@sebsauvage.net>
+ * (c) 2011-2017 The Shaarli Community, see AUTHORS
+ *
+ * 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.
+ *
+ * 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:
+ *
+ * 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.
+ *
+ * 2. Altered source versions must be plainly marked as such, and must
+ * not be misrepresented as being the original software.
+ *
+ * 3. This notice may not be removed or altered from any source distribution.
+ *
+ * @licend The above is the entire license notice
+ * for the JavaScript code in this page.
+ */
+
var awp = Awesomplete.$;
var autocompleteFields = document.querySelectorAll('input[data-multiple]');
[].forEach.call(autocompleteFields, function(autocompleteField) {
+/** @licstart The following is the entire license notice for the
+ * JavaScript code in this page.
+ *
+ * Copyright: (c) 2011-2015 Sébastien SAUVAGE <sebsauvage@sebsauvage.net>
+ * (c) 2011-2017 The Shaarli Community, see AUTHORS
+ *
+ * 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.
+ *
+ * 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:
+ *
+ * 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.
+ *
+ * 2. Altered source versions must be plainly marked as such, and must
+ * not be misrepresented as being the original software.
+ *
+ * 3. This notice may not be removed or altered from any source distribution.
+ *
+ * @licend The above is the entire license notice
+ * for the JavaScript code in this page.
+ */
+
/**
* Change the position counter of a row.
*
$_POST,
$_FILES,
$LINKSDB,
- $conf->get('resource.page_cache')
+ $conf
);
echo '<script>alert("'.$status.'");document.location=\'?do='
.Router::$PAGE_IMPORT .'\';</script>';
<!-- Piwik -->
<script type="text/javascript">
+ /** @licstart The following is the entire license notice for the
+ * JavaScript code in this page.
+ *
+ * Copyright: (c) 2011-2015 Sébastien SAUVAGE <sebsauvage@sebsauvage.net>
+ * (c) 2011-2017 The Shaarli Community, see AUTHORS
+ *
+ * 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.
+ *
+ * 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:
+ *
+ * 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.
+ *
+ * 2. Altered source versions must be plainly marked as such, and must
+ * not be misrepresented as being the original software.
+ *
+ * 3. This notice may not be removed or altered from any source distribution.
+ *
+ * @licend The above is the entire license notice
+ * for the JavaScript code in this page.
+ */
+
var _paq = _paq || [];
_paq.push(['trackPageView']);
_paq.push(['enableLinkTracking']);
+/** @licstart The following is the entire license notice for the
+ * JavaScript code in this page.
+ *
+ * Copyright: (c) 2011-2015 Sébastien SAUVAGE <sebsauvage@sebsauvage.net>
+ * (c) 2011-2017 The Shaarli Community, see AUTHORS
+ *
+ * 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.
+ *
+ * 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:
+ *
+ * 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.
+ *
+ * 2. Altered source versions must be plainly marked as such, and must
+ * not be misrepresented as being the original software.
+ *
+ * 3. This notice may not be removed or altered from any source distribution.
+ *
+ * @licend The above is the entire license notice
+ * for the JavaScript code in this page.
+ */
+
var run_playideos = (function () {
var e, n, t, o, r, i = [].indexOf || function (e) {
for (var n = 0, t = this.length; n < t; n++) {
+/** @licstart The following is the entire license notice for the
+ * JavaScript code in this page.
+ *
+ * Copyright: (c) 2011-2015 Sébastien SAUVAGE <sebsauvage@sebsauvage.net>
+ * (c) 2011-2017 The Shaarli Community, see AUTHORS
+ *
+ * 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.
+ *
+ * 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:
+ *
+ * 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.
+ *
+ * 2. Altered source versions must be plainly marked as such, and must
+ * not be misrepresented as being the original software.
+ *
+ * 3. This notice may not be removed or altered from any source distribution.
+ *
+ * @licend The above is the entire license notice
+ * for the JavaScript code in this page.
+ */
+
// Show the QR-Code of a permalink (when the QR-Code icon is clicked).
function showQrCode(caller,loading)
{
require_once 'application/NetscapeBookmarkUtils.php';
+use Shaarli\Config\ConfigManager;
/**
* Utility function to load a file's metadata in a $_FILES-like array
*/
protected $pagecache = 'tests';
+ /**
+ * @var ConfigManager instance.
+ */
+ protected $conf;
+
/**
* @var string Save the current timezone.
*/
// start with an empty datastore
file_put_contents(self::$testDatastore, '<?php /* S7QysKquBQA= */ ?>');
$this->linkDb = new LinkDB(self::$testDatastore, true, false);
+ $this->conf = new ConfigManager('tests/utils/config/configJson');
+ $this->conf->set('resource.page_cache', $this->pagecache);
}
public static function tearDownAfterClass()
$this->assertEquals(
'File empty.htm (0 bytes) has an unknown file format.'
.' Nothing was imported.',
- NetscapeBookmarkUtils::import(NULL, $files, NULL, NULL)
+ NetscapeBookmarkUtils::import(NULL, $files, NULL, $this->conf)
);
$this->assertEquals(0, count($this->linkDb));
}
$files = file2array('no_doctype.htm');
$this->assertEquals(
'File no_doctype.htm (350 bytes) has an unknown file format. Nothing was imported.',
- NetscapeBookmarkUtils::import(NULL, $files, NULL, NULL)
+ NetscapeBookmarkUtils::import(NULL, $files, NULL, $this->conf)
);
$this->assertEquals(0, count($this->linkDb));
}
$this->assertEquals(
'File internet_explorer_encoding.htm (356 bytes) was successfully processed:'
.' 1 links imported, 0 links overwritten, 0 links skipped.',
- NetscapeBookmarkUtils::import(array(), $files, $this->linkDb, $this->pagecache)
+ NetscapeBookmarkUtils::import(array(), $files, $this->linkDb, $this->conf)
);
$this->assertEquals(1, count($this->linkDb));
$this->assertEquals(0, count_private($this->linkDb));
$this->assertEquals(
'File netscape_nested.htm (1337 bytes) was successfully processed:'
.' 8 links imported, 0 links overwritten, 0 links skipped.',
- NetscapeBookmarkUtils::import(array(), $files, $this->linkDb, $this->pagecache)
+ NetscapeBookmarkUtils::import(array(), $files, $this->linkDb, $this->conf)
);
$this->assertEquals(8, count($this->linkDb));
$this->assertEquals(2, count_private($this->linkDb));
$this->assertEquals(
'File netscape_basic.htm (482 bytes) was successfully processed:'
.' 2 links imported, 0 links overwritten, 0 links skipped.',
- NetscapeBookmarkUtils::import(array(), $files, $this->linkDb, $this->pagecache)
+ NetscapeBookmarkUtils::import(array(), $files, $this->linkDb, $this->conf)
);
$this->assertEquals(2, count($this->linkDb));
$this->assertEquals(
'File netscape_basic.htm (482 bytes) was successfully processed:'
.' 2 links imported, 0 links overwritten, 0 links skipped.',
- NetscapeBookmarkUtils::import($post, $files, $this->linkDb, $this->pagecache)
+ NetscapeBookmarkUtils::import($post, $files, $this->linkDb, $this->conf)
);
$this->assertEquals(2, count($this->linkDb));
$this->assertEquals(1, count_private($this->linkDb));
$this->assertEquals(
'File netscape_basic.htm (482 bytes) was successfully processed:'
.' 2 links imported, 0 links overwritten, 0 links skipped.',
- NetscapeBookmarkUtils::import($post, $files, $this->linkDb, $this->pagecache)
+ NetscapeBookmarkUtils::import($post, $files, $this->linkDb, $this->conf)
);
$this->assertEquals(2, count($this->linkDb));
$this->assertEquals(0, count_private($this->linkDb));
$this->assertEquals(
'File netscape_basic.htm (482 bytes) was successfully processed:'
.' 2 links imported, 0 links overwritten, 0 links skipped.',
- NetscapeBookmarkUtils::import($post, $files, $this->linkDb, $this->pagecache)
+ NetscapeBookmarkUtils::import($post, $files, $this->linkDb, $this->conf)
);
$this->assertEquals(2, count($this->linkDb));
$this->assertEquals(2, count_private($this->linkDb));
$this->assertEquals(
'File netscape_basic.htm (482 bytes) was successfully processed:'
.' 2 links imported, 0 links overwritten, 0 links skipped.',
- NetscapeBookmarkUtils::import($post, $files, $this->linkDb, $this->pagecache)
+ NetscapeBookmarkUtils::import($post, $files, $this->linkDb, $this->conf)
);
$this->assertEquals(2, count($this->linkDb));
$this->assertEquals(2, count_private($this->linkDb));
$this->assertEquals(
'File netscape_basic.htm (482 bytes) was successfully processed:'
.' 2 links imported, 2 links overwritten, 0 links skipped.',
- NetscapeBookmarkUtils::import($post, $files, $this->linkDb, $this->pagecache)
+ NetscapeBookmarkUtils::import($post, $files, $this->linkDb, $this->conf)
);
$this->assertEquals(2, count($this->linkDb));
$this->assertEquals(0, count_private($this->linkDb));
$this->assertEquals(
'File netscape_basic.htm (482 bytes) was successfully processed:'
.' 2 links imported, 0 links overwritten, 0 links skipped.',
- NetscapeBookmarkUtils::import($post, $files, $this->linkDb, $this->pagecache)
+ NetscapeBookmarkUtils::import($post, $files, $this->linkDb, $this->conf)
);
$this->assertEquals(2, count($this->linkDb));
$this->assertEquals(0, count_private($this->linkDb));
$this->assertEquals(
'File netscape_basic.htm (482 bytes) was successfully processed:'
.' 2 links imported, 2 links overwritten, 0 links skipped.',
- NetscapeBookmarkUtils::import($post, $files, $this->linkDb, $this->pagecache)
+ NetscapeBookmarkUtils::import($post, $files, $this->linkDb, $this->conf)
);
$this->assertEquals(2, count($this->linkDb));
$this->assertEquals(2, count_private($this->linkDb));
$this->assertEquals(
'File netscape_basic.htm (482 bytes) was successfully processed:'
.' 2 links imported, 0 links overwritten, 0 links skipped.',
- NetscapeBookmarkUtils::import($post, $files, $this->linkDb, $this->pagecache)
+ NetscapeBookmarkUtils::import($post, $files, $this->linkDb, $this->conf)
);
$this->assertEquals(2, count($this->linkDb));
$this->assertEquals(0, count_private($this->linkDb));
$this->assertEquals(
'File netscape_basic.htm (482 bytes) was successfully processed:'
.' 0 links imported, 0 links overwritten, 2 links skipped.',
- NetscapeBookmarkUtils::import($post, $files, $this->linkDb, $this->pagecache)
+ NetscapeBookmarkUtils::import($post, $files, $this->linkDb, $this->conf)
);
$this->assertEquals(2, count($this->linkDb));
$this->assertEquals(0, count_private($this->linkDb));
$this->assertEquals(
'File netscape_basic.htm (482 bytes) was successfully processed:'
.' 2 links imported, 0 links overwritten, 0 links skipped.',
- NetscapeBookmarkUtils::import($post, $files, $this->linkDb, $this->pagecache)
+ NetscapeBookmarkUtils::import($post, $files, $this->linkDb, $this->conf)
);
$this->assertEquals(2, count($this->linkDb));
$this->assertEquals(0, count_private($this->linkDb));
$this->assertEquals(
'File netscape_basic.htm (482 bytes) was successfully processed:'
.' 2 links imported, 0 links overwritten, 0 links skipped.',
- NetscapeBookmarkUtils::import($post, $files, $this->linkDb, $this->pagecache)
+ NetscapeBookmarkUtils::import($post, $files, $this->linkDb, $this->conf)
);
$this->assertEquals(2, count($this->linkDb));
$this->assertEquals(0, count_private($this->linkDb));
$this->assertEquals(
'File same_date.htm (453 bytes) was successfully processed:'
.' 3 links imported, 0 links overwritten, 0 links skipped.',
- NetscapeBookmarkUtils::import(array(), $files, $this->linkDb, $this->pagecache)
+ NetscapeBookmarkUtils::import(array(), $files, $this->linkDb, $this->conf)
);
$this->assertEquals(3, count($this->linkDb));
$this->assertEquals(0, count_private($this->linkDb));
* Read a non existent config file -> empty array.
*
* @expectedException \Exception
- * @expectedExceptionMessage An error occurred while parsing JSON file: error code #4
+ * @expectedExceptionMessageRegExp /An error occurred while parsing JSON configuration file \([\w\/\.]+\): error code #4/
*/
public function testReadInvalidJson()
{
},
"resource": {
"datastore": "tests\/utils\/config\/datastore.php",
- "data_dir": "tests\/utils\/config",
+ "data_dir": "sandbox/",
"raintpl_tpl": "tpl/"
},
"plugins": {
</div>
<div class="pure-u-lg-{$ratioInput} pure-u-1">
<div class="form-input">
- <select name="theme" id="theme">
+ <select name="theme" id="theme" class="align">
{loop="$theme_available"}
<option value="{$value}"
{if="$value===$theme"}
</form>
{include="page.footer"}
-
-<script>
- (function (window, document) {
- var toRemove = document.getElementById('timezone-remove');
- var firstSelect = toRemove.getElementsByTagName('select')[0];
- var secondSelect = toRemove.getElementsByTagName('select')[1];
- toRemove.parentNode.removeChild(toRemove);
- var toAdd = document.getElementById('timezone-add');
- var newTimezone = '<span class="timezone-continent">Continent ' + firstSelect.outerHTML + '</span>';
- newTimezone += ' <span class="timezone-country">Country ' + secondSelect.outerHTML + '</span>';
- toAdd.innerHTML = newTimezone;
- })(this, this.document);
-</script>
</body>
</html>
.page-form-complete .form-label label,
.page-form-complete .form-input input,
+.page-form-complete .form-input select.align,
.page-form-complete .timezone {
position: absolute;
top: 50%;
color: black;
}
+form[name="linkform"].page-form {
+ overflow: visible;
+}
+
@media screen and (max-width: 64em) {
.page-form-complete .form-label {
height: inherit;
{include="includes"}
</head>
<body>
- {if="$source !== 'firefoxsocialapi' && $source !== 'bookmarklet'"}
- {include="page.header"}
- {else}
- <div class="center">Shaare to: {$shaarlititle}</div>
- {/if}
+ {include="page.header"}
<div id="editlinkform" class="pure-g">
<div class="pure-u-lg-1-5 pure-u-1-24"></div>
<form method="post" name="linkform" class="page-form pure-u-lg-3-5 pure-u-22-24 page-form page-form-light">
<label for="lf_url">{'URL'|t}</label>
</div>
<div>
- <input type="text" name="lf_url" id="lf_url" value="{$link.url}" class="lf_input">
+ <input type="text" name="lf_url" id="lf_url" value="{$link.url}" class="lf_input autofocus">
</div>
<div>
<label for="lf_title">{'Title'|t}</label>
</div>
<div>
- <input type="text" name="lf_title" id="lf_title" value="{$link.title}" class="lf_input">
+ <input type="text" name="lf_title" id="lf_title" value="{$link.title}" class="lf_input autofocus">
</div>
<div>
<label for="lf_description">{'Description'|t}</label>
</div>
<div>
- <textarea name="lf_description" id="lf_description">{$link.description}</textarea>
+ <textarea name="lf_description" id="lf_description" class="autofocus">{$link.description}</textarea>
</div>
<div>
<label for="lf_tags">{'Tags'|t}</label>
</div>
<div>
- <input type="text" name="lf_tags" id="lf_tags" value="{$link.tags}" class="lf_input"
- data-list="{loop="$tags"}{$key}, {/loop}" data-multiple autocomplete="off" >
+ <input type="text" name="lf_tags" id="lf_tags" value="{$link.tags}" class="lf_input autofocus"
+ data-list="{loop="$tags"}{$key}, {/loop}" data-multiple data-autofirst autocomplete="off" >
</div>
<div>
{/if}
</form>
</div>
- {if="$source !== 'firefoxsocialapi' && $source !== 'bookmarklet'"}
- {include="page.footer"}
- {/if}
-<script>
- awesompleteUniqueTag('#lf_tags');
- if (!document.linkform.lf_title.value) {
- document.linkform.lf_title.focus();
- } else if (!document.linkform.lf_description.value) {
- document.linkform.lf_description.focus();
- } else {
- document.linkform.lf_tags.focus();
- }
-</script>
+ {include="page.footer"}
</body>
</html>
</div>
</form>
{include="page.footer"}
-<script>
- // FIXME!
- (function (window, document) {
- var toRemove = document.getElementById('timezone-remove');
- var firstSelect = toRemove.getElementsByTagName('select')[0];
- var secondSelect = toRemove.getElementsByTagName('select')[1];
- toRemove.parentNode.removeChild(toRemove);
- var toAdd = document.getElementById('timezone-add');
- var newTimezone = '<span class="timezone-continent">Continent ' + firstSelect.outerHTML + '</span>';
- newTimezone += ' <span class="timezone-country">Country ' + secondSelect.outerHTML + '</span>';
- toAdd.innerHTML = newTimezone;
- })(this, this.document);
-</script>
</body>
</html>
+/** @licstart The following is the entire license notice for the
+ * JavaScript code in this page.
+ *
+ * Copyright: (c) 2011-2015 Sébastien SAUVAGE <sebsauvage@sebsauvage.net>
+ * (c) 2011-2017 The Shaarli Community, see AUTHORS
+ *
+ * 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.
+ *
+ * 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:
+ *
+ * 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.
+ *
+ * 2. Altered source versions must be plainly marked as such, and must
+ * not be misrepresented as being the original software.
+ *
+ * 3. This notice may not be removed or altered from any source distribution.
+ *
+ * @licend The above is the entire license notice
+ * for the JavaScript code in this page.
+ */
+
window.onload = function () {
/**
/**
* Autofocus text fields
*/
- var autofocusElements = document.querySelector('.autofocus');
- if (autofocusElements != null) {
- autofocusElements.focus();
+ // ES6 syntax
+ let autofocusElements = document.querySelectorAll('.autofocus');
+ for (let autofocusElement of autofocusElements) {
+ if (autofocusElement.value == '') {
+ autofocusElement.focus();
+ break;
+ }
}
/**
}
});
}
+
+ /**
+ * TimeZome select
+ * FIXME! way too hackish
+ */
+ var toRemove = document.getElementById('timezone-remove');
+ if (toRemove != null) {
+ var firstSelect = toRemove.getElementsByTagName('select')[0];
+ var secondSelect = toRemove.getElementsByTagName('select')[1];
+ toRemove.parentNode.removeChild(toRemove);
+ var toAdd = document.getElementById('timezone-add');
+ var newTimezone = '<span class="timezone-continent">Continent ' + firstSelect.outerHTML + '</span>';
+ newTimezone += ' <span class="timezone-country">Country ' + secondSelect.outerHTML + '</span>';
+ toAdd.innerHTML = newTimezone;
+ }
+
+ /**
+ * Awesomplete trigger.
+ */
+ var tags = document.getElementById('lf_tags');
+ if (tags != null) {
+ awesompleteUniqueTag('#lf_tags');
+ }
+
+ /**
+ * bLazy trigger
+ */
+ var picwall = document.getElementById('picwall_container');
+ if (picwall != null) {
+ var bLazy = new Blazy();
+ }
+
+ /**
+ * Bookmarklet alert
+ */
+ var bookmarkletLinks = document.querySelectorAll('.bookmarklet-link');
+ var bkmMessage = document.getElementById('bookmarklet-alert');
+ [].forEach.call(bookmarkletLinks, function(link) {
+ link.addEventListener('click', function(event) {
+ event.preventDefault();
+ alert(bkmMessage.value);
+ });
+ });
+
+ /**
+ * Firefox Social
+ */
+ var ffButton = document.getElementById('ff-social-button');
+ if (ffButton != null) {
+ ffButton.addEventListener('click', function(event) {
+ activateFirefoxSocial(event.target);
+ });
+ }
+
+ /**
+ * Plugin admin order
+ */
+ var orderPA = document.querySelectorAll('.order');
+ [].forEach.call(orderPA, function(link) {
+ link.addEventListener('click', function(event) {
+ event.preventDefault();
+ if (event.target.classList.contains('order-up')) {
+ return orderUp(event.target.parentNode.parentNode.getAttribute('data-order'));
+ } else if (event.target.classList.contains('order-down')) {
+ return orderDown(event.target.parentNode.parentNode.getAttribute('data-order'));
+ }
+ });
+ });
};
+
+function activateFirefoxSocial(node) {
+ var loc = location.href;
+ var baseURL = loc.substring(0, loc.lastIndexOf("/"));
+
+ // Keeping the data separated (ie. not in the DOM) so that it's maintainable and diffable.
+ var data = {
+ name: "{$shaarlititle}",
+ description: "The personal, minimalist, super-fast, database free, bookmarking service by the Shaarli community.",
+ author: "Shaarli",
+ version: "1.0.0",
+
+ iconURL: baseURL + "/images/favicon.ico",
+ icon32URL: baseURL + "/images/favicon.ico",
+ icon64URL: baseURL + "/images/favicon.ico",
+
+ shareURL: baseURL + "{noparse}?post=%{url}&title=%{title}&description=%{text}&source=firefoxsocialapi{/noparse}",
+ homepageURL: baseURL
+ };
+ node.setAttribute("data-service", JSON.stringify(data));
+
+ var activate = new CustomEvent("ActivateSocialFeature");
+ node.dispatchEvent(activate);
+}
{if="!empty($search_tags)"}
value="{$search_tags}"
{/if}
- autocomplete="off" data-multiple data-minChars="1"
+ autocomplete="off" data-multiple data-autofirst data-minChars="1"
data-list="{loop="$tags"}{$key}, {/loop}"
>
<button type="submit" class="search-button"><i class="fa fa-search"></i></button>
{if="!empty($username)"}value="{$username}"{/if} class="autofocus" tabindex="20">
</div>
<div>
- <input type="password" name="password" placeholder="{'Password'|t}" tabindex="21">
+ <input type="password" name="password" placeholder="{'Password'|t}" class="autofocus" tabindex="21">
</div>
<div class="remember-me">
<input type="checkbox" name="longlastingsession" id="longlastingsessionform"
{/if}
{include="page.footer"}
-<script>
- {if="ban_canLogin($conf) && ! empty($username)"}
- // Focus password on load if the username is set.
- var passwords = document.getElementsByName('password');
- if (passwords.length == 2) {
- passwords[1].focus();
- }
- {/if}
-</script>
</body>
</html>
<div class="pure-g">
<div class="pure-u-2-24"></div>
<div id="footer" class="pure-u-20-24">
- <strong><a href="https://github.com/shaarli/Shaarli">Shaarli</a></strong> ·
+ <strong><a href="https://github.com/shaarli/Shaarli">Shaarli</a></strong>
+ {if="isLoggedIn()===true"}
+ {$version}
+ {/if}
+ ·
The personal, minimalist, super-fast, database free, bookmarking service by the Shaarli community ·
<a href="doc/Home.html" rel="nofollow">Documentation</a>
{loop="$plugins_footer.text"}
<div class="pure-u-lg-0 pure-u-1">
<div class="pure-menu">
<a href="{$titleLink}" class="pure-menu-link">
- <i class="fa fa-home"></i>
+ <img src="img/icon.png" width="16" height="16" class="head-logo" alt="logo" />
{$shaarlititle}
</a>
<a href="#" class="menu-toggle" id="menu-toggle"><s class="bar"></s><s class="bar"></s></a>
{if="!empty($search_tags)"}
value="{$search_tags}"
{/if}
- autocomplete="off" data-multiple data-minChars="1"
+ autocomplete="off" data-multiple data-autofirst data-minChars="1"
data-list="{loop="$tags"}{$key}, {/loop}"
>
<button type="submit" class="search-button"><i class="fa fa-search"></i></button>
{include="page.footer"}
<script src="inc/blazy-1.3.1.min.js#"></script>
-<script>
- window.onload = function() {
- var bLazy = new Blazy();
- }
-</script>
</body>
</html>
<td><div class="pure-u-0 pure-u-lg-visible"><label for="{$key}">{$value.description}</label></div></td>
<td class="center">
{if="count($enabledPlugins)>1"}
- <a href="#" class="order"
- onclick="return orderUp(this.parentNode.parentNode.getAttribute('data-order'));">
- ▲
- </a>
- <a href="#" class="order"
- onclick="return orderDown(this.parentNode.parentNode.getAttribute('data-order'));">
- ▼
- </a>
+ <a href="#" class="order order-up">▲</a>
+ <a href="#" class="order order-down">▼</a>
{/if}
<input type="hidden" name="order_{$key}" value="{$counter}">
</td>
<div class="tools-item">
<a title="{'Drag this link to your bookmarks toolbar or right-click it and Bookmark This Link'|t},
{'then click ✚Shaare link button in any page you want to share'|t}"
- onclick="return alertBookmarklet();"
+ class="bookmarklet-link"
href="javascript:(
function(){
var%20url%20=%20location.href;
<div class="tools-item">
<a title="{'Drag this link to your bookmarks toolbar or right-click it and Bookmark This Link'|t},
{'Then click ✚Add Note button anytime to start composing a private Note (text post) to your Shaarli'|t}"
- onclick="return alertBookmarklet();"
- href="?private=1&post=">
+ href="?private=1&post="
+ class="bookmarklet-link">
<span class="pure-button pure-u-lg-2-3 pure-u-3-4">✚ {'Add Note'|t}</span>
</a>
</div>
<div class="tools-item">
<a title="{'Click on this button to add Shaarli to the 'Share this page' button in Firefox"
- onclick="activateFirefoxSocial(this)">
+ id="ff-social-button">
<span class="pure-button pure-u-lg-2-3 pure-u-3-4">✚ {'Add to'|t} Firefox Social</span>
</a>
</div>
</div>
{include="page.footer"}
-
-<script>
- {if="$sslenabled"}
- function activateFirefoxSocial(node) {
- var loc = location.href;
- var baseURL = loc.substring(0, loc.lastIndexOf("/"));
-
- // Keeping the data separated (ie. not in the DOM) so that it's maintainable and diffable.
- var data = {
- name: "{$shaarlititle}",
- description: "The personal, minimalist, super-fast, database free, bookmarking service by the Shaarli community.",
- author: "Shaarli",
- version: "1.0.0",
-
- iconURL: baseURL + "/images/favicon.ico",
- icon32URL: baseURL + "/images/favicon.ico",
- icon64URL: baseURL + "/images/favicon.ico",
-
- shareURL: baseURL + "{noparse}?post=%{url}&title=%{title}&description=%{text}&source=firefoxsocialapi{/noparse}",
- homepageURL: baseURL
- };
- node.setAttribute("data-service", JSON.stringify(data));
-
- var activate = new CustomEvent("ActivateSocialFeature");
- node.dispatchEvent(activate);
- }
- {/if}
- function alertBookmarklet() {
- alert({"'Drag this link to your bookmarks toolbar, or right-click it and choose Bookmark This Link'"|t});
- return false;
- }
-</script>
+<input type="hidden" id="bookmarklet-alert"
+ value="{'Drag this link to your bookmarks toolbar, or right-click it and choose Bookmark This Link'|t}">
</body>
</html>