Compare commits
21 commits
master
...
paste-mana
Author | SHA1 | Date | |
---|---|---|---|
|
f66c690fc0 | ||
|
c37052fe82 | ||
|
7e30134d1f | ||
|
2b67f0369d | ||
|
b2751ca444 | ||
|
ddfccb338c | ||
|
85943a9ce1 | ||
|
2b9abb8db4 | ||
|
e531592b05 | ||
|
d027cb188d | ||
|
4e3348795c | ||
|
2d9666c5ef | ||
|
521f134638 | ||
|
ea93c474ea | ||
|
af02731002 | ||
|
8b170397a9 | ||
|
ac32826009 | ||
|
c4830044f7 | ||
|
daae0d0dca | ||
|
283d85c7cf | ||
|
8537f396b3 |
13 changed files with 467 additions and 111 deletions
1
.gitattributes
vendored
1
.gitattributes
vendored
|
@ -20,6 +20,7 @@ js/test/ export-ignore
|
|||
.travis.yml export-ignore
|
||||
composer.json export-ignore
|
||||
composer.lock export-ignore
|
||||
crowdin.yml export-ignore
|
||||
BADGES.md export-ignore
|
||||
CODE_OF_CONDUCT.md export-ignore
|
||||
Makefile export-ignore
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
# PrivateBin version history
|
||||
|
||||
* **1.4 (not yet released)**
|
||||
* CHANGED: Upgrading libraries to: DOMpurify 2.0.14
|
||||
* ADDED: Translation for Hebrew
|
||||
* CHANGED: Upgrading libraries to: DOMpurify 2.1.1
|
||||
* **1.3.4 (2020-03-22)**
|
||||
* CHANGED: Minimum required PHP version is 5.6, due to a change in the identicon library and to use php's native hash_equals()
|
||||
* CHANGED: Upgrading libraries to: identicon 2.0.0
|
||||
|
|
|
@ -45,4 +45,5 @@ Sébastien Sauvage - original idea and main developer
|
|||
* Péter Tabajdi - Hungarian
|
||||
* info-path - Czech
|
||||
* BigWax - Bulgarian
|
||||
* AndriiZ - Ukrainian
|
||||
* AndriiZ - Ukrainian
|
||||
* Yaron Shahrabani - Hebrew
|
||||
|
|
|
@ -205,3 +205,43 @@ li.L0, li.L1, li.L2, li.L3, li.L5, li.L6, li.L7, li.L8 {
|
|||
.modal .modal-content button {
|
||||
margin: 0.5em 0;
|
||||
}
|
||||
|
||||
/* sidebar */
|
||||
#menu-toggle {
|
||||
position: relative;
|
||||
bottom: -94px;
|
||||
left: -61px;
|
||||
margin-bottom: -12px;
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
#menu-toggle {
|
||||
position: static;
|
||||
margin-bottom: 1em;
|
||||
transform: rotate(0);
|
||||
}
|
||||
}
|
||||
|
||||
main {
|
||||
transition: all 0.5s ease;
|
||||
}
|
||||
|
||||
main.toggled {
|
||||
padding-left: 450px;
|
||||
}
|
||||
|
||||
#sidebar-wrapper {
|
||||
position: fixed;
|
||||
width: 450px;
|
||||
height: 100%;
|
||||
left: -450px;
|
||||
padding-left: 4ch;
|
||||
overflow-y: scroll;
|
||||
overflow-x: hidden;
|
||||
transition: all 0.5s ease;
|
||||
}
|
||||
|
||||
main.toggled #sidebar-wrapper {
|
||||
left: 0;
|
||||
}
|
||||
|
|
|
@ -8,6 +8,8 @@ global.cleanup = global.jsdom();
|
|||
global.URL = require('jsdom-url').URL;
|
||||
global.fs = require('fs');
|
||||
global.WebCrypto = require('@peculiar/webcrypto').Crypto;
|
||||
require('fake-indexeddb/auto');
|
||||
global.FDBFactory = require('fake-indexeddb/lib/FDBFactory');
|
||||
|
||||
// application libraries to test
|
||||
global.$ = global.jQuery = require('./jquery-3.4.1');
|
||||
|
@ -17,7 +19,7 @@ require('./prettify');
|
|||
global.prettyPrint = window.PR.prettyPrint;
|
||||
global.prettyPrintOne = window.PR.prettyPrintOne;
|
||||
global.showdown = require('./showdown-1.9.1');
|
||||
global.DOMPurify = require('./purify-2.0.14');
|
||||
global.DOMPurify = require('./purify-2.1.1');
|
||||
global.baseX = require('./base-x-3.0.7').baseX;
|
||||
global.Legacy = require('./legacy').Legacy;
|
||||
require('./bootstrap-3.3.7');
|
||||
|
|
|
@ -8,11 +8,12 @@
|
|||
},
|
||||
"dependencies": {},
|
||||
"devDependencies": {
|
||||
"@peculiar/webcrypto": "^1.1.1",
|
||||
"fake-indexeddb": "^3.0.2",
|
||||
"jsdom": "^9.12.0",
|
||||
"jsdom-global": "^2.1.1",
|
||||
"jsdom-url": "^2.2.1",
|
||||
"jsverify": "^0.8.3",
|
||||
"@peculiar/webcrypto": "^1.1.1"
|
||||
"jsverify": "^0.8.3"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "mocha"
|
||||
|
|
363
js/privatebin.js
363
js/privatebin.js
|
@ -12,9 +12,9 @@
|
|||
*/
|
||||
|
||||
// global Base64, DOMPurify, FileReader, RawDeflate, history, navigator, prettyPrint, prettyPrintOne, showdown, kjua
|
||||
'use strict';
|
||||
|
||||
jQuery.fn.draghover = function() {
|
||||
'use strict';
|
||||
return this.each(function() {
|
||||
let collection = $(),
|
||||
self = $(this);
|
||||
|
@ -37,14 +37,11 @@ jQuery.fn.draghover = function() {
|
|||
|
||||
// main application start, called when DOM is fully loaded
|
||||
jQuery(document).ready(function() {
|
||||
'use strict';
|
||||
// run main controller
|
||||
$.PrivateBin.Controller.init();
|
||||
});
|
||||
|
||||
jQuery.PrivateBin = (function($, RawDeflate) {
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* zlib library interface
|
||||
*
|
||||
|
@ -565,10 +562,11 @@ jQuery.PrivateBin = (function($, RawDeflate) {
|
|||
*
|
||||
* @name Helper.reset
|
||||
* @function
|
||||
* @param {string} uri - (optional) URI to reset to
|
||||
*/
|
||||
me.reset = function()
|
||||
me.reset = function(uri)
|
||||
{
|
||||
baseUri = null;
|
||||
baseUri = typeof uri === 'string' ? uri : null;
|
||||
};
|
||||
|
||||
return me;
|
||||
|
@ -3521,6 +3519,7 @@ jQuery.PrivateBin = (function($, RawDeflate) {
|
|||
$emailLink,
|
||||
$sendButton,
|
||||
$retryButton,
|
||||
$rememberButton,
|
||||
pasteExpiration = null,
|
||||
retryButtonCallback;
|
||||
|
||||
|
@ -3885,6 +3884,7 @@ jQuery.PrivateBin = (function($, RawDeflate) {
|
|||
$cloneButton.removeClass('hidden');
|
||||
$rawTextButton.removeClass('hidden');
|
||||
$qrCodeLink.removeClass('hidden');
|
||||
$rememberButton.removeClass('hidden');
|
||||
|
||||
viewButtonsDisplayed = true;
|
||||
};
|
||||
|
@ -3905,6 +3905,7 @@ jQuery.PrivateBin = (function($, RawDeflate) {
|
|||
$newButton.addClass('hidden');
|
||||
$rawTextButton.addClass('hidden');
|
||||
$qrCodeLink.addClass('hidden');
|
||||
$rememberButton.addClass('hidden');
|
||||
me.hideEmailButton();
|
||||
|
||||
viewButtonsDisplayed = false;
|
||||
|
@ -3970,17 +3971,6 @@ jQuery.PrivateBin = (function($, RawDeflate) {
|
|||
createButtonsDisplayed = false;
|
||||
};
|
||||
|
||||
/**
|
||||
* only shows the "new paste" button
|
||||
*
|
||||
* @name TopNav.showNewPasteButton
|
||||
* @function
|
||||
*/
|
||||
me.showNewPasteButton = function()
|
||||
{
|
||||
$newButton.removeClass('hidden');
|
||||
};
|
||||
|
||||
/**
|
||||
* only shows the "retry" button
|
||||
*
|
||||
|
@ -4043,17 +4033,6 @@ jQuery.PrivateBin = (function($, RawDeflate) {
|
|||
$emailLink.off('click.sendEmail');
|
||||
}
|
||||
|
||||
/**
|
||||
* only hides the clone button
|
||||
*
|
||||
* @name TopNav.hideCloneButton
|
||||
* @function
|
||||
*/
|
||||
me.hideCloneButton = function()
|
||||
{
|
||||
$cloneButton.addClass('hidden');
|
||||
};
|
||||
|
||||
/**
|
||||
* only hides the raw text button
|
||||
*
|
||||
|
@ -4065,17 +4044,6 @@ jQuery.PrivateBin = (function($, RawDeflate) {
|
|||
$rawTextButton.addClass('hidden');
|
||||
};
|
||||
|
||||
/**
|
||||
* only hides the qr code button
|
||||
*
|
||||
* @name TopNav.hideQrCodeButton
|
||||
* @function
|
||||
*/
|
||||
me.hideQrCodeButton = function()
|
||||
{
|
||||
$qrCodeLink.addClass('hidden');
|
||||
}
|
||||
|
||||
/**
|
||||
* hide all irrelevant buttons when viewing burn after reading paste
|
||||
*
|
||||
|
@ -4084,8 +4052,8 @@ jQuery.PrivateBin = (function($, RawDeflate) {
|
|||
*/
|
||||
me.hideBurnAfterReadingButtons = function()
|
||||
{
|
||||
me.hideCloneButton();
|
||||
me.hideQrCodeButton();
|
||||
$cloneButton.addClass('hidden');
|
||||
$qrCodeLink.addClass('hidden');
|
||||
me.hideEmailButton();
|
||||
}
|
||||
|
||||
|
@ -4325,6 +4293,7 @@ jQuery.PrivateBin = (function($, RawDeflate) {
|
|||
$sendButton = $('#sendbutton');
|
||||
$qrCodeLink = $('#qrcodelink');
|
||||
$emailLink = $('#emaillink');
|
||||
$rememberButton = $('#rememberbutton');
|
||||
|
||||
// bootstrap template drop down
|
||||
$('#language ul.dropdown-menu li a').click(setLanguage);
|
||||
|
@ -4360,6 +4329,312 @@ jQuery.PrivateBin = (function($, RawDeflate) {
|
|||
return me;
|
||||
})(window, document);
|
||||
|
||||
/**
|
||||
* (view) Handles the memory, storing paste URLs in the browser
|
||||
*
|
||||
* @name Memory
|
||||
* @class
|
||||
*/
|
||||
const Memory = (function (document, window) {
|
||||
const me = {};
|
||||
|
||||
let pastes = [],
|
||||
db;
|
||||
|
||||
/**
|
||||
* checks if the given URL was already memorized
|
||||
*
|
||||
* @name Memory.isInMemory
|
||||
* @private
|
||||
* @function
|
||||
* @param {string} pasteUrl
|
||||
* @return {bool}
|
||||
*/
|
||||
function isInMemory(pasteUrl)
|
||||
{
|
||||
return pastes.filter(
|
||||
function(memorizedPaste) {
|
||||
return memorizedPaste.url === pasteUrl;
|
||||
}
|
||||
).length > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* sort the memory list by the given column offset
|
||||
*
|
||||
* @name Memory.sort
|
||||
* @private
|
||||
* @function
|
||||
* @param {int} column
|
||||
*/
|
||||
function sort(column)
|
||||
{
|
||||
let i, x, y, shouldSwitch, dir, switchcount = 0;
|
||||
let table = $('#sidebar-wrapper table')[0];
|
||||
let header = $('#sidebar-wrapper table thead tr th')[column];
|
||||
let switching = true;
|
||||
if (header.dataset.direction == 'asc') {
|
||||
header.dataset.direction = 'desc';
|
||||
} else {
|
||||
header.dataset.direction = 'asc';
|
||||
}
|
||||
while (switching) {
|
||||
switching = false;
|
||||
let rows = table.rows;
|
||||
// skip first row, containing headers
|
||||
for (i = 1; i < (rows.length - 1); i++) {
|
||||
shouldSwitch = false;
|
||||
x = rows[i].getElementsByTagName("td")[column];
|
||||
y = rows[i + 1].getElementsByTagName("td")[column];
|
||||
if (header.dataset.direction == 'asc') {
|
||||
if (x.innerHTML.toLowerCase() > y.innerHTML.toLowerCase()) {
|
||||
shouldSwitch = true;
|
||||
break;
|
||||
}
|
||||
} else if (header.dataset.direction == 'desc') {
|
||||
if (x.innerHTML.toLowerCase() < y.innerHTML.toLowerCase()) {
|
||||
shouldSwitch = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (shouldSwitch) {
|
||||
rows[i].parentNode.insertBefore(rows[i + 1], rows[i]);
|
||||
switching = true;
|
||||
switchcount ++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* called after successfully connecting to the indexedDB
|
||||
*
|
||||
* @name Memory.updateCacheFromDb
|
||||
* @private
|
||||
* @function
|
||||
*/
|
||||
function updateCacheFromDb()
|
||||
{
|
||||
const memory = db.transaction('pastes').objectStore('pastes');
|
||||
memory.openCursor().onsuccess = function(e) {
|
||||
let cursor = e.target.result;
|
||||
if (cursor) {
|
||||
pastes.push(cursor.value);
|
||||
cursor.continue();
|
||||
} else {
|
||||
me.refreshList();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* adds a paste URL to the memory
|
||||
*
|
||||
* @name Memory.add
|
||||
* @function
|
||||
* @param {string} pasteUrl
|
||||
* @return {bool}
|
||||
*/
|
||||
me.add = function(pasteUrl)
|
||||
{
|
||||
const url = new URL(pasteUrl),
|
||||
newPaste = {
|
||||
'https': url.protocol == 'https:',
|
||||
'service': url.hostname + (url.pathname.length > 1 ? url.pathname : ''),
|
||||
'pasteid': url.search.replace(/^\?+/, ''),
|
||||
'key': url.hash.replace(/^#+/, ''),
|
||||
// we store the full URL as it may contain additonal information
|
||||
// required to open the paste, like port, username and password
|
||||
'url': pasteUrl
|
||||
};
|
||||
// don't add an already memorized paste
|
||||
if (isInMemory(pasteUrl)) {
|
||||
return false;
|
||||
}
|
||||
pastes.push(newPaste);
|
||||
if (!window.indexedDB || !db) {
|
||||
return false;
|
||||
}
|
||||
const memory = db.transaction('pastes', 'readwrite').objectStore('pastes'),
|
||||
request = memory.add(newPaste);
|
||||
request.onsuccess = function(e) {
|
||||
me.refreshList();
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
/**
|
||||
* open a given paste URL using the current instance
|
||||
*
|
||||
* @name Memory.open
|
||||
* @function
|
||||
* @param {string} pasteUrl
|
||||
*/
|
||||
me.open = function(pasteUrl)
|
||||
{
|
||||
// parse URL
|
||||
const url = new URL(pasteUrl);
|
||||
const baseUri = Helper.baseUri();
|
||||
history.pushState({type: 'viewpaste'}, document.title, url.search + url.hash);
|
||||
Helper.reset(url.origin);
|
||||
Model.reset();
|
||||
PasteDecrypter.run();
|
||||
Helper.reset(baseUri);
|
||||
};
|
||||
|
||||
/**
|
||||
* refresh the state of the remember button
|
||||
*
|
||||
* @name Memory.refreshButton
|
||||
* @function
|
||||
*/
|
||||
me.refreshButton = function()
|
||||
{
|
||||
if (isInMemory(window.location.href)) {
|
||||
$('#rememberbutton').addClass('disabled');
|
||||
} else {
|
||||
$('#rememberbutton').removeClass('disabled');
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* refresh the displayed list from memory
|
||||
*
|
||||
* @name Memory.refreshList
|
||||
* @function
|
||||
*/
|
||||
me.refreshList = function()
|
||||
{
|
||||
me.refreshButton();
|
||||
const $tbody = $('#sidebar-wrapper table tbody')[0];
|
||||
if (!$tbody) {
|
||||
return;
|
||||
}
|
||||
$tbody.textContent = '';
|
||||
pastes.forEach(function(paste) {
|
||||
const row = document.createElement('tr');
|
||||
|
||||
let cell = document.createElement('td');
|
||||
let input = document.createElement('input');
|
||||
input.setAttribute('type', 'checkbox');
|
||||
input.setAttribute('name', 'memoryselect');
|
||||
input.setAttribute('value', paste.pasteid);
|
||||
cell.appendChild(input);
|
||||
row.appendChild(cell);
|
||||
|
||||
cell = document.createElement('td');
|
||||
cell.textContent = paste.https ? '✔' : '✘';
|
||||
row.appendChild(cell);
|
||||
|
||||
cell = document.createElement('td');
|
||||
cell.textContent = paste.service;
|
||||
row.appendChild(cell);
|
||||
|
||||
cell = document.createElement('td');
|
||||
cell.textContent = paste.pasteid;
|
||||
row.appendChild(cell);
|
||||
|
||||
cell = document.createElement('td');
|
||||
cell.addEventListener('click', function () {
|
||||
navigator.clipboard.writeText(paste.url);
|
||||
});
|
||||
let button = document.createElement('button');
|
||||
button.setAttribute('class', 'btn btn-info btn-xs');
|
||||
button.setAttribute('title', I18n._('Copy paste URL'));
|
||||
let span = document.createElement('span');
|
||||
span.setAttribute('class', 'glyphicon glyphicon-duplicate');
|
||||
span.setAttribute('aria-hidden', 'true');
|
||||
button.appendChild(span);
|
||||
cell.appendChild(button);
|
||||
row.appendChild(cell);
|
||||
|
||||
$tbody.appendChild(row);
|
||||
row.addEventListener('click', function () {
|
||||
me.open(paste.url);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* initiate
|
||||
*
|
||||
* attaches click event to toggle memory sidepanel
|
||||
*
|
||||
* @name Memory.init
|
||||
* @function
|
||||
*/
|
||||
me.init = function()
|
||||
{
|
||||
pastes = [];
|
||||
if (!window.indexedDB) {
|
||||
$('#menu-toggle').hide();
|
||||
return;
|
||||
}
|
||||
const request = window.indexedDB.open('privatebin', 1);
|
||||
request.onerror = function(e) {
|
||||
Alert.showWarning('Unable to open indexedDB, memory will not be available in this session.');
|
||||
$('#menu-toggle').hide();
|
||||
};
|
||||
request.onupgradeneeded = function(event) {
|
||||
const newDb = event.target.result;
|
||||
const objectStore = newDb.createObjectStore('pastes', {keyPath: 'pasteid'});
|
||||
objectStore.createIndex('https', 'https', {unique: false});
|
||||
objectStore.createIndex('service', 'service', {unique: false});
|
||||
objectStore.createIndex('pasteid', 'pasteid', {unique: true});
|
||||
objectStore.transaction.oncomplete = function(e) {
|
||||
db = newDb;
|
||||
updateCacheFromDb();
|
||||
};
|
||||
};
|
||||
request.onsuccess = function(e) {
|
||||
db = request.result;
|
||||
db.onerror = function(e) {
|
||||
Alert.showError(e.target.error.message);
|
||||
}
|
||||
updateCacheFromDb();
|
||||
};
|
||||
|
||||
$('#forgetbutton').on('click', function(e) {
|
||||
const memory = db.transaction('pastes', 'readwrite').objectStore('pastes');
|
||||
for (const checkbox of document.getElementsByName('memoryselect')) {
|
||||
if (checkbox.checked) {
|
||||
const request = memory.delete(checkbox.value);
|
||||
request.onsuccess = function(e) {
|
||||
pastes = [];
|
||||
$('#sidebar-wrapper table tbody').empty();
|
||||
updateCacheFromDb();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
$('#memoryselectall').on('click', function(e) {
|
||||
const checkedState = document.getElementById('memoryselectall').checked;
|
||||
for (const checkbox of document.getElementsByName('memoryselect')) {
|
||||
checkbox.checked = checkedState;
|
||||
}
|
||||
});
|
||||
|
||||
$('#menu-toggle').on('click', function(e) {
|
||||
$('main').toggleClass('toggled');
|
||||
$('#menu-toggle .glyphicon').toggleClass('glyphicon glyphicon-menu-down glyphicon glyphicon-menu-up')
|
||||
});
|
||||
|
||||
$('#rememberbutton').on('click', function(e) {
|
||||
if (me.add(window.location.href))
|
||||
$('#menu-toggle').click();
|
||||
});
|
||||
const headers = $('#sidebar-wrapper table thead tr th');
|
||||
for (let i = 1; i < headers.length; i++) {
|
||||
headers[i].addEventListener('click', function(e) {
|
||||
sort(i);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
return me;
|
||||
})(document, window);
|
||||
|
||||
/**
|
||||
* Responsible for AJAX requests, transparently handles encryption…
|
||||
*
|
||||
|
@ -4667,6 +4942,7 @@ jQuery.PrivateBin = (function($, RawDeflate) {
|
|||
|
||||
// show new URL in browser bar
|
||||
history.pushState({type: 'newpaste'}, document.title, url);
|
||||
Memory.refreshButton();
|
||||
|
||||
TopNav.showViewButtons();
|
||||
|
||||
|
@ -5337,8 +5613,7 @@ jQuery.PrivateBin = (function($, RawDeflate) {
|
|||
I18n.loadTranslations();
|
||||
|
||||
DOMPurify.setConfig({
|
||||
ALLOWED_URI_REGEXP: /^(?:(?:(?:f|ht)tps?|mailto|magnet):)/i,
|
||||
SAFE_FOR_JQUERY: true
|
||||
ALLOWED_URI_REGEXP: /^(?:(?:(?:f|ht)tps?|mailto|magnet):)/i
|
||||
});
|
||||
|
||||
// Add a hook to make all links open a new window
|
||||
|
@ -5376,6 +5651,7 @@ jQuery.PrivateBin = (function($, RawDeflate) {
|
|||
Prompt.init();
|
||||
TopNav.init();
|
||||
UiHelper.init();
|
||||
Memory.init();
|
||||
|
||||
// check for legacy browsers before going any further
|
||||
if (!Legacy.Check.getInit()) {
|
||||
|
@ -5429,6 +5705,7 @@ jQuery.PrivateBin = (function($, RawDeflate) {
|
|||
AttachmentViewer: AttachmentViewer,
|
||||
DiscussionViewer: DiscussionViewer,
|
||||
TopNav: TopNav,
|
||||
Memory: Memory,
|
||||
ServerInteraction: ServerInteraction,
|
||||
PasteEncrypter: PasteEncrypter,
|
||||
PasteDecrypter: PasteDecrypter,
|
||||
|
|
File diff suppressed because one or more lines are too long
2
js/purify-2.1.1.js
Normal file
2
js/purify-2.1.1.js
Normal file
File diff suppressed because one or more lines are too long
60
js/test/Memory.js
Normal file
60
js/test/Memory.js
Normal file
|
@ -0,0 +1,60 @@
|
|||
'use strict';
|
||||
const common = require('../common');
|
||||
|
||||
describe('Memory', function () {
|
||||
describe('add & refreshList', function () {
|
||||
this.timeout(30000);
|
||||
|
||||
jsc.property(
|
||||
'allows adding valid paste URLs',
|
||||
common.jscSchemas(),
|
||||
jsc.nearray(common.jscA2zString()),
|
||||
jsc.array(common.jscQueryString()),
|
||||
'string',
|
||||
function (schema, address, query, fragment) {
|
||||
const expectedQuery = encodeURI(
|
||||
query.join('').replace(/^&+|&+$/gm,'')
|
||||
),
|
||||
expected = schema + '://' + address.join('') + '/?' +
|
||||
expectedQuery + '#' + fragment,
|
||||
clean = jsdom();
|
||||
$('body').html(
|
||||
'<main><div id="sidebar-wrapper"><table><tbody>' +
|
||||
'</tbody></table></div></main>'
|
||||
);
|
||||
// clear cache, then the first cell will match what we add
|
||||
$.PrivateBin.Memory.init();
|
||||
$.PrivateBin.Memory.add(expected);
|
||||
$.PrivateBin.Memory.refreshList();
|
||||
const result = $('#sidebar-wrapper table tbody tr td')[3].textContent;
|
||||
clean();
|
||||
return result === expectedQuery;
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
describe('init', function () {
|
||||
it(
|
||||
'enables toggling the memory sidebar',
|
||||
function() {
|
||||
$('body').html(
|
||||
'<main><div id="sidebar-wrapper"></div>' +
|
||||
'<button id="menu-toggle"></button></main>'
|
||||
);
|
||||
assert.ok(!$('main').hasClass('toggled'));
|
||||
|
||||
$('#menu-toggle').click();
|
||||
assert.ok(!$('main').hasClass('toggled'));
|
||||
|
||||
$.PrivateBin.Memory.init();
|
||||
assert.ok(!$('main').hasClass('toggled'));
|
||||
|
||||
$('#menu-toggle').click();
|
||||
assert.ok($('main').hasClass('toggled'));
|
||||
|
||||
$('#menu-toggle').click();
|
||||
assert.ok(!$('main').hasClass('toggled'));
|
||||
}
|
||||
);
|
||||
});
|
||||
});
|
|
@ -118,62 +118,6 @@ describe('TopNav', function () {
|
|||
);
|
||||
});
|
||||
|
||||
describe('showNewPasteButton', function () {
|
||||
before(function () {
|
||||
cleanup();
|
||||
});
|
||||
|
||||
it(
|
||||
'displays the button for creating a paste',
|
||||
function () {
|
||||
var results = [];
|
||||
$('body').html(
|
||||
'<nav><div id="navbar"><ul><li><button id="newbutton" type=' +
|
||||
'"button" class="hidden">New</button></li></ul></div></nav>'
|
||||
);
|
||||
$.PrivateBin.TopNav.init();
|
||||
results.push(
|
||||
$('#newbutton').hasClass('hidden')
|
||||
);
|
||||
$.PrivateBin.TopNav.showNewPasteButton();
|
||||
results.push(
|
||||
!$('#newbutton').hasClass('hidden')
|
||||
);
|
||||
cleanup();
|
||||
assert.ok(results.every(element => element));
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
describe('hideCloneButton', function () {
|
||||
before(function () {
|
||||
cleanup();
|
||||
});
|
||||
|
||||
it(
|
||||
'hides the button for cloning a paste',
|
||||
function () {
|
||||
var results = [];
|
||||
$('body').html(
|
||||
'<nav><div id="navbar"><ul><li><button id="clonebutton" ' +
|
||||
'type="button" class="btn btn-warning navbar-btn">' +
|
||||
'<span class="glyphicon glyphicon-duplicate" aria-hidden=' +
|
||||
'"true"></span> Clone</button></li></ul></div></nav>'
|
||||
);
|
||||
$.PrivateBin.TopNav.init();
|
||||
results.push(
|
||||
!$('#clonebutton').hasClass('hidden')
|
||||
);
|
||||
$.PrivateBin.TopNav.hideCloneButton();
|
||||
results.push(
|
||||
$('#clonebutton').hasClass('hidden')
|
||||
);
|
||||
cleanup();
|
||||
assert.ok(results.every(element => element));
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
describe('hideRawButton', function () {
|
||||
before(function () {
|
||||
cleanup();
|
||||
|
|
|
@ -70,9 +70,9 @@ if ($MARKDOWN) :
|
|||
<?php
|
||||
endif;
|
||||
?>
|
||||
<script type="text/javascript" data-cfasync="false" src="js/purify-2.0.14.js" integrity="sha512-kbLhjIj/m/AW++o2eErCfqPueoX2btJo7VznhEC2YQRbVR/+Eup3w7thwDZwoCZ/gLrPxTX3W4H2KzupLg2PKA==" crossorigin="anonymous"></script>
|
||||
<script type="text/javascript" data-cfasync="false" src="js/purify-2.1.1.js" integrity="sha512-0RqB620aQhcT40T4kxf/vx3J4DOmFsqcGu2mPha21ZqufRsth3MsiU35ffSHX0OIJbE92XSKyvNcL1I6sYhh4w==" crossorigin="anonymous"></script>
|
||||
<script type="text/javascript" data-cfasync="false" src="js/legacy.js?<?php echo rawurlencode($VERSION); ?>" integrity="sha512-LYos+qXHIRqFf5ZPNphvtTB0cgzHUizu2wwcOwcwz/VIpRv9lpcBgPYz4uq6jx0INwCAj6Fbnl5HoKiLufS2jg==" crossorigin="anonymous"></script>
|
||||
<script type="text/javascript" data-cfasync="false" src="js/privatebin.js?<?php echo rawurlencode($VERSION); ?>" integrity="sha512-GCiSgkYlcyJq3SOMOAh52rIlUAoGH8yDJzOm/NkzBorbk2qiBSjc289/RxpeZJcdu36fQObFTzLvz4Do/2LFsA==" crossorigin="anonymous"></script>
|
||||
<script type="text/javascript" data-cfasync="false" src="js/privatebin.js?<?php echo rawurlencode($VERSION); ?>" integrity="sha512-9cJdKFvcsrk3G411+Wp5Y6ZvFE6UUMKVzCB6LLXhg1BaN/jkviL01Ox+4HzbYNflFuSYK0USVFLeCW89774A6w==" crossorigin="anonymous"></script>
|
||||
<!-- icon -->
|
||||
<link rel="apple-touch-icon" href="<?php echo I18n::encode($BASEPATH); ?>img/apple-touch-icon.png" sizes="180x180" />
|
||||
<link rel="icon" type="image/png" href="img/favicon-32x32.png" sizes="32x32" />
|
||||
|
@ -224,6 +224,9 @@ if ($QRCODE) :
|
|||
<?php
|
||||
endif;
|
||||
?>
|
||||
<button id="rememberbutton" type="button" class="hidden btn btn-<?php echo $isDark ? 'warning' : 'default'; ?> navbar-btn">
|
||||
<span class="glyphicon glyphicon-star" aria-hidden="true"></span> <?php echo I18n::_('Remember'), PHP_EOL; ?>
|
||||
</button>
|
||||
</li>
|
||||
<li class="dropdown">
|
||||
<select id="pasteExpiration" name="pasteExpiration" class="hidden">
|
||||
|
@ -447,6 +450,28 @@ if ($isCpct) :
|
|||
endif;
|
||||
?></nav>
|
||||
<main>
|
||||
<div id="sidebar-wrapper">
|
||||
<h4><?php echo I18n::_('Memory'); ?></h4>
|
||||
<p><?php echo I18n::_('The memory lets you remember different paste links. The memory is unique to each website and device.'); ?></p>
|
||||
<table class="table<?php echo $isDark ? '' : ' table-striped'; ?>">
|
||||
<thead>
|
||||
<tr>
|
||||
<th title="<?php echo I18n::_('Select all'); ?>"><input type="checkbox" id="memoryselectall" /></th>
|
||||
<th title="<?php echo I18n::_('HTTPS'); ?>">🔒</th>
|
||||
<th><?php echo I18n::_('Service'); ?></th>
|
||||
<th><?php echo I18n::_('ID'); ?></th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
</tbody>
|
||||
</table>
|
||||
<p>
|
||||
<button id="forgetbutton" type="button" class="btn btn-danger navbar-btn">
|
||||
<span class="glyphicon glyphicon-trash" aria-hidden="true"></span> <?php echo I18n::_('Forget'), PHP_EOL; ?>
|
||||
</button>
|
||||
</p>
|
||||
</div>
|
||||
<section class="container">
|
||||
<?php
|
||||
if (strlen($NOTICE)) :
|
||||
|
@ -466,7 +491,7 @@ if ($FILEUPLOAD) :
|
|||
?>
|
||||
<div id="attachment" role="alert" class="hidden alert alert-info">
|
||||
<span class="glyphicon glyphicon-download-alt" aria-hidden="true"></span>
|
||||
<a class="alert-link"><?php echo I18n::_('Download attachment'), PHP_EOL; ?></a>
|
||||
<a class="alert-link"><?php echo I18n::_('Download attachment'); ?></a>
|
||||
</div>
|
||||
<?php
|
||||
endif;
|
||||
|
@ -530,6 +555,10 @@ if (strlen($URLSHORTENER)) :
|
|||
endif;
|
||||
?>
|
||||
</div>
|
||||
<button id="menu-toggle" class="btn btn-<?php echo $isDark ? 'warning' : 'default'; ?> btn-xs">
|
||||
<span class="glyphicon glyphicon-menu-up" aria-hidden="true"></span>
|
||||
<?php echo I18n::_('Memory'); ?>
|
||||
</button>
|
||||
<ul id="editorTabs" class="nav nav-tabs hidden">
|
||||
<li role="presentation" class="active"><a role="tab" aria-selected="true" aria-controls="editorTabs" id="messageedit" href="#"><?php echo I18n::_('Editor'); ?></a></li>
|
||||
<li role="presentation"><a role="tab" aria-selected="false" aria-controls="editorTabs" id="messagepreview" href="#"><?php echo I18n::_('Preview'); ?></a></li>
|
||||
|
|
|
@ -48,9 +48,9 @@ if ($MARKDOWN):
|
|||
<?php
|
||||
endif;
|
||||
?>
|
||||
<script type="text/javascript" data-cfasync="false" src="js/purify-2.0.14.js" integrity="sha512-kbLhjIj/m/AW++o2eErCfqPueoX2btJo7VznhEC2YQRbVR/+Eup3w7thwDZwoCZ/gLrPxTX3W4H2KzupLg2PKA==" crossorigin="anonymous"></script>
|
||||
<script type="text/javascript" data-cfasync="false" src="js/purify-2.1.1.js" integrity="sha512-0RqB620aQhcT40T4kxf/vx3J4DOmFsqcGu2mPha21ZqufRsth3MsiU35ffSHX0OIJbE92XSKyvNcL1I6sYhh4w==" crossorigin="anonymous"></script>
|
||||
<script type="text/javascript" data-cfasync="false" src="js/legacy.js?<?php echo rawurlencode($VERSION); ?>" integrity="sha512-LYos+qXHIRqFf5ZPNphvtTB0cgzHUizu2wwcOwcwz/VIpRv9lpcBgPYz4uq6jx0INwCAj6Fbnl5HoKiLufS2jg==" crossorigin="anonymous"></script>
|
||||
<script type="text/javascript" data-cfasync="false" src="js/privatebin.js?<?php echo rawurlencode($VERSION); ?>" integrity="sha512-GCiSgkYlcyJq3SOMOAh52rIlUAoGH8yDJzOm/NkzBorbk2qiBSjc289/RxpeZJcdu36fQObFTzLvz4Do/2LFsA==" crossorigin="anonymous"></script>
|
||||
<script type="text/javascript" data-cfasync="false" src="js/privatebin.js?<?php echo rawurlencode($VERSION); ?>" integrity="sha512-9cJdKFvcsrk3G411+Wp5Y6ZvFE6UUMKVzCB6LLXhg1BaN/jkviL01Ox+4HzbYNflFuSYK0USVFLeCW89774A6w==" crossorigin="anonymous"></script>
|
||||
<!-- icon -->
|
||||
<link rel="apple-touch-icon" href="img/apple-touch-icon.png?<?php echo rawurlencode($VERSION); ?>" sizes="180x180" />
|
||||
<link rel="icon" type="image/png" href="img/favicon-32x32.png?<?php echo rawurlencode($VERSION); ?>" sizes="32x32" />
|
||||
|
|
Loading…
Reference in a new issue