').append(button.clone());
29 | button.replaceWith(div);
30 | }
31 |
32 | function drawInstallIndicatorArrow() {
33 | if (settings.get('dockState') != 'left') {
34 | return;
35 | }
36 |
37 | var p = $('#optionsContainer p').first();
38 | var arrow = $('#installIndicatorArrow');
39 | var newArrow = $('
').html(getMessage('installIndicatorArrowText')) );
42 | arrow.replaceWith(newArrow);
43 |
44 | newArrow.offset({ top: p.offset().top, left: p.offset().left });
45 |
46 | setTimeout(function() {
47 | newArrow.animate({ left: -4 }, 2000, 'easeOutBounce', function() {
48 | setTimeout(function() {
49 | newArrow.fadeOut(300);
50 | }, 7000);
51 | });
52 | }, 1000);
53 | }
--------------------------------------------------------------------------------
/js/ui/panes/external-site.js:
--------------------------------------------------------------------------------
1 | var IFRAME_LOAD_TIMEOUT_MS = 12000;
2 |
3 | // Prevent access by iframe since it could theoretically
4 | // get at the extension's JS or chrome context, i.e. loading
5 | // the inner iframe with a chrome-extension:// url
6 | window.parent = undefined;
7 | chrome = undefined;
8 |
9 | window.onload = onLoad;
10 |
11 | function onLoad() {
12 | document.getElementById('contentFrame').onload=onLoadIframe;
13 | setIframeSrc();
14 | }
15 |
16 | function onLoadIframe() {
17 | document.getElementById('loadingHint').style.display = 'none';
18 | TimeoutManager.clear('iframeLoad');
19 | }
20 |
21 | function setIframeSrc() {
22 | var src = location.hash.slice(1);
23 | document.getElementById('loadingHint').style.display = 'block';
24 | document.getElementById('loadingURL').innerText = src;
25 | document.getElementById('contentFrame').src = src;
26 | TimeoutManager.set('iframeLoad', onIframeLoadTimeout, IFRAME_LOAD_TIMEOUT_MS);
27 | }
28 |
29 | function onIframeLoadTimeout() {
30 | this.document.getElementById('loadingError').innerHTML =
31 | 'This content is taking a long time to load.
Many sites block being loaded in an IFRAME. ' +
32 | 'This may be the case here. If the content never loads, you are probably out of luck. Sorry!';
33 | }
34 |
--------------------------------------------------------------------------------
/js/ui/panes/notepad.js:
--------------------------------------------------------------------------------
1 | var NOTEPAD_AUTOSAVE_DELAY_MS = 1000;
2 | var TAB_INSERT_STRING = ' ';
3 |
4 | initSidebarPane();
5 |
6 | $(document).ready(() => {
7 | // this call is wrapped in a fn closure because jQuery ready() doesn't like
8 | // being passed async fns
9 | onReady();
10 | });
11 |
12 | async function onReady() {
13 | setI18NText();
14 |
15 | var notepadData = await settings.loadData('notepadContent', '');
16 |
17 | if (notepadData === '') {
18 | // If notepad data was previously stored in localStorage, migrate it now
19 | var oldData = settings.get('notepadContent', '');
20 |
21 | if (oldData !== '') {
22 | await settings.saveData('notepadContent', oldData);
23 | settings.set('notepadContent', undefined);
24 | notepadData = oldData;
25 | console.log('Migrated notepad data to chrome.storage.local');
26 | }
27 | }
28 |
29 | $('#notepad')
30 | .keyup(onNotepadKeyUp)
31 | .keydown(onNotepadKeyDown)
32 | .val(settings.get('notepadContent', ''))
33 | .focus();
34 |
35 | var lastSavedDateVal = settings.get('notepadSavedAt');
36 | if (lastSavedDateVal) {
37 | setLastSavedText(lastSavedDateVal);
38 | }
39 | }
40 |
41 | function onNotepadKeyUp(evt) {
42 | TimeoutManager.reset('saveNotepad', saveNotepad, NOTEPAD_AUTOSAVE_DELAY_MS);
43 | }
44 |
45 | function onNotepadKeyDown(evt) {
46 | if (evt.keyCode == 9) {
47 | evt.stopPropagation();
48 | $('#notepad').insertAtCaret(TAB_INSERT_STRING);
49 | return false;
50 | }
51 |
52 | if (evt.keyCode == 83 && evt.ctrlKey) {
53 | saveNotepad();
54 | evt.stopPropagation();
55 | return false;
56 | }
57 | }
58 |
59 | async function saveNotepad() {
60 | await settings.saveData('notepadContent', $('#notepad').val());
61 |
62 | var dateVal = Date.now();
63 | settings.set('notepadSavedAt', dateVal);
64 |
65 | setLastSavedText(dateVal);
66 | }
67 |
68 | function setLastSavedText(dateVal) {
69 | var dateText = new Date(dateVal).toString().replace(/ GMT.+/, '');
70 | $('#lastSavedAt').text(dateText);
71 | }
72 |
--------------------------------------------------------------------------------
/js/ui/panes/whatsnew.js:
--------------------------------------------------------------------------------
1 | initSidebarPane();
2 |
3 | $(document).ready(onReady);
4 |
5 | function onReady() {
6 | setI18NText();
7 |
8 | readFile('http://www.sidewise.info/changelog?embed=1', function(data) {
9 | $('#latestChanges').html(data);
10 | });
11 |
12 | $('#showAfterNew').attr('checked', settings.get('showWhatsNewPane', true));
13 |
14 | $(document)
15 | .on('click', '#closeButton', function() {
16 | bg.paneCatalog.removePane('whatsnew');
17 | bg.paneCatalog.saveState();
18 | bg.sidebarHandler.sidebarPanes['sidebarHost'].manager.removeSidebarPane('whatsnew');
19 | })
20 | .on('click', '#showAfterNew', function() {
21 | settings.set('showWhatsNewPane', $('#showAfterNew').is(':checked'));
22 | });
23 | }
24 |
--------------------------------------------------------------------------------
/js/ui/sidebar.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | //////////////////////////////////////////
4 | // Initialization
5 | //////////////////////////////////////////
6 |
7 | var manager;
8 | var bg;
9 | var settings;
10 |
11 | $(document).ready(onReady);
12 |
13 | function onReady() {
14 | bg = chrome.extension.getBackgroundPage();
15 | settings = bg.settings;
16 |
17 | $.fx.off = !settings.get('animationEnabled');
18 |
19 | manager = new SidebarNavManager($('ul#sidebarButtons'), $('tr#sidebars'),
20 | $('table#main'), $('body'), 'td');
21 | manager.addSidebarPanes(bg.paneCatalog.items);
22 |
23 | // Set initial sidebar position
24 | var initialSidebar = settings.get('lastSidebarPaneId');
25 | if (initialSidebar === undefined || bg.paneCatalog.getPaneIds().indexOf(initialSidebar) == -1) {
26 | initialSidebar = bg.paneCatalog.items[0].id;
27 | }
28 | manager.showSidebarPane(initialSidebar);
29 |
30 | // Defeat Chrome's possible attempt to set its own scroll position when sidebar is refreshed
31 | $(window).load(function() {
32 | setTimeout(function() { manager.scrollToCurrentSidebarPane(true); }, 0);
33 | setTimeout(function() { manager.scrollToCurrentSidebarPane(true); }, 100);
34 | });
35 |
36 | $(document)
37 | .keydown(onDocumentKeyDown)
38 | .scroll(onDocumentScroll)
39 | .mouseover(onDocumentMouseOver);
40 | $(window).resize(onWindowResize);
41 |
42 | $('#optionsButton')
43 | .attr('title', getMessage('sidebars_optionsButtonTooltip'))
44 | .tooltip({ position: 'bottom center', predelay: 400, offset: [15, -15]})
45 | .click(onClickOptionsButton)
46 | .mousedown(onMouseDownOptionsButton)
47 | .mouseup(onMouseUpOptionsButton)
48 | .mouseover(onMouseOverOptionsButton)
49 | .mouseout(onMouseUpOptionsButton);
50 |
51 | setI18NText();
52 |
53 | bg.sidebarHandler.registerSidebarPane('sidebarHost', window);
54 |
55 | }
56 |
57 |
58 | //////////////////////////////////////////
59 | // Event handlers
60 | //////////////////////////////////////////
61 |
62 | function onDocumentKeyDown(evt) {
63 | if (evt.keyCode == 27 // esc
64 | || (evt.ctrlKey && evt.keyCode == 70) // ctrl-f
65 | || (!evt.ctrlKey && !evt.altKey && evt.keyCode >= 48 && evt.keyCode <= 90) // non modified printable
66 | || (evt.ctrlKey && evt.keyCode == 86) // ctrl-w
67 | || (evt.ctrlKey && evt.keyCode == 115)) // ctrl-f4
68 | {
69 | try {
70 | // transmit keydown events to the sidebar pane via jQuery.trigger()
71 | // TODO make this work with vanilla JS if possible so non-$-using panes can receive our communique
72 | var $iframe =$('#sidebarContainer__' + manager.currentSidebarId).children('iframe').get(0).contentWindow;
73 | var $iframeJQuery = $iframe.jQuery($iframe.document);
74 | $iframeJQuery.trigger(evt);
75 | return false;
76 | }
77 | catch(ex) { }
78 | }
79 |
80 | return true;
81 | }
82 |
83 | function onDocumentScroll() {
84 | if (manager.scrolling) {
85 | return;
86 | }
87 | // prevent user scrolling of sidebar panes through e.g. drag-selecting some text
88 | // and moving the mouse off the edge of the sidebar window
89 | manager.scrollToCurrentSidebarPane(true);
90 | }
91 |
92 | function onDocumentMouseOver() {
93 | if (!settings.get('focusSidebarOnHover')) {
94 | return;
95 | }
96 | bg.sidebarHandler.focus();
97 | }
98 |
99 | function onWindowResize() {
100 | // perform resize work, e.g. resizing an attached dock window
101 | bg.sidebarHandler.onResize();
102 |
103 | // prevent width-resizing of sidebar from showing part of another sidebar pane
104 | manager.scrollToCurrentSidebarPane(true);
105 | }
106 |
107 | function onClickOptionsButton() {
108 | var optionsUrl = chrome.extension.getURL('/options.html');
109 | chrome.tabs.query({ url: optionsUrl }, function(tabs) {
110 | if (tabs.length == 0) {
111 | chrome.tabs.create({ url: optionsUrl });
112 | return;
113 | }
114 | chrome.tabs.update(tabs[0].id, { active: true });
115 | });
116 | }
117 |
118 | function onMouseDownOptionsButton(evt) {
119 | var $target = $(evt.target.id == 'optionsButtonIcon' ? evt.target.parentElement : evt.target);
120 | $target.addClass('mousedown');
121 | evt.stopPropagation();
122 | }
123 |
124 | function onMouseUpOptionsButton(evt) {
125 | var $target = $(evt.target.id == 'optionsButtonIcon' ? evt.target.parentElement : evt.target);
126 | $target.removeClass('mousedown');
127 | evt.stopPropagation();
128 | }
129 |
130 | function onMouseOverOptionsButton(evt) {
131 | if (evt.which == 1) {
132 | onMouseDownOptionsButton(evt);
133 | }
134 | evt.stopPropagation();
135 | }
136 |
--------------------------------------------------------------------------------
/js/ui/test_icons/test_icons.js:
--------------------------------------------------------------------------------
1 | var bg;
2 |
3 | $(document).ready(function() {
4 | bg = chrome.extension.getBackgroundPage();
5 | bg.IconTesterDomWindow = window;
6 | });
7 |
8 | function testIcon(url) {
9 | var $icon = $('
![]()
', { width: 16, height: 16, src: url });
10 | $('body').append($icon);
11 | }
12 |
--------------------------------------------------------------------------------
/js/util/chrome-functions.js:
--------------------------------------------------------------------------------
1 | /* Functions relating to Chrome specific functionality */
2 |
3 | // Get extension version from manifest.json.
4 | function getVersion() {
5 | var details = chrome.app.getDetails();
6 | return details.version;
7 | }
8 |
9 | // Update window's position and state.
10 | function positionWindow(winId, metrics, callback)
11 | {
12 | if (callback) {
13 | chrome.windows.update(winId, metrics, callback);
14 | return;
15 | }
16 | chrome.windows.update(winId, metrics);
17 | }
18 |
19 | // Focus the current Chrome window's active tab in the page tree.
20 | // If force=true, perform a (re)focusing even if we're already focused
21 | // on the currently focused tab.
22 | function focusCurrentTabInPageTree(force) {
23 | var windowId = focusTracker.getFocused();
24 |
25 | if (!windowId) {
26 | return;
27 | }
28 |
29 | chrome.tabs.query({ active: true, windowId: windowId }, function(tabs) {
30 | if (tabs.length == 0) {
31 | return;
32 | }
33 |
34 | var activeTab = tabs[0];
35 |
36 | if (activeTab.id == tree.focusedTabId && !force) {
37 | return;
38 | }
39 |
40 | tree.focusPage(activeTab.id);
41 | });
42 | }
43 |
44 | // Retrieve and update the tab.status of the specified PageTreeNode or matcher.
45 | function refreshPageStatus(page) {
46 | if (!page.isTab()) {
47 | return;
48 | }
49 | setTimeout(function() {
50 | var pageId = page.id;
51 | page = tree.getNode(pageId);
52 | if (!page) {
53 | log('Aborting page status refresh because page node no longer exists in tree', pageId);
54 | return;
55 | }
56 | if (!page.chromeId) {
57 | log('Aborting page status refresh because page node no longer has a chromeId', page);
58 | return;
59 | }
60 | chrome.tabs.get(page.chromeId, function(tab) {
61 | if (!tab) {
62 | log('Aborting page status refresh because tab no longer exists in Chrome', tabId);
63 | return;
64 | }
65 | var tabId = tab.id;
66 | page = tree.getNode(['chromeId', tabId]);
67 | if (!page) {
68 | log('Aborting page status refresh because tab no longer exists in tree by chromeId', tabId);
69 | return;
70 | }
71 | tree.updateNode(page, { status: tab.status });
72 | });
73 | }, 100);
74 | }
75 |
76 | function fixAllPinnedUnpinnedTabOrder() {
77 | tree.filter(function(e) { return e instanceof PageNode && !e.hibernated && e.pinned; })
78 | .forEach(function(e) { fixPinnedUnpinnedTabOrder(e); }
79 | );
80 |
81 | tree.filter(function(e) { return e instanceof PageNode && !e.hibernated && !e.pinned; }).reverse()
82 | .forEach(function(e) { fixPinnedUnpinnedTabOrder(e); }
83 | );
84 | }
85 |
86 | // Repair the tab ordering of the given PageTreeNode with respect to
87 | // its pinned state versus the pinned state of other page nodes in the tree
88 | function fixPinnedUnpinnedTabOrder(page) {
89 |
90 | // fix order wrt pinned tabs if necessary
91 | if (!page.pinned
92 | && page.following(function(e) { return e.isTab() && e.pinned }, page.topParent()))
93 | {
94 | var lastPinned = last(page.followingNodes(page.topParent()), function(e) {
95 | return e.isTab() && e.pinned;
96 | })[1];
97 | if (!lastPinned) {
98 | throw new Error('Could not find lastPinned but should have been able to');
99 | }
100 | log('Moving non-pinned tab to be below last pinned tab', page.id, 'after', lastPinned.id);
101 | return tree.moveNodeRel(page, lastPinned.children.length == 0 ? 'after' : 'prepend', lastPinned);
102 | }
103 |
104 | if (page.pinned
105 | && page.preceding(function(e) { return e.isTab() && !e.pinned }, page.topParent()))
106 | {
107 | var topUnpinned = first(page.precedingNodes(page.topParent()), function(e) {
108 | return e.isTab && !e.pinned;
109 | })[1];
110 | if (!topUnpinned) {
111 | throw new Error('Could not find topUnpinned but should have been able to');
112 | }
113 | log('Moving pinned tab to be before first pinned tab', page.id, 'before', topUnpinned.id);
114 | return tree.moveNodeRel(page, 'before', topUnpinned);
115 | }
116 |
117 | // log('no un/pin fix made');
118 | return undefined;
119 | }
--------------------------------------------------------------------------------
/js/util/classes/Catalog.js:
--------------------------------------------------------------------------------
1 | // Manages an ordered array of items, each of which should be an Object
2 | // with a unique .id property (and other properties as desired)
3 |
4 | var Catalog = function(items) {
5 | this.items = items || [];
6 | };
7 |
8 | Catalog.prototype = {
9 |
10 | // Get the catalog item with the given id or undefined if not found.
11 | getItem: function(id) {
12 | return firstElem(this.items, function(e) { return e.id == id; });
13 | },
14 |
15 | // Returns an ordered array of the ids of the items in the catalog.
16 | getIds: function() {
17 | return this.items.map(function(e) { return e.id; });
18 | },
19 |
20 | // Append a new item to the catalog.
21 | appendItem: function(item) {
22 | var existing = this.getItem(item.id);
23 | if (existing) {
24 | throw new Error('Item already exists in catalog with id ' + item.id);
25 | }
26 | this.items.push(item);
27 | return item;
28 | },
29 |
30 | // Remove the item from the catalog with the given id.
31 | removeItem: function(id) {
32 | var found = first(this.items, function(e) { return e.id == id; });
33 | if (!found) {
34 | throw new Error('Could not find item to remove with id ' + id);
35 | }
36 | this.items.splice(found[0], 1);
37 | },
38 |
39 | // Reorder the item with the given id to have index newIndex in the item array
40 | reorderItem: function(id, newIndex) {
41 | var index, item;
42 | for (var i = 0; i < this.items.length; i++) {
43 | item = this.items[i];
44 | if (item.id == id) {
45 | index = i;
46 | break;
47 | }
48 | }
49 |
50 | if (index === undefined) {
51 | throw new Error('Could not find item by id ' + id);
52 | }
53 | this.items.splice(index, 1);
54 | this.items.splice(newIndex, 0, item);
55 | },
56 |
57 | // Load catalog from local setting with the provided settingName.
58 | // Will populate instead with defaultItems if provided and setting is empty.
59 | loadState: function(settingName, defaultItems) {
60 | this.items = settings.get(settingName, defaultItems || []);
61 | },
62 |
63 | // Save the contents of the catalog to the local setting with the provided settingName.
64 | saveState: function(settingName) {
65 | var state = [];
66 |
67 | for (var i = 0; i < this.items.length; i++) {
68 | state.push({ id: this.items[i].id, enabled: this.items[i].enabled });
69 | }
70 |
71 | settings.set(settingName, state);
72 | }
73 |
74 | };
75 |
76 |
--------------------------------------------------------------------------------
/js/util/classes/TimeoutManager.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @static
3 | * @class
4 | * Used to manage timeouts more easily.
5 | * Use as a singleton: do not instatiate, just call TimeoutManager.set(), et al. directly.
6 | */
7 | var TimeoutManager = {
8 | timeouts: {},
9 |
10 | get: function(label) {
11 | var timeout = this.timeouts[label];
12 | return timeout;
13 | },
14 |
15 | exists: function(label) {
16 | return (this.timeouts[label] !== undefined);
17 | },
18 |
19 | set: function(label, fn, timeoutMs) {
20 | if (this.exists(label)) {
21 | throw new Error('A timeout with the given label has already been set');
22 | }
23 | var tmgr = this;
24 | var timeoutFn = function() {
25 | fn();
26 | tmgr.del(label);
27 | }
28 | var t = setTimeout(timeoutFn, timeoutMs);
29 | this.timeouts[label] = {id: t, fn: timeoutFn, ms: timeoutMs};
30 | },
31 |
32 | clear: function(label) {
33 | var timeout = this.get(label);
34 | if (timeout) {
35 | clearTimeout(timeout.id);
36 | this.del(label);
37 | return true;
38 | }
39 | return false;
40 | },
41 |
42 | del: function(label) {
43 | delete this.timeouts[label];
44 | },
45 |
46 | reset: function(label, fn, timeoutMs) {
47 | if (this.exists(label)) {
48 | var timeout = this.get(label);
49 | clearTimeout(timeout.id);
50 | timeout.fn = fn || timeout.fn;
51 | timeout.ms = timeoutMs !== undefined ? timeoutMs : timeout.ms;
52 | timeout.id = setTimeout(timeout.fn, timeout.ms);
53 | return;
54 | }
55 |
56 | if (!fn || !timeoutMs) {
57 | throw new Error('Tried to reset a timer that does not yet exist, but did not pass fn or timeoutMs');
58 | }
59 |
60 | // Called .reset but we haven't .set yet; do that now
61 | this.set(label, fn, timeoutMs);
62 | }
63 | };
64 |
65 |
--------------------------------------------------------------------------------
/js/util/i18n.js:
--------------------------------------------------------------------------------
1 | // Populate each element's text on the page with an i18n attribute with
2 | // the i18n message taken from _locales/xx/messages.json, where xx is
3 | // the user's 2 char language code
4 | function setI18NText() {
5 | var elems = $('[i18n]');
6 | elems.each(function(i, e) {
7 | e.innerHTML = getMessage(e.attributes.i18n.value);
8 | });
9 | }
10 |
11 | // Get an i18n message from _locales/xx/messages.json and transform it
12 | function getMessage(key, placeholderValues) {
13 | var msg = (chrome.i18n || window.parent.chrome.i18n).getMessage(key, placeholderValues);
14 | if (key.match(/^prompt_/)) {
15 | return transformPromptMessage(msg);
16 | }
17 | if (msg.match(/^#/)) {
18 | // leading # tells us we want to do markdown syntax transformation
19 | return transformMessage(msg.slice(1));
20 | }
21 | if (msg.match(/^!#/)) {
22 | // leading # tells us we want to do markdown syntax transformation,
23 | // and leading ! is used to denote "warning" hints in options, so preserve
24 | // the ! after transformation
25 | return '!' + transformMessage(msg.slice(2));
26 | }
27 | // just return the message untransformed
28 | return msg;
29 | }
30 |
31 | // Transform a message using Markdown syntax translation after turning //'s into \n's
32 | function transformMessage(msg) {
33 | msg = msg.replace(/:\/\//g, 'URL_PROTOCOL_SEPARATOR');
34 | msg = msg.replace(/\s*\/\/\s*/g, '\n');
35 | msg = msg.replace(/URL_PROTOCOL_SEPARATOR/g, '://');
36 | return marked(msg);
37 | }
38 |
39 | // Transform a prompt message, just turning //'s into \n\n
40 | function transformPromptMessage(msg) {
41 | return msg.replace(/\s*\/\/\s*/g, '\n\n');
42 | }
--------------------------------------------------------------------------------
/js/util/ui-util.js:
--------------------------------------------------------------------------------
1 | function isScrolledIntoView(elem)
2 | {
3 | var $window = $(window);
4 | var $elem = $(elem);
5 |
6 | var docViewTop = $window.scrollTop();
7 | var docViewBottom = docViewTop + $window.height();
8 |
9 | var elemTop = $elem.offset().top;
10 | var elemBottom = elemTop + $elem.height();
11 |
12 | return ((elemBottom >= docViewTop) && (elemTop <= docViewBottom)
13 | && (elemBottom <= docViewBottom) && (elemTop >= docViewTop) );
14 | }
15 |
16 | function copyTextToClipboard(text) {
17 | var copyFrom = $('
');
18 | copyFrom.text(text);
19 | $('body').append(copyFrom);
20 | copyFrom.select();
21 | document.execCommand('copy');
22 | copyFrom.remove();
23 | }
--------------------------------------------------------------------------------
/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "manifest_version": 2,
3 | "name": "__MSG_extName__",
4 | "version": "2017.2.13.0",
5 | "description": "__MSG_extDescription__",
6 | "icons": {
7 | "16": "images/sidewise_icon_16.png",
8 | "32": "images/sidewise_icon_32.png",
9 | "48": "images/sidewise_icon_48.png",
10 | "128": "images/sidewise_icon_128.png",
11 | "256": "images/sidewise_icon_256.png"
12 | },
13 | "background": {
14 | "page": "background.html"
15 | },
16 | "browser_action": {
17 | "default_icon": "images/sidewise_icon_16.png",
18 | "default_title": "Open Sidewise"
19 | },
20 | "content_scripts": [
21 | {
22 | "matches": ["http://*/*", "https://*/*"],
23 | "js": ["content_script.js"],
24 | "run_at": "document_start"
25 | }
26 | ],
27 | "default_locale": "en",
28 | "homepage_url": "http://www.sidewise.info",
29 | "minimum_chrome_version": "56.0.2924.87",
30 | "omnibox": { "keyword": "sw" },
31 | "options_page": "options.html",
32 | "permissions": [
33 | "tabs",
34 | "webNavigation",
35 | "*://*/*",
36 | "chrome://favicon/",
37 | "https://ssl.google-analytics.com/ga.js",
38 | "clipboardWrite",
39 | "storage",
40 | "unlimitedStorage"
41 | ],
42 | "web_accessible_resources": [
43 | "/sidebars/pages.html",
44 | "/sidebars/notepad.html",
45 | "/sidebars/external-site.html"
46 | ],
47 | "content_security_policy": "script-src 'self' https://ssl.google-analytics.com https://apis.google.com; object-src 'self'"
48 | }
--------------------------------------------------------------------------------
/options_install.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
33 |
34 |
35 |
36 |
39 |
40 |
41 |
46 |
47 |
48 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 | |
64 |
65 |
66 |
67 |
68 |
69 |
70 |

71 |
72 |
78 |
79 |
80 |
81 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "sidewise",
3 | "version": "1.0.0",
4 | "description": "Sidewise extension for Google Chrome",
5 | "main": "background.html",
6 | "scripts": {
7 | "test": "echo \"Error: no test specified\" && exit 1"
8 | },
9 | "author": "Joel Thornton
",
10 | "license": "SEE LICENSE IN LICENSE.txt",
11 | "devDependencies": {
12 | "grunt": "^1.0.1",
13 | "grunt-banner": "^0.6.0",
14 | "grunt-cli": "^1.2.0",
15 | "grunt-contrib-clean": "^1.0.0",
16 | "grunt-contrib-compress": "^1.4.1",
17 | "grunt-contrib-copy": "^1.0.0"
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/panes/404.html:
--------------------------------------------------------------------------------
1 |
2 | THERE IS NOTHING HERE
3 |
--------------------------------------------------------------------------------
/panes/bookmarks.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/panes/closed.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
57 |
58 |
61 |
62 |
68 |
69 |
70 |
--------------------------------------------------------------------------------
/panes/external-site.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 | Loading content from
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/panes/notepad.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
27 |
28 |
29 |
30 |
31 | |
32 |
33 |
34 |
37 |
38 |
39 |
40 |
--------------------------------------------------------------------------------
/panes/pages.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
55 |
56 |
59 |
60 |
66 |
67 |
68 |
--------------------------------------------------------------------------------
/panes/reddit.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
19 |
24 |
25 |
26 | Loading content
27 |
28 |
29 |
--------------------------------------------------------------------------------
/panes/tribunal.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/panes/whatsnew.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 | What's new
23 | Sidewise has recently been updated.
24 |
25 | View the full changelog
26 |
27 |
28 | Here are the latest changes:
29 |
30 |
31 | Submit bugs/feedback
32 | Donate to Sidewise
33 |
34 |
--------------------------------------------------------------------------------
/sidebar.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
43 |
44 |
--------------------------------------------------------------------------------
/test_icons.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | Testing for bad favicons. Please do not interact with Chrome at this time!
9 |
10 |
--------------------------------------------------------------------------------