Skip to content

Instantly share code, notes, and snippets.

@devinrhode2
Created December 27, 2018 01:34
Show Gist options
  • Save devinrhode2/86ae0e9e8629ae01ccb0adb142debba4 to your computer and use it in GitHub Desktop.
Save devinrhode2/86ae0e9e8629ae01ccb0adb142debba4 to your computer and use it in GitHub Desktop.
public-ff/bg/
(function() {
'use strict';
var globals = typeof global === 'undefined' ? self : global;
if (typeof globals.require === 'function') return;
var modules = {};
var cache = {};
var aliases = {};
var has = {}.hasOwnProperty;
var expRe = /^\.\.?(\/|$)/;
var expand = function(root, name) {
var results = [], part;
var parts = (expRe.test(name) ? root + '/' + name : name).split('/');
for (var i = 0, length = parts.length; i < length; i++) {
part = parts[i];
if (part === '..') {
results.pop();
} else if (part !== '.' && part !== '') {
results.push(part);
}
}
return results.join('/');
};
var dirname = function(path) {
return path.split('/').slice(0, -1).join('/');
};
var localRequire = function(path) {
return function expanded(name) {
var absolute = expand(dirname(path), name);
return globals.require(absolute, path);
};
};
var initModule = function(name, definition) {
var hot = hmr && hmr.createHot(name);
var module = {id: name, exports: {}, hot: hot};
cache[name] = module;
definition(module.exports, localRequire(name), module);
return module.exports;
};
var expandAlias = function(name) {
return aliases[name] ? expandAlias(aliases[name]) : name;
};
var _resolve = function(name, dep) {
return expandAlias(expand(dirname(name), dep));
};
var require = function(name, loaderPath) {
if (loaderPath == null) loaderPath = '/';
var path = expandAlias(name);
if (has.call(cache, path)) return cache[path].exports;
if (has.call(modules, path)) return initModule(path, modules[path]);
throw new Error("Cannot find module '" + name + "' from '" + loaderPath + "'");
};
require.alias = function(from, to) {
aliases[to] = from;
};
var extRe = /\.[^.\/]+$/;
var indexRe = /\/index(\.[^\/]+)?$/;
var addExtensions = function(bundle) {
if (extRe.test(bundle)) {
var alias = bundle.replace(extRe, '');
if (!has.call(aliases, alias) || aliases[alias].replace(extRe, '') === alias + '/index') {
aliases[alias] = bundle;
}
}
if (indexRe.test(bundle)) {
var iAlias = bundle.replace(indexRe, '');
if (!has.call(aliases, iAlias)) {
aliases[iAlias] = bundle;
}
}
};
require.register = require.define = function(bundle, fn) {
if (bundle && typeof bundle === 'object') {
for (var key in bundle) {
if (has.call(bundle, key)) {
require.register(key, bundle[key]);
}
}
} else {
modules[bundle] = fn;
delete cache[bundle];
addExtensions(bundle);
}
};
require.list = function() {
var list = [];
for (var item in modules) {
if (has.call(modules, item)) {
list.push(item);
}
}
return list;
};
var hmr = globals._hmr && new globals._hmr(_resolve, require, modules, cache);
require._cache = cache;
require.hmr = hmr && hmr.wrap;
require.brunch = true;
globals.require = require;
})();
/// validation.js: Data-validation routines.
/// Copyright (c) cxw42, 2017--2018.
/// NOTE: does NOT use common.js routines, so that common.js can use it.
(function (root, factory) {
if (typeof define === 'function' && define.amd) {
// AMD
define([], factory);
} else if (typeof exports === 'object') {
// Node, CommonJS-like
module.exports = factory();
} else {
// Browser globals (root is `window`)
root.Validation = factory();
}
}(this, function () {
"use strict";
/// The module we are creating
let module = {};
/// Validate a CSS color. Modified from a gist by
/// https://gist.github.com/HatScripts at
/// https://gist.github.com/olmokramer/82ccce673f86db7cda5e#gistcomment-2082703
module.isValidColor = function(color) {
color = color.trim();
if (color.charAt(0) === "#") { // hex colors
color = color.substring(1);
return (
([3, 4, 6, 8].indexOf(color.length) > -1) &&
(!isNaN(parseInt(color, 16)))
);
} else if(/^[A-Za-z]{1,32}$/.test(color)) { // color names
return true; // for now, allow any alpha string
} else { // RGB, HSL colors
const RE = /^(rgb|hsl)a?\((-?[\d\.]+%?(deg|rad|grad|turn)?[,\s]+){2,3}[\s\/]*[\d\.]+%?\)$/i;
return RE.test(color);
}
}; //isValidColor
/// Validate a URL.
/// @param test_url The URL to test
/// @param allowed_schemes {Optional array} If provided, only those schemes
/// (no colons) are allowed.
module.isValidURL = function(test_url, allowed_schemes) {
try {
let url = new URL(String(test_url));
if(Array.isArray(allowed_schemes)) {
let scheme = url.protocol.replace(/:$/,'');
if(allowed_schemes.indexOf(scheme) === -1) return false;
}
return true;
} catch(e) { } // nop
return false;
}; //isValidURL
return module;
}));
// vi: set ts=4 sts=4 sw=4 et ai fo-=o: //
;// common.js: Only constants and stateless helper functions should be
// in this file.
// ** Not currently a require.js module so that it can be used in contexts
// ** where require.js is not available (e.g., background.js).
// ** TODO split this into UMD modules so the pieces can be loaded with or
// without require.js.
console.log('TabFern common.js loading');
// Messages between parts of TabFern // {{{1
// The format of a message is
// { msg: <one of the below constants> [, anything else] }
// For responses, response:true is also included.
const MSG_GET_VIEW_WIN_ID = 'getViewWindowID';
const MSG_EDIT_TAB_NOTE = 'editTabNote';
////////////////////////////////////////////////////////////////////////// }}}1
// Test for Firefox // {{{1
// Not sure if I need this, but I'm playing it safe for now. Firefox returns
// null rather than undefined in chrome.runtime.lastError when there is
// no error. This is to test for null in Firefox without changing my
// Chrome code. Hopefully in the future I can test for null/undefined
// in either browser, and get rid of this block.
BROWSER_TYPE=null; // unknown
(function(win){
let isLastError_chrome =
()=>{return (typeof(chrome.runtime.lastError) !== 'undefined');};
let isLastError_firefox =
()=>{return (chrome.runtime.lastError !== null);};
if(typeof browser !== 'undefined' && browser.runtime &&
browser.runtime.getBrowserInfo) {
browser.runtime.getBrowserInfo().then(
(info)=>{ // fullfillment
if(info.name === 'Firefox') {
win.isLastError = isLastError_firefox;
BROWSER_TYPE = 'ff';
} else {
win.isLastError = isLastError_chrome;
}
},
()=>{ //rejection --- assume Chrome by default
win.isLastError = isLastError_chrome;
}
);
} else { // Chrome
BROWSER_TYPE = 'chrome';
win.isLastError = isLastError_chrome;
}
})(window);
////////////////////////////////////////////////////////////////////////// }}}1
// DOM-related functions // {{{1
/// Append a <script> to the <head> of #document.
/// @param {Document} document
/// @param {String} url URL of script to load
/// @param {String} [type] Type of Script tag. Default: text/javascript
/// @param {Function} callback Set as callback for BOTH onreadystatechange and onload
function asyncAppendScriptToHead(document, url, callback, type = 'text/javascript')
{
// Adding the script tag to the head as suggested before
var head = document.getElementsByTagName('head')[0];
var script = document.createElement('script');
script.type = type;
script.src = url;
// Then bind the event to the callback function.
// There are several events for cross browser compatibility.
script.onreadystatechange = callback;
script.onload = callback;
script.onerror = callback;
// Fire the loading
head.appendChild(script);
} //asyncAppendScriptToHead()
/// Invoke a callback only when the document is loaded. Does not pass any
/// parameters to the callback.
function callbackOnLoad(callback)
{
if(document.readyState !== 'complete') {
// Thanks to https://stackoverflow.com/a/28093606/2877364 by
// https://stackoverflow.com/users/4483389/matthias-samsel
window.addEventListener('load', ()=>{ callback(); }, { 'once': true });
} else {
window.setTimeout(callback, 0); //always async
}
} //callbackOnLoad
/// Add a CSS file to the specified document.
/// Modified from http://requirejs.org/docs/faq-advanced.html#css
/// @param doc {DOM Document}
/// @param url {String}
/// @param before {DOM Node} Optional --- if provided, the new sheet
/// will be inserted before #before.
function loadCSS(doc, url, before) {
let link = doc.createElement("link");
link.type = "text/css";
link.rel = "stylesheet";
link.href = url;
let head = document.getElementsByTagName("head")[0];
if(before) {
head.insertBefore(link, before);
} else {
head.appendChild(link);
}
} //loadCSS
////////////////////////////////////////////////////////////////////////// }}}1
// Miscellaneous functions // {{{1
/// Shortcut for i18n. Call _T("name") to pull the localized "name".
var _T = chrome.i18n.getMessage;
/// Ignore a Chrome callback error, and suppress Chrome's
/// `runtime.lastError` diagnostic. Use this as a Chrome callback.
function ignore_chrome_error() { void chrome.runtime.lastError; }
/// Deep-compare two objects for memberwise equality. However, if either
/// object contains a pointer to the other, this will return false rather
/// than getting stuck in a loop. Non-`object` types are compared by ===.
/// All member comparisons are ===.
/// @param obj1 An object to compare
/// @param obj2 The other object to compare
/// Modified from https://gist.github.com/nicbell/6081098 by
/// https://gist.github.com/nicbell
function ObjectCompare(obj1, obj2) {
if( (typeof obj1 !== 'object') || (typeof obj2 !== 'object') ) {
return obj1 === obj2;
}
//Loop through properties in object 1
for (var p in obj1) {
//Check property exists on both objects
if (obj1.hasOwnProperty(p) !== obj2.hasOwnProperty(p)) return false;
switch (typeof (obj1[p])) {
//Deep compare objects
case 'object':
//Check for circularity
if( (obj1[p]===obj2) || (obj2[p]===obj1) ) return false;
if (!ObjectCompare(obj1[p], obj2[p])) return false;
break;
//Compare function code
case 'function':
if (typeof (obj2[p]) == 'undefined' ||
(obj1[p].toString() !== obj2[p].toString())) {
return false;
}
break;
//Compare values
default:
if (obj1[p] !== obj2[p]) return false;
}
}
//Check object 2 for any extra properties
for (var p in obj2) {
if (typeof (obj1[p]) === 'undefined') return false;
}
return true;
} //ObjectCompare
/// }}}1
// vi: set ts=4 sts=4 sw=4 et ai fo-=o fdm=marker fdl=0: //
;
(function() {
var global = typeof window === 'undefined' ? this : window;
var process;
var __makeRelativeRequire = function(require, mappings, pref) {
var none = {};
var tryReq = function(name, pref) {
var val;
try {
val = require(pref + '/node_modules/' + name);
return val;
} catch (e) {
if (e.toString().indexOf('Cannot find module') === -1) {
throw e;
}
if (pref.indexOf('node_modules') !== -1) {
var s = pref.split('/');
var i = s.lastIndexOf('node_modules');
var newPref = s.slice(0, i).join('/');
return tryReq(name, newPref);
}
}
return none;
};
return function(name) {
if (name in mappings) name = mappings[name];
if (!name) return;
if (name[0] !== '.' && pref) {
var val = tryReq(name, pref);
if (val !== none) return val;
}
return require(name);
}
};
require.register("bg/background.js", function(exports, require, module) {
// background.js: background script for TabFern. Runs as a module.
// CC-BY-SA 3.0
console.log('TabFern: running ' + 'app/bg/background.js');
if(false) { // Vendor files - listed here only so they'll be bundled
require('vendor/validation');
require('vendor/common');
}
const S = require('common/setting-definitions'); // in app/
/// The module exports, for use in command-line debugging
let me = {
viewWindowID: undefined, // the chrome.windows ID of our view
loadView: undefined,
};
module.exports = me;
//////////////////////////////////////////////////////////////////////////
// Helpers //
// Open the view
me.loadView = function loadView()
{
console.log("TabFern: Opening view");
chrome.windows.create(
{ 'url': chrome.runtime.getURL('win/container.html'),
'type': 'popup',
//'focused': true
// Note: `focused` is not supported on Firefox, but
// focused=true is usually the effect.
// https://bugzilla.mozilla.org/show_bug.cgi?id=1253129
// However, Firefox does support windows.update with focused.
},
function(win) {
me.viewWindowID = win.id;
console.log('TabFern new View ID: ' + me.viewWindowID.toString());
chrome.windows.update(win.id, {focused:true}, ignore_chrome_error);
});
} //loadView()
//////////////////////////////////////////////////////////////////////////
// Action button //
/// Move the TabFern window to #reference_win. This helps if the
/// TabFern window winds up off-screen.
/// This is called as a Chrome callback.
function moveTabFernViewToWindow(reference_cwin)
{
function clip(x, lo, hi) { if(hi<lo) hi=lo; return Math.min(hi, Math.max(lo, x)); }
if(!isLastError()) {
if(!me.viewWindowID) return;
chrome.windows.get(me.viewWindowID, function(view_cwin) {
// TODO handle Chrome error
let updates = {left: reference_cwin.left+16,
top: reference_cwin.top+16,
state: 'normal', // not minimized or maximized
};
// Don't let it be too large or too small
updates.width = clip(view_cwin.width, 200, reference_cwin.width-32);
updates.height = clip(view_cwin.height, 100, reference_cwin.height-32);
chrome.windows.update(me.viewWindowID, updates
// TODO handle Chrome error
);
});
}
} //moveTabFernViewToWindow()
// Modified from https://stackoverflow.com/q/8984047/2877364 by
// https://stackoverflow.com/users/930675/sean-bannister
// When the icon is clicked in Chrome
let onClickedListener = function(tab) {
// If viewWindowID is undefined then there isn't a popup currently open.
if (typeof me.viewWindowID === "undefined") { // Open the popup
me.loadView();
} else { // There's currently a popup open
// Bring it to the front so the user can see it
chrome.windows.update(me.viewWindowID, { "focused": true });
}
// Set a timer to bring the window to the front on another click
// that follows fairly shortly.
if(tab) {
let clickListener = function(tab) {
if(me.viewWindowID && tab.windowId) {
chrome.windows.get(tab.windowId, moveTabFernViewToWindow);
}
};
let removeClickListener = function() {
chrome.browserAction.onClicked.removeListener(clickListener);
};
setTimeout(removeClickListener, 1337);
// Do not change this constant or the Unix daemon will dog
// your footsteps until the `time_t`s roll over.
chrome.browserAction.onClicked.addListener(clickListener);
}
} //onClickedListener()
chrome.browserAction.onClicked.addListener(onClickedListener);
let onCommandListener = function(cmd) {
console.log("Received command " + cmd);
if(cmd == 'reveal-view') {
onClickedListener(null); // null => no tab, so no summon
}
} //onCommandListener()
chrome.commands.onCommand.addListener(onCommandListener);
// When a window is closed
chrome.windows.onRemoved.addListener(function(windowId) {
// If the window getting closed is the popup we created
if (windowId === me.viewWindowID) {
// Set viewWindowID to undefined so we know the popup is not open
me.viewWindowID = undefined;
console.log('Popup window was closed');
}
});
//////////////////////////////////////////////////////////////////////////
// Action button context-menu items //
function editNoteOnClick(info, tab)
{
console.log({editNoteOnClick:info, tab});
if(!tab.id) return;
console.log(`Sending editNote for ${tab.id}`);
chrome.runtime.sendMessage({msg:MSG_EDIT_TAB_NOTE, tab_id: tab.id},
// This callback is only for debugging --- all the action happens in
// src/view/tree.js, the receiver.
function(resp){
if(isLastError()) {
console.log('Couldn\'t send edit-note to ' + tab.id + ': ' +
chrome.runtime.lastError);
} else {
console.log({[`response to edit-note for ${tab.id}`]: resp});
}
}
);
} //editNoteOnClick
chrome.contextMenus.create({
id: 'editNote', title: _T('menuAddEditNoteThisTab'),
contexts: ['browser_action'], onclick: editNoteOnClick
});
//////////////////////////////////////////////////////////////////////////
// Messages //
function messageListener(request, sender, sendResponse)
{
console.log({'bg got message':request,from:sender});
if(!request || !request.msg) {
console.log('bg Bad request');
return;
}
// For now, only accept messages from our extension
if(!sender.id || sender.id !== chrome.runtime.id) {
console.log(`bg Bad id ${sender.id} (ours ${chrome.runtime.id})`);
return;
}
if(request.msg === MSG_GET_VIEW_WIN_ID && !request.response) {
console.log('Responding with window ID ' + me.viewWindowID.toString());
sendResponse({msg: request.msg, response: true, id: me.viewWindowID});
}
} //messageListener
chrome.runtime.onMessage.addListener(messageListener);
//////////////////////////////////////////////////////////////////////////
// MAIN //
// Create the main window when Chrome starts
if(true) {
callbackOnLoad(
function() {
console.log('TabFern: background window loaded');
if(S.getBool(S.POPUP_ON_STARTUP)) {
console.log('Opening popup window');
setTimeout(me.loadView, 500);
}
}
);
}
// Set the defaults for the options. The settings boilerplate from
// extensionizr does not appear to have this facility.
for(let opt in S.defaults) {
S.setIfNonexistent(opt, S.defaults[opt]);
}
S.set(S.PRUNE_NEW_WINDOWS, false); // don't prune - it's a Chrome bug
// See https://bugs.chromium.org/p/chromium/issues/detail?id=883709#c16
console.log('TabFern: done running background.js');
// vi: set ts=4 sts=4 sw=4 et ai fo-=o: //
});
;require.register("common/setting-definitions.js", function(exports, require, module) {
// setting-definitions.js: The TabFern settings, and setting-access functions
// Names of settings, and their defaults // {{{1
/// An object to stash the names of the settings in. The values here
/// are the keys for _DEF and _VAL.
let _NAM = { __proto__: null };
/// An object to stash the defaults in. Every property must have a default,
/// since the defaults array is also used to identify properties to be
/// saved/loaded. The JS types of the defaults must match the types
/// of the properties. The keys are the values of _NAM and the
/// keys of _VAL.
let _DEF = { __proto__: null };
/// An array of validators, used when loading settings. Each is a function
/// that returns a valid value for that setting, or `undefined` to use the
/// default. NOTE: returning "undefined" will trigger a warning on the console.
/// The keys are the values of _NAM and the keys of _VAL.
let _VAL = { __proto__: null };
/// The default validator for bool values
let _vbool = (v)=>{ return ((typeof v === 'boolean')?v:undefined)};
// Booleans {{{2
_NAM.CFG_POPUP_ON_STARTUP = 'open-popup-on-chrome-startup';
_DEF[_NAM.CFG_POPUP_ON_STARTUP] = true;
_VAL[_NAM.CFG_POPUP_ON_STARTUP] = _vbool;
_NAM.CFG_ENB_CONTEXT_MENU = 'ContextMenu.Enabled';
_DEF[_NAM.CFG_ENB_CONTEXT_MENU] = true;
_VAL[_NAM.CFG_ENB_CONTEXT_MENU] = _vbool;
_NAM.CFG_RESTORE_ON_LAST_DELETED = 'open-tree-on-restore-last-deleted';
_DEF[_NAM.CFG_RESTORE_ON_LAST_DELETED] = false;
_VAL[_NAM.CFG_RESTORE_ON_LAST_DELETED] = _vbool;
_NAM.CFG_JUMP_WITH_SORT_OPEN_TOP = 'jump-to-top-when-sort-open-to-top';
_DEF[_NAM.CFG_JUMP_WITH_SORT_OPEN_TOP] = true;
_VAL[_NAM.CFG_JUMP_WITH_SORT_OPEN_TOP] = _vbool;
_NAM.CFG_COLLAPSE_ON_STARTUP = 'collapse-trees-on-startup';
_DEF[_NAM.CFG_COLLAPSE_ON_STARTUP] = true;
_VAL[_NAM.CFG_COLLAPSE_ON_STARTUP] = _vbool;
_NAM.CFG_OPEN_TOP_ON_STARTUP = 'open-to-top-on-startup';
_DEF[_NAM.CFG_OPEN_TOP_ON_STARTUP] = false;
_VAL[_NAM.CFG_OPEN_TOP_ON_STARTUP] = _vbool;
_NAM.CFG_COLLAPSE_ON_WIN_CLOSE = 'collapse-tree-on-window-close';
_DEF[_NAM.CFG_COLLAPSE_ON_WIN_CLOSE] = true;
_VAL[_NAM.CFG_COLLAPSE_ON_WIN_CLOSE] = _vbool;
//_NAM.CFG_COLLAPSE_ON_PARTIAL_WIN_CLOSE = 'collapse-tree-on-partially-open-window-close';
//_DEF[_NAM.CFG_COLLAPSE_ON_PARTIAL_WIN_CLOSE] = true;
//_VAL[_NAM.CFG_COLLAPSE_ON_PARTIAL_WIN_CLOSE] = _vbool;
_NAM.CFG_HIDE_HORIZONTAL_SCROLLBARS = 'hide-horizontal-scrollbars';
_DEF[_NAM.CFG_HIDE_HORIZONTAL_SCROLLBARS] = true;
_VAL[_NAM.CFG_HIDE_HORIZONTAL_SCROLLBARS] = _vbool;
_NAM.CFG_SKINNY_SCROLLBARS = 'skinny-scrollbars';
_DEF[_NAM.CFG_SKINNY_SCROLLBARS] = false;
_VAL[_NAM.CFG_SKINNY_SCROLLBARS] = _vbool;
_NAM.CFG_NEW_WINS_AT_TOP = 'open-new-windows-at-top';
_DEF[_NAM.CFG_NEW_WINS_AT_TOP] = true;
_VAL[_NAM.CFG_NEW_WINS_AT_TOP] = _vbool;
_NAM.CFG_SHOW_TREE_LINES = 'show-tree-lines';
_DEF[_NAM.CFG_SHOW_TREE_LINES] = false;
_VAL[_NAM.CFG_SHOW_TREE_LINES] = _vbool;
_NAM.CFG_CONFIRM_DEL_OF_SAVED = 'confirm-del-of-saved-wins';
_DEF[_NAM.CFG_CONFIRM_DEL_OF_SAVED] = true;
_VAL[_NAM.CFG_CONFIRM_DEL_OF_SAVED] = _vbool;
_NAM.CFG_CONFIRM_DEL_OF_UNSAVED = 'confirm-del-of-unsaved-wins';
_DEF[_NAM.CFG_CONFIRM_DEL_OF_UNSAVED] = false;
_VAL[_NAM.CFG_CONFIRM_DEL_OF_UNSAVED] = _vbool;
_NAM.CFG_CONFIRM_DEL_OF_SAVED_TABS = 'confirm-del-of-saved-tabs';
_DEF[_NAM.CFG_CONFIRM_DEL_OF_SAVED_TABS] = true;
_VAL[_NAM.CFG_CONFIRM_DEL_OF_SAVED_TABS] = _vbool;
_NAM.CFG_CONFIRM_DEL_OF_UNSAVED_TABS = 'confirm-del-of-unsaved-tabs';
_DEF[_NAM.CFG_CONFIRM_DEL_OF_UNSAVED_TABS] = false;
_VAL[_NAM.CFG_CONFIRM_DEL_OF_UNSAVED_TABS] = _vbool;
_NAM.CFG_URL_IN_TOOLTIP = 'tooltip-has-url';
_DEF[_NAM.CFG_URL_IN_TOOLTIP] = false;
_VAL[_NAM.CFG_URL_IN_TOOLTIP] = _vbool;
_NAM.CFG_TITLE_IN_TOOLTIP = 'tooltip-has-title';
_DEF[_NAM.CFG_TITLE_IN_TOOLTIP] = false;
_VAL[_NAM.CFG_TITLE_IN_TOOLTIP] = _vbool;
_NAM.CFG_PRUNE_NEW_WINDOWS = 'prune-new-windows';
_DEF[_NAM.CFG_PRUNE_NEW_WINDOWS] = false;
_VAL[_NAM.CFG_PRUNE_NEW_WINDOWS] = ()=>false;
// Always false --- don't permit a settings load to set prune to true.
/// Not actually a setting, but an indicator that we loaded settings OK.
/// Used by src/settings/main.js.
_NAM.SETTINGS_LOADED_OK = '__settings_loaded_OK';
_DEF[_NAM.SETTINGS_LOADED_OK] = false;
_VAL[_NAM.SETTINGS_LOADED_OK] = ()=>{return undefined;}
// }}}2
// Strings and limited-choice controls such as radio buttons and dropdowns. {{{2
_NAM.CFGS_BACKGROUND = 'window-background';
_DEF[_NAM.CFGS_BACKGROUND] = '';
_VAL[_NAM.CFGS_BACKGROUND] = (v)=>{
if(!v) return '';
if(Validation.isValidColor(v)) return v;
if(Validation.isValidURL(v,
['file', 'https', 'data', 'chrome-extension'])) return v;
return undefined;
};
_NAM.CFGS_THEME_NAME = 'theme-name';
_DEF[_NAM.CFGS_THEME_NAME] = 'default-dark';
_VAL[_NAM.CFGS_THEME_NAME] = (v)=>{
return (( v === 'default-dark' || v === 'default' ) ? v : undefined);
};
_NAM.CFGS_SCROLLBAR_COLOR = 'skinny-scrollbar-color';
_DEF[_NAM.CFGS_SCROLLBAR_COLOR] = '';
_VAL[_NAM.CFGS_SCROLLBAR_COLOR] = (v)=>{
if(!v) return '';
return ((Validation.isValidColor(v)) ? v : undefined);
};
// #35. Whether to open closed tabs when you click on the tree item
// for a partially-open window. This is string, not bool, because the
// fancy-settings radio-button control provides a string value, not a Boolean.
_NAM.CFGS_OPEN_REST_ON_CLICK = 'open-rest-on-win-click';
const CFG_OROC_DO = "yep";
const CFG_OROC_DO_NOT = "nope";
_DEF[_NAM.CFGS_OPEN_REST_ON_CLICK] = CFG_OROC_DO_NOT;
_VAL[_NAM.CFGS_OPEN_REST_ON_CLICK] = (v)=>{
return (( v === CFG_OROC_DO || v === CFG_OROC_DO_NOT ) ? v : undefined);
};
// }}}2
/// The default values for the configuration settings.
const CFG_NAMES = Object.seal(_NAM);
const CFG_DEFAULTS = Object.seal(_DEF);
const CFG_VALIDATORS = Object.seal(_VAL);
////////////////////////////////////////////////////////////////////////// }}}1
// Setting-related functions // {{{1
const SETTING_PREFIX = 'store.settings.';
/// Get the raw value of a setting. Returns null if the key doesn't exist.
/// @param setting_name A value in CFG_NAMES
function getRawSetting(setting_name)
{
return localStorage.getItem(SETTING_PREFIX + setting_name);
} //getSetting
/// Get the string value of a setting, if it is a string.
/// @param setting_name A value in CFG_NAMES
/// @param default_value Optional default. If unspecified or
/// undefined, the default from CFG_DEFAULTS
/// is used.
function getStringSetting(setting_name, default_value = undefined)
{
if(typeof default_value === 'undefined' && setting_name in CFG_DEFAULTS) {
default_value = CFG_DEFAULTS[setting_name];
}
let locStorageValue = localStorage.getItem(SETTING_PREFIX + setting_name);
if ( locStorageValue !== null ) { // key exists
// Get the value, which is stored as JSON
try {
let val = JSON.parse(locStorageValue);
if(typeof val === 'string') return val;
} catch(e) {
// do nothing
}
}
// If we get here, we didn't have a value, or didn't have a string.
return String(default_value);
} //getStringSetting
/// Get a boolean setting from the settings page, which uses HTML5 localStorage.
/// @param setting_name A value in CFG_NAMES
/// @param default_value Optional default. If unspecified or
/// undefined, the default from CFG_DEFAULTS
/// is used.
function getBoolSetting(setting_name, default_value = undefined)
{
if(typeof default_value === 'undefined' && setting_name in CFG_DEFAULTS) {
default_value = CFG_DEFAULTS[setting_name];
}
let locStorageValue = localStorage.getItem(SETTING_PREFIX + setting_name);
if ( locStorageValue === null ) { // nonexistent key
return default_value;
} else { // Get the value, which is stored as JSON
let str = String(locStorageValue).toLowerCase();
if ( str === "false" ) {
return false;
} else if ( str === "true" ) {
return true;
} else {
return default_value;
}
}
} //getBoolSetting
/// Find out whether the given setting from the settings page exists.
/// @param setting_name A value in CFG_NAMES
function haveSetting(setting_name)
{
if(!setting_name) return false;
return (SETTING_PREFIX + setting_name) in localStorage;
} //haveSetting()
/// Set a setting (wow!).
/// @param setting_name {String} A value in CFG_NAMES
/// @param setting_value {mixed} The value, which must be
/// JSON.stringify()able.
function setSetting(setting_name, setting_value)
{
// TODO handle exceptions in some reasonable way.
localStorage.setItem(
SETTING_PREFIX + setting_name,
JSON.stringify(setting_value)
); // JSON stringify so we can store more than just strings.
} //setSetting
/// Set a setting only if it's not already there. Parameters are as
/// setSetting().
function setSettingIfNonexistent(setting_name, setting_value)
{
if(!haveSetting(setting_name)) setSetting(setting_name, setting_value);
}
/// Custom getter for the current theme name. This enforces known themes.
function getThemeName()
{
let theme = getStringSetting(CFG_NAMES.CFGS_THEME_NAME);
if( theme === 'default' || theme === 'default-dark') return theme;
else return CFG_DEFAULTS[CFGS_THEME_NAME];
} //getThemeName
////////////////////////////////////////////////////////////////////////// }}}1
// Exports // {{{1
/// The object we will export
let me = {
// settings
names: CFG_NAMES,
defaults: CFG_DEFAULTS,
validators: CFG_VALIDATORS,
// special values settings can take on
OROC_DO: CFG_OROC_DO,
OROC_DO_NOT: CFG_OROC_DO_NOT,
// special accessors
isOROC: ()=>(getStringSetting(CFG_NAMES.CFG_OPEN_REST_ON_CLICK) === CFG_OROC_DO),
// functions
getRaw: getRawSetting,
getString: getStringSetting,
getBool: getBoolSetting,
have: haveSetting,
set: setSetting,
setIfNonexistent: setSettingIfNonexistent,
getThemeName
};
// Each of the names is a property directly on the export object,
// with /^CFG/ removed for convenience.
for(let name in CFG_NAMES) {
me[name.replace(/^CFG_/,'').replace(/^CFG/,'')] = CFG_NAMES[name];
// CFG_FOO -> FOO; CFGS_FOO -> S_FOO
}
module.exports = me;
////////////////////////////////////////////////////////////////////////// }}}1
// vi: set fo-=o fdm=marker fdl=0: //
});
;require.alias("path-browserify", "path");
require.alias("process/browser.js", "process");process = require('process');require.register("___globals___", function(exports, require, module) {
});})();require('___globals___');
require('bg/background');
//# sourceMappingURL=background.js.map
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment