From 30b47d39f358e07a296e535223d576f7d59ea4bd Mon Sep 17 00:00:00 2001
From: Peter Jaszkowiak
Date: Wed, 19 Apr 2017 20:33:03 -0600
Subject: [PATCH 1/2] Fix #5536
---
public/src/app.js | 23 +++++-----
public/src/client/category.js | 11 ++---
public/src/client/search.js | 6 +--
public/src/client/topic.js | 11 ++---
public/src/modules/search.js | 4 +-
public/src/modules/sounds.js | 8 ++--
public/src/modules/storage.js | 83 +++++++++++++++++++++++++++++++++++
src/meta/js.js | 1 +
8 files changed, 117 insertions(+), 30 deletions(-)
create mode 100644 public/src/modules/storage.js
diff --git a/public/src/app.js b/public/src/app.js
index 3e53305aac..b5b5302bd2 100644
--- a/public/src/app.js
+++ b/public/src/app.js
@@ -630,16 +630,17 @@ app.cacheBuster = null;
};
app.showCookieWarning = function () {
- if (!config.cookies.enabled || !navigator.cookieEnabled) {
- // Skip warning if cookie consent subsystem disabled (obviously), or cookies not in use
- return;
- } else if (window.location.pathname.startsWith(config.relative_path + '/admin')) {
- // No need to show cookie consent warning in ACP
- return;
- } else if (window.localStorage.getItem('cookieconsent') === '1') {
- return;
- }
- require(['translator'], function (translator) {
+ require(['translator', 'storage'], function (translator, storage) {
+ if (!config.cookies.enabled || !navigator.cookieEnabled) {
+ // Skip warning if cookie consent subsystem disabled (obviously), or cookies not in use
+ return;
+ } else if (window.location.pathname.startsWith(config.relative_path + '/admin')) {
+ // No need to show cookie consent warning in ACP
+ return;
+ } else if (storage.getItem('cookieconsent') === '1') {
+ return;
+ }
+
config.cookies.message = translator.unescape(config.cookies.message);
config.cookies.dismiss = translator.unescape(config.cookies.dismiss);
config.cookies.link = translator.unescape(config.cookies.link);
@@ -651,7 +652,7 @@ app.cacheBuster = null;
var dismissEl = warningEl.find('button');
dismissEl.on('click', function () {
// Save consent cookie and remove warning element
- window.localStorage.setItem('cookieconsent', '1');
+ storage.setItem('cookieconsent', '1');
warningEl.remove();
});
});
diff --git a/public/src/client/category.js b/public/src/client/category.js
index e2dfcd9924..7fea7cfbcb 100644
--- a/public/src/client/category.js
+++ b/public/src/client/category.js
@@ -11,7 +11,8 @@ define('forum/category', [
'translator',
'topicSelect',
'forum/pagination',
-], function (infinitescroll, share, navigator, categoryTools, sort, components, translator, topicSelect, pagination) {
+ 'storage',
+], function (infinitescroll, share, navigator, categoryTools, sort, components, translator, topicSelect, pagination, storage) {
var Category = {};
$(window).on('action:ajaxify.start', function (ev, data) {
@@ -51,8 +52,8 @@ define('forum/category', [
var clickedIndex = $(this).parents('[data-index]').attr('data-index');
$('[component="category/topic"]').each(function (index, el) {
if ($(el).offset().top - $(window).scrollTop() > 0) {
- localStorage.setItem('category:' + cid + ':bookmark', $(el).attr('data-index'));
- localStorage.setItem('category:' + cid + ':bookmark:clicked', clickedIndex);
+ storage.setItem('category:' + cid + ':bookmark', $(el).attr('data-index'));
+ storage.setItem('category:' + cid + ':bookmark:clicked', clickedIndex);
return false;
}
});
@@ -118,8 +119,8 @@ define('forum/category', [
$(window).on('action:ajaxify.contentLoaded', function () {
if (ajaxify.data.template.category && ajaxify.data.cid) {
- var bookmarkIndex = localStorage.getItem('category:' + ajaxify.data.cid + ':bookmark');
- var clickedIndex = localStorage.getItem('category:' + ajaxify.data.cid + ':bookmark:clicked');
+ var bookmarkIndex = storage.getItem('category:' + ajaxify.data.cid + ':bookmark');
+ var clickedIndex = storage.getItem('category:' + ajaxify.data.cid + ':bookmark:clicked');
bookmarkIndex = Math.max(0, parseInt(bookmarkIndex, 10) || 0);
clickedIndex = Math.max(0, parseInt(clickedIndex, 10) || 0);
diff --git a/public/src/client/search.js b/public/src/client/search.js
index 4f65935913..de68d1e401 100644
--- a/public/src/client/search.js
+++ b/public/src/client/search.js
@@ -1,7 +1,7 @@
'use strict';
-define('forum/search', ['search', 'autocomplete'], function (searchModule, autocomplete) {
+define('forum/search', ['search', 'autocomplete', 'storage'], function (searchModule, autocomplete, storage) {
var Search = {};
Search.init = function () {
@@ -147,13 +147,13 @@ define('forum/search', ['search', 'autocomplete'], function (searchModule, autoc
function handleSavePreferences() {
$('#save-preferences').on('click', function () {
- localStorage.setItem('search-preferences', JSON.stringify(getSearchData()));
+ storage.setItem('search-preferences', JSON.stringify(getSearchData()));
app.alertSuccess('[[search:search-preferences-saved]]');
return false;
});
$('#clear-preferences').on('click', function () {
- localStorage.removeItem('search-preferences');
+ storage.removeItem('search-preferences');
var query = $('#search-input').val();
$('#advanced-search')[0].reset();
$('#search-input').val(query);
diff --git a/public/src/client/topic.js b/public/src/client/topic.js
index d620da1e70..e4ba5a06a5 100644
--- a/public/src/client/topic.js
+++ b/public/src/client/topic.js
@@ -12,7 +12,8 @@ define('forum/topic', [
'navigator',
'sort',
'components',
-], function (infinitescroll, threadTools, postTools, events, posts, images, replies, navigator, sort, components) {
+ 'storage',
+], function (infinitescroll, threadTools, postTools, events, posts, images, replies, navigator, sort, components, storage) {
var Topic = {};
var currentUrl = '';
@@ -142,7 +143,7 @@ define('forum/topic', [
function handleBookmark(tid) {
// use the user's bookmark data if available, fallback to local if available
- var bookmark = ajaxify.data.bookmark || localStorage.getItem('topic:' + tid + ':bookmark');
+ var bookmark = ajaxify.data.bookmark || storage.getItem('topic:' + tid + ':bookmark');
var postIndex = getPostIndex();
if (postIndex && window.location.search.indexOf('page=') === -1) {
@@ -160,7 +161,7 @@ define('forum/topic', [
navigator.scrollToPost(parseInt(bookmark - 1, 10), true);
},
closefn: function () {
- localStorage.removeItem('topic:' + tid + ':bookmark');
+ storage.removeItem('topic:' + tid + ':bookmark');
},
});
setTimeout(function () {
@@ -273,7 +274,7 @@ define('forum/topic', [
function updateUserBookmark(index) {
var bookmarkKey = 'topic:' + ajaxify.data.tid + ':bookmark';
- var currentBookmark = ajaxify.data.bookmark || localStorage.getItem(bookmarkKey);
+ var currentBookmark = ajaxify.data.bookmark || storage.getItem(bookmarkKey);
if (ajaxify.data.postcount > ajaxify.data.bookmarkThreshold && (!currentBookmark || parseInt(index, 10) > parseInt(currentBookmark, 10))) {
if (app.user.uid) {
@@ -287,7 +288,7 @@ define('forum/topic', [
ajaxify.data.bookmark = index;
});
} else {
- localStorage.setItem(bookmarkKey, index);
+ storage.setItem(bookmarkKey, index);
}
}
diff --git a/public/src/modules/search.js b/public/src/modules/search.js
index 1401bf8619..5b77ab7572 100644
--- a/public/src/modules/search.js
+++ b/public/src/modules/search.js
@@ -1,7 +1,7 @@
'use strict';
-define('search', ['navigator', 'translator'], function (nav, translator) {
+define('search', ['navigator', 'translator', 'storage'], function (nav, translator, storage) {
var Search = {
current: {},
};
@@ -79,7 +79,7 @@ define('search', ['navigator', 'translator'], function (nav, translator) {
Search.getSearchPreferences = function () {
try {
- return JSON.parse(localStorage.getItem('search-preferences') || '{}');
+ return JSON.parse(storage.getItem('search-preferences') || '{}');
} catch (e) {
return {};
}
diff --git a/public/src/modules/sounds.js b/public/src/modules/sounds.js
index 38bbaec9cb..9a5f560447 100644
--- a/public/src/modules/sounds.js
+++ b/public/src/modules/sounds.js
@@ -1,7 +1,7 @@
'use strict';
-define('sounds', function () {
+define('sounds', ['storage'], function (storage) {
var Sounds = {};
var fileMap;
@@ -67,13 +67,13 @@ define('sounds', function () {
if (id) {
var item = 'sounds.handled:' + id;
- if (localStorage.getItem(item)) {
+ if (storage.getItem(item)) {
return;
}
- localStorage.setItem(item, true);
+ storage.setItem(item, true);
setTimeout(function () {
- localStorage.removeItem(item);
+ storage.removeItem(item);
}, 5000);
}
diff --git a/public/src/modules/storage.js b/public/src/modules/storage.js
new file mode 100644
index 0000000000..2fdf38f8af
--- /dev/null
+++ b/public/src/modules/storage.js
@@ -0,0 +1,83 @@
+'use strict';
+
+/**
+ * Checks localStorage and provides a fallback if it doesn't exist or is disabled
+ */
+define('storage', function () {
+ function Storage() {
+ this._store = {};
+ this._keys = [];
+ }
+ Storage.prototype.setItem = function (key, val) {
+ key = String(key);
+ if (this._keys.indexOf(key) === -1) {
+ this._keys.push(key);
+ }
+ this._store[key] = val;
+ };
+ Storage.prototype.getItem = function (key) {
+ key = String(key);
+ if (this._keys.indexOf(key) === -1) {
+ return null;
+ }
+
+ return this._store[key];
+ };
+ Storage.prototype.removeItem = function (key) {
+ key = String(key);
+ this._keys = this._keys.filter(function (x) {
+ return x !== key;
+ });
+ this._store[key] = null;
+ };
+ Storage.prototype.clear = function () {
+ this._keys = [];
+ this._store = {};
+ };
+ Storage.prototype.key = function (n) {
+ n = parseInt(n, 10) || 0;
+ return this._keys[n];
+ };
+ if (Object.defineProperty) {
+ Object.defineProperty(Storage.prototype, 'length', {
+ get: function () {
+ return this._keys.length;
+ },
+ });
+ }
+
+ var storage;
+ var item = Date.now();
+
+ try {
+ storage = window.localStorage;
+ storage.setItem(item, item);
+ if (storage.getItem(item) !== item) {
+ throw Error('localStorage behaved unexpectedly');
+ }
+ storage.removeItem(item);
+
+ return storage;
+ } catch (e) {
+ console.warn(e);
+ console.warn('localStorage failed, falling back on sessionStorage');
+
+ // see if sessionStorage works, and if so, return that
+ try {
+ storage = window.sessionStorage;
+ storage.setItem(item, item);
+ if (storage.getItem(item) !== item) {
+ throw Error('sessionStorage behaved unexpectedly');
+ }
+ storage.removeItem(item);
+
+ return storage;
+ } catch (e) {
+ console.warn(e);
+ console.warn('sessionStorage failed, falling back on memory storage');
+
+ // return an object implementing mock methods
+ return new Storage();
+ }
+ }
+});
diff --git a/src/meta/js.js b/src/meta/js.js
index 967b100a32..0fff87c6a1 100644
--- a/src/meta/js.js
+++ b/src/meta/js.js
@@ -78,6 +78,7 @@ module.exports = function (Meta) {
'public/src/modules/taskbar.js',
'public/src/modules/helpers.js',
'public/src/modules/string.js',
+ 'public/src/modules/storage.js',
],
// modules listed below are built (/src/modules) so they can be defined anonymously
From cc76a8663d095ae58a71682e4a63c1954986d0fb Mon Sep 17 00:00:00 2001
From: Peter Jaszkowiak
Date: Wed, 19 Apr 2017 20:44:11 -0600
Subject: [PATCH 2/2] Add flag for detecting if the mock is being used
---
public/src/modules/storage.js | 1 +
1 file changed, 1 insertion(+)
diff --git a/public/src/modules/storage.js b/public/src/modules/storage.js
index 2fdf38f8af..5cb6051586 100644
--- a/public/src/modules/storage.js
+++ b/public/src/modules/storage.js
@@ -8,6 +8,7 @@ define('storage', function () {
this._store = {};
this._keys = [];
}
+ Storage.prototype.isMock = true;
Storage.prototype.setItem = function (key, val) {
key = String(key);
if (this._keys.indexOf(key) === -1) {