diff --git a/Makefile b/Makefile
index 260a7572..955fb47c 100644
--- a/Makefile
+++ b/Makefile
@@ -34,6 +34,7 @@ DEST_BUNDLE_2 = tabfern/src/view/bundle_tree.js
.PHONY: all bundle clean
all: bundle
+ ./check-version.sh
./check-webstore.sh
bundle: $(DEST_BUNDLE_1) $(DEST_BUNDLE_2)
diff --git a/check-version.sh b/check-version.sh
new file mode 100755
index 00000000..bc1155cd
--- /dev/null
+++ b/check-version.sh
@@ -0,0 +1,24 @@
+#!/bin/bash
+# check-version.sh: report version numbers in TabFern
+function check() {
+ ack_opts=( -m 1 --nocolor --nopager --output '$line_no: $line' )
+ # $filename is also available
+ echo -n "$1: "
+ ack "${ack_opts[@]}" VERSION "$1" ||
+ ack "${ack_opts[@]}" version "$1"
+}
+
+files=(package.json package-lock.json)
+
+for tree in tabfern webstore ; do
+ files+=(${tree}/manifest.json ${tree}/src/common/common.js)
+done
+
+for f in "${files[@]}" ; do
+ check "$f"
+done
+
+if [[ $1 = '-e' ]]; then
+ vi "${files[@]}"
+fi
+# vi: set ts=4 sts=4 sw=4 et ai: #
diff --git a/package-lock.json b/package-lock.json
index 9250ee52..470f2dbd 100755
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,6 +1,6 @@
{
"name": "tabfern",
- "version": "0.1.17.1337",
+ "version": "0.1.18.1337",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
diff --git a/package.json b/package.json
index 254c6653..2c158fb8 100755
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "tabfern",
- "version": "0.1.17.1337",
+ "version": "0.1.18.1337",
"description": "Google Chrome extension for displaying, saving, and managing tabs",
"main": "src/view/main.js",
"directories": {
diff --git a/tabfern/_locales/de/messages.json b/tabfern/_locales/de/messages.json
index 8609f660..88f4146a 100755
--- a/tabfern/_locales/de/messages.json
+++ b/tabfern/_locales/de/messages.json
@@ -214,6 +214,10 @@
"message": "Notiz hinzufügen oder bearbeiten"
,"description":"The context-menu item to add or edit a tab's note"
}
+ , "menuAddEditNoteThisTab": {
+ "message": "Notiz für den aktuellen Tab hinzufügen oder bearbeiten"
+ ,"description":"The extension-menu item to add or edit the current tab's note"
+ }
, "menuRename": {
"message": "Umbenennen"
,"description":"The context-menu item to rename a window's tree entry"
diff --git a/tabfern/_locales/en/messages.json b/tabfern/_locales/en/messages.json
index fc30c4a2..ff2a9056 100755
--- a/tabfern/_locales/en/messages.json
+++ b/tabfern/_locales/en/messages.json
@@ -84,6 +84,14 @@
}
}
}
+ , "dlgpTextToReplace": {
+ "message": "Text to replace? (/.../ for regex)"
+ ,"description":"Prompt for the user to enter text to replace"
+ }
+ , "dlgpReplacementText": {
+ "message": "Replacement text?"
+ ,"description":"Prompt for the user to enter text to replace"
+ }
, "dlgYesHTML": {
"message": "Yes"
@@ -215,6 +223,10 @@
"message": "Add/edit a note"
,"description":"The context-menu item to add or edit a tab's note"
}
+ , "menuAddEditNoteThisTab": {
+ "message": "Add/edit a note for the current tab"
+ ,"description":"The extension-menu item to add or edit the current tab's note"
+ }
, "menuRename": {
"message": "Rename"
,"description":"The context-menu item to rename a window's tree entry"
@@ -243,6 +255,18 @@
"message": "Delete"
,"description":"The context-menu item to delete a window or tab's tree item"
}
+ , "menuURLSubstitute": {
+ "message": "Replace in URLs"
+ ,"description":"The context-menu item to make a replacement in the URL of each tab in the window"
+ }
+ , "menuttURLSubstitute": {
+ "message": "Make a text replacement in all of the URLs in this window"
+ ,"description":"The context-menu tooltip for menuURLSubstitute"
+ }
+ , "menuMoveToTop": {
+ "message": "Move to top"
+ ,"description":"The context-menu item to move a window's tree item to the top of the tree"
+ }
, "error_text": { "message": "--------------------------------------------"
,"description": "Text for error messages" }
diff --git a/tabfern/assets/css/icons.css b/tabfern/assets/css/icons.css
index 6c54a6aa..7331aa75 100755
--- a/tabfern/assets/css/icons.css
+++ b/tabfern/assets/css/icons.css
@@ -60,6 +60,10 @@
background-image: url("/assets/icons/text_padding_top.png");
}
+.jstree-themeicon-custom.arrow-switch, .vakata-context .arrow-switch {
+ background-image: url("/assets/icons/arrow_switch.png");
+}
+
/* Class for icons with no content. Used in jstree.set_icon() when the
* icon is actually being set using CSS.
*/
@@ -91,12 +95,9 @@
content: url("/assets/icons/cross.png");
}
-/* Background sizes in context menu are different. TODO fix this --- it is
- * an ugly hack. */
-.vakata-context .fff-pencil,
-.vakata-context .fff-cross,
-.vakata-context .fff-picture-delete,
-.vakata-context .fff-text-padding-top {
+/* Background sizes of icons in context menu are different. Note: Need the
+ * `li a` to make it specific enough. */
+.vakata-context li a i {
background-repeat: no-repeat;
background-position: center center;
}
diff --git a/tabfern/assets/icons/arrow_switch.png b/tabfern/assets/icons/arrow_switch.png
new file mode 100644
index 00000000..258c16c6
Binary files /dev/null and b/tabfern/assets/icons/arrow_switch.png differ
diff --git a/tabfern/conf/require-config.js b/tabfern/conf/require-config.js
index a042a965..105623c9 100755
--- a/tabfern/conf/require-config.js
+++ b/tabfern/conf/require-config.js
@@ -50,10 +50,6 @@ var require = {
exports: 'BLAKE2s'
}
},
- async: {
- useHash: true // #callback=x rather than ?callback=x since Chrome
- // won't load files with ?
- },
};
// vi: set ts=4 sts=4 sw=4 et ai fo-=o fo-=r: //
diff --git a/tabfern/js/asq-helpers.js b/tabfern/js/asq-helpers.js
index 7f7b667b..970ec07d 100755
--- a/tabfern/js/asq-helpers.js
+++ b/tabfern/js/asq-helpers.js
@@ -1,25 +1,15 @@
// asq-helpers.js: Helpers for asynquence and Chrome callbacks.
(function (root, factory) {
- let imports=['asynquence-contrib'];
-
if (typeof define === 'function' && define.amd) {
// AMD
- define('asq-helpers',imports, factory);
+ define('asq-helpers', ['asynquence-contrib'], factory);
} else if (typeof exports === 'object') {
// Node, CommonJS-like
- let requirements = [];
- for(let modulename of imports) {
- requirements.push(require(modulename));
- }
- module.exports = factory(...requirements);
+ module.exports = factory(require('asynquence-contrib'));
} else {
// Browser globals (root is `window`)
- let requirements = [];
- for(let modulename of imports) {
- requirements.push(root[modulename]);
- }
- root.ASQH = factory(...requirements);
+ root.ASQH = factory(root.ASQ);
}
}(this, function (ASQ) {
"use strict";
diff --git a/tabfern/js/async.js b/tabfern/js/async.js
deleted file mode 100644
index 4786c106..00000000
--- a/tabfern/js/async.js
+++ /dev/null
@@ -1,46 +0,0 @@
-/** @license
- * RequireJS plugin for async dependency load like JSONP and Google Maps
- * Author: Miller Medeiros
- * Version: 0.1.2 (2014/02/24)
- * Released under the MIT license
- */
-define(function(){
-
- var DEFAULT_PARAM_NAME = 'callback',
- _uid = 0;
-
- function injectScript(src){
- var s, t;
- s = document.createElement('script'); s.type = 'text/javascript'; s.async = true; s.src = src;
- t = document.getElementsByTagName('script')[0]; t.parentNode.insertBefore(s,t);
- }
-
- function formatUrl(name, id, useHash){
- var separ = (useHash ? '#' : '?'),
- paramRegex = /!(.+)/,
- url = name.replace(paramRegex, ''),
- param = (paramRegex.test(name)) ? name.replace(/.+!/, '') : DEFAULT_PARAM_NAME;
- url += (url.indexOf(separ) < 0) ? separ : '&';
- return url + param +'='+ id;
- }
-
- function uid() {
- _uid += 1;
- return '__async_req_'+ _uid +'__';
- }
-
- return{
- load : function(name, req, onLoad, config){
- if(config.isBuild){
- onLoad(null); //avoid errors on the optimizer
- }else{
- var id = uid();
- //create a global variable that stores onLoad so callback
- //function can define new module after async load
- window[id] = onLoad;
- injectScript(formatUrl(req.toUrl(name), id, config.async.useHash));
- }
- }
- };
-});
-// vi: set ts=4 sts=4 sw=4 et ai fo-=o fo-=r: //
diff --git a/tabfern/js/buffer.js b/tabfern/js/buffer.js
index b84e1e22..c709bcee 100755
--- a/tabfern/js/buffer.js
+++ b/tabfern/js/buffer.js
@@ -2121,4 +2121,4 @@ module.exports = Array.isArray || function (arr) {
/***/ })
-/******/ ]);
\ No newline at end of file
+/******/ ]);
diff --git a/tabfern/js/export-file.js b/tabfern/js/export-file.js
index f6fc3fa0..63f700e0 100644
--- a/tabfern/js/export-file.js
+++ b/tabfern/js/export-file.js
@@ -11,10 +11,9 @@
module.exports = factory();
} else {
// Browser globals (root is window)
- root.Fileops = root.Fileops || {};
- root.Fileops.Export = factory();
+ root.ExportFile = factory();
}
-}(this, function ($) {
+}(this, function () {
/// Save the given text to the given filename. This is what is returned
/// by the module loader.
diff --git a/tabfern/js/import-file.js b/tabfern/js/import-file.js
index ed33bcaa..fc8f2129 100644
--- a/tabfern/js/import-file.js
+++ b/tabfern/js/import-file.js
@@ -11,8 +11,7 @@
module.exports = factory();
} else {
// Browser globals (root is window)
- root.Fileops = root.Fileops || {};
- root.Fileops.Importer = factory();
+ root.ImportFile = factory();
}
}(this, function () {
diff --git a/tabfern/js/jstree-multitype.js b/tabfern/js/jstree-multitype.js
index 8f76af8a..b0c968f6 100644
--- a/tabfern/js/jstree-multitype.js
+++ b/tabfern/js/jstree-multitype.js
@@ -20,7 +20,7 @@
else {
factory(jQuery, jQuery.jstree);
}
-}(function ($, jstree, undefined) {
+}(function ($, _jstree_unused, undefined) {
"use strict";
if($.jstree.plugins.multitype) { return; }
diff --git a/tabfern/js/jstree-redraw-event.js b/tabfern/js/jstree-redraw-event.js
index cca800cd..17d194de 100644
--- a/tabfern/js/jstree-redraw-event.js
+++ b/tabfern/js/jstree-redraw-event.js
@@ -24,17 +24,24 @@
if($.jstree.plugins.redraw_event) { return; }
$.jstree.plugins.redraw_event = function (options, parent) {
- //this._data.redraw_event = {reason: undefined};
+ this._data.redraw_event = {suppress: false};
/// Redraw.
/// @param {DOM object} obj The node being redrawn
/// @return the object, if the parent was able to redraw it.
this.redraw_node = function(obj, deep, callback, force_render) {
- obj = parent.redraw_node.apply(this, arguments);
- this.trigger('redraw_event', {obj: obj});
+ if(!this._data.redraw_event.suppress) {
+ obj = parent.redraw_node.apply(this, arguments);
+ this.trigger('redraw_event', {obj: obj});
+ }
return obj;
}; //redraw_node
+ /// Suppress redraw temporarily. EXPERIMENTAL.
+ this.suppress_redraw = function(whether_to) {
+ this._data.redraw_event.suppress = !!whether_to;
+ }
+
};
}));
diff --git a/tabfern/js/justhtmlescape.js b/tabfern/js/justhtmlescape.js
index 72fd9b2c..c6fc455c 100644
--- a/tabfern/js/justhtmlescape.js
+++ b/tabfern/js/justhtmlescape.js
@@ -3,7 +3,7 @@
/// Adapted from https://github.com/janl/mustache.js/blob/master/mustache.js
/// MIT license --- see end of file
-// Defines HTMLEscaper, which has escape(text) and unescape(text) functions.
+// Returns { escape(text), unescape(text) }.
(function (root, factory) {
if (typeof define === 'function' && define.amd) {
@@ -14,7 +14,7 @@
module.exports = factory();
} else {
// Browser globals (root is window)
- root.HTMLEscaper = factory();
+ root.JustHTMLEscape = factory();
}
}(this, function () {
diff --git a/tabfern/js/management.js b/tabfern/js/management.js
deleted file mode 100755
index cafdd8e9..00000000
--- a/tabfern/js/management.js
+++ /dev/null
@@ -1,57 +0,0 @@
-// management.js: Test of a management module.
-// Copyright (c) Chris White 2017. CC-BY-SA 4.0 International.
-// Load this using the async plugin,
-// https://github.com/millermedeiros/requirejs-plugins/blob/master/src/async.js
-
-// Code to check development status thanks to
-// https://stackoverflow.com/a/12833511/2877364 by
-// https://stackoverflow.com/users/1143495/konrad-dzwinel and
-// https://stackoverflow.com/users/934239/xan
-
-(function(root){
-
- /// The completion callback - call when the module is fully loaded
- let callback;
-
- /// Our worker function
- function with_info(info)
- {
- let obj = info;
- obj.isDevelMode = (info.installType === 'development');
- console.log({'Got info': obj});
- callback(obj); //complete module loading
- } //with_info
-
- // Stash the onload callback for later, when we are done loading
- // Thanks to https://stackoverflow.com/a/22745553/2877364 by
- // https://stackoverflow.com/users/140264/brice for
- // info about document.currentScript.
- if(!document.currentScript)
- throw new Error("Can't load --- I don't know what script I'm in");
-
- // VVV code from here to "^^^" is also available as CC-BY 4.0 International
-
- let script_url = document.currentScript.src;
-
- let url = new URL(script_url);
- let searchParams = new URLSearchParams(url.hash.slice(1));
- // Using the hash, not the query string, because Chrome won't load
- // chrome-extension resources with query strings.
-
- if(searchParams.has('callback')) {
- let cbk_name = searchParams.get('callback');
- callback = root[cbk_name];
- if(!callback) throw new Error(
- `Can't load --- I can't find the ${cbk_name} callback`);
-
- } else {
- throw new Error("Can't load --- I can't find a #callback=... param");
- }
-
- // ^^^
-
- // Fire off the loading
- chrome.management.getSelf(with_info);
-
-})(this);
-// vi: set ts=4 sts=4 sw=4 et ai fo-=o: //
diff --git a/tabfern/js/spin-packed.js b/tabfern/js/spin-packed.js
index 8a5c2d05..846afe62 100644
--- a/tabfern/js/spin-packed.js
+++ b/tabfern/js/spin-packed.js
@@ -292,4 +292,4 @@ function convertOffset(x, y, degrees) {
/***/ })
-/******/ ]);
\ No newline at end of file
+/******/ ]);
diff --git a/tabfern/manifest.json b/tabfern/manifest.json
index 6d9bacdc..1a8f695a 100755
--- a/tabfern/manifest.json
+++ b/tabfern/manifest.json
@@ -1,8 +1,8 @@
{
"name": "__MSG_wsLongName__",
"short_name": "__MSG_wsShortName__",
- "version": "0.1.17.1337",
- "version_name": "0.1.17",
+ "version": "0.1.18.1337",
+ "version_name": "0.1.18",
"offline_enabled": true,
"manifest_version": 2,
"minimum_chrome_version": "54",
diff --git a/tabfern/src/bg/background.js b/tabfern/src/bg/background.js
index 1ef6e284..7e4a6b83 100755
--- a/tabfern/src/bg/background.js
+++ b/tabfern/src/bg/background.js
@@ -133,7 +133,7 @@ function editNoteOnClick(info, tab)
} //editNoteOnClick
chrome.contextMenus.create({
- id: 'editNote', title: 'Add/edit a note for the current tab',
+ id: 'editNote', title: _T('menuAddEditNoteThisTab'),
contexts: ['browser_action'], onclick: editNoteOnClick
});
@@ -166,14 +166,6 @@ chrome.runtime.onMessage.addListener(messageListener);
// 'sample_setting': 'This is how you use Store.js to remember values'
//});
-
-////example of using a message handler from the inject scripts
-//chrome.extension.onMessage.addListener(
-// function(request, sender, sendResponse) {
-// chrome.pageAction.show(sender.tab.id);
-// sendResponse();
-// });
-
//////////////////////////////////////////////////////////////////////////
// MAIN //
@@ -181,7 +173,10 @@ chrome.runtime.onMessage.addListener(messageListener);
window.addEventListener('load',
function() {
console.log('TabFern: background window loaded');
- setTimeout(loadView, 500);
+ if(getBoolSetting(CFG_POPUP_ON_STARTUP)) {
+ console.log('Opening popup window');
+ setTimeout(loadView, 500);
+ }
},
{ 'once': true }
);
diff --git a/tabfern/src/common/common.js b/tabfern/src/common/common.js
index 9e6c98c8..8e56cd11 100755
--- a/tabfern/src/common/common.js
+++ b/tabfern/src/common/common.js
@@ -2,16 +2,17 @@
// 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 make this a UMD module?
+// ** TODO split this into UMD modules so the pieces can be loaded with or
+// without require.js.
console.log('TabFern common.js loading');
//////////////////////////////////////////////////////////////////////////
-// General constants //
+// General constants // {{{1
/// The TabFern extension friendly version number. Displayed in the
/// title bar of the popup window, so lowercase (no shouting!).
-const TABFERN_VERSION='0.1.17';
+const TABFERN_VERSION='0.1.18';
// When you change this, also update:
// - manifest.json: both the version and version_name
// - package.json
@@ -20,14 +21,14 @@ const TABFERN_VERSION='0.1.17';
// Design decision: version numbers follow semver.org.
// In the Chrome manifest, the version_name attribute tracks the above.
// The version attribute, `x.y.z.w`, which is compared in numeric order L-R,
-// is as follows: x.y.z track the above. w is the "-pre." number.
+// is as follows: x.y.z track the above. w is the "-pre." or "-rc." number.
// A release to the Chrome Web Store has w=1337.
// E.g., 1.2.3-pre.4 is `version='1.2.3.4'`, and 1.2.3 (release) is
// `version='1.2.3.1337'`.
// If you get up to -pre.1336, just bump the `z` value and reset `w` :) .
-//////////////////////////////////////////////////////////////////////////
-// Messages between parts of TabFern //
+////////////////////////////////////////////////////////////////////////// }}}1
+// Messages between parts of TabFern // {{{1
// The format of a message is
// { msg: [, anything else] }
@@ -36,8 +37,8 @@ const TABFERN_VERSION='0.1.17';
const MSG_GET_VIEW_WIN_ID = 'getViewWindowID';
const MSG_EDIT_TAB_NOTE = 'editTabNote';
-//////////////////////////////////////////////////////////////////////////
-// Names of settings, and their defaults //
+////////////////////////////////////////////////////////////////////////// }}}1
+// Names of settings, and their defaults // {{{1
/// An array to build the defaults in. Every property must have a default,
/// since the defaults array is also used to identify properties to be
@@ -51,7 +52,11 @@ let _DEF = { __proto__: null };
let _VAL = { __proto__: null };
let _vbool = (v)=>{ return ((typeof v === 'boolean')?v:undefined)};
-// Booleans
+// Booleans {{{2
+const CFG_POPUP_ON_STARTUP = 'open-popup-on-chrome-startup';
+_DEF[CFG_POPUP_ON_STARTUP] = true;
+_VAL[CFG_POPUP_ON_STARTUP] = _vbool;
+
const CFG_ENB_CONTEXT_MENU = 'ContextMenu.Enabled';
_DEF[CFG_ENB_CONTEXT_MENU] = true;
_VAL[CFG_ENB_CONTEXT_MENU] = _vbool;
@@ -122,8 +127,6 @@ const SETTINGS_LOADED_OK = '__settings_loaded_OK';
_DEF[SETTINGS_LOADED_OK] = false;
_VAL[SETTINGS_LOADED_OK] = ()=>{return undefined;}
-
-
// Not yet implemented - pending #35. Whether to open closed tabs when
// you click on the tree item for a partially-open window.
//const CFG_OPEN_REST_ON_CLICK = 'open-rest-on-win-click',
@@ -131,7 +134,8 @@ _VAL[SETTINGS_LOADED_OK] = ()=>{return undefined;}
// CFG_OROC_DO_NOT = false;
//_DEF[CFG_OPEN_REST_ON_CLICK] = CFG_OROC_DO_NOT;
-// Strings, including limited-choice controls such as radio buttons and dropdowns.
+// }}}2
+// Strings and limited-choice controls such as radio buttons and dropdowns. {{{2
const CFGS_BACKGROUND = 'window-background';
_DEF[CFGS_BACKGROUND] = '';
_VAL[CFGS_BACKGROUND] = (v)=>{
@@ -155,12 +159,13 @@ _VAL[CFGS_SCROLLBAR_COLOR] = (v)=>{
return ((Validation.isValidColor(v)) ? v : undefined);
};
+// }}}2
/// The default values for the configuration settings.
const CFG_DEFAULTS = Object.seal(_DEF);
const CFG_VALIDATORS = Object.seal(_VAL);
-//////////////////////////////////////////////////////////////////////////
-// Test for Firefox //
+////////////////////////////////////////////////////////////////////////// }}}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
@@ -197,8 +202,8 @@ BROWSER_TYPE=null; // unknown
}
})(window);
-//////////////////////////////////////////////////////////////////////////
-// Setting-related functions //
+////////////////////////////////////////////////////////////////////////// }}}1
+// Setting-related functions // {{{1
const SETTING_PREFIX = 'store.settings.';
@@ -288,8 +293,8 @@ function getThemeName()
else return CFG_DEFAULTS[CFGS_THEME_NAME];
} //getThemeName
-//////////////////////////////////////////////////////////////////////////
-// DOM-related functions //
+////////////////////////////////////////////////////////////////////////// }}}1
+// DOM-related functions // {{{1
/// Append a
-
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
+