-
-
Save devinrhode2/86ae0e9e8629ae01ccb0adb142debba4 to your computer and use it in GitHub Desktop.
public-ff/bg/
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(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