diff --git a/test/more-placeholders.json b/test/more-placeholders.json index 7d403f05..9fc5312b 100644 --- a/test/more-placeholders.json +++ b/test/more-placeholders.json @@ -1,98 +1 @@ -{ - "version": "6.1", - "createdBy": "Clippings/wx", - "userClippingsRoot": [ - { - "name": "New demo clippings", - "children": [ - { - "name": "Multi-select placehldr w/ default value containing special chars", - "content": "$[NAME]\n\n$[PlaceholderWithSpecialChars{Whoa!|This is OK?|Foo/bar _baz_|La-a|Censored profanity: @#%&!|The \"Best\" thing 'ever'|Lotsa Ca$h|£22.00 ea., in GBP|¥99 Japanese Yen|süß (German for «sweet» as in taste)|EUR €99.88|*Hello, world!?*|¡Hola! 89¢ per item}]\n\nTested with love on $[HOSTAPP]", - "shortcutKey": "", - "sourceURL": "", - "label": "", - "seq": 0 - }, - { - "name": "Normal placeholder w/ default value containing special chars", - "content": "$[NAME]\nHello, cruel world!\n\n$[PlaceholderWithSpecialChars{Whoa! This is OK? Foo/bar _baz_ La-a is an actual name! Censored profanity: @#%&! The \"Best\" thing 'ever'. Lotsa Ca$h: £22.00 ea. in GBP, ¥99 Japanese Yen... süß (German for «sweet» as in taste) ... EUR €99.88 -- *Hello, world!?* ¡Hola! 89¢ per item}]\n\nGoodbye, cruel world!", - "shortcutKey": "", - "sourceURL": "", - "label": "", - "seq": 2 - }, - { - "name": "Moment.js test", - "content": "Today's date and time, expressed in various formats:\n\nDefault date: $[DATE]\nLong format: $[DATE(MMMM D, YYYY)]\nLong format with abbreviated month: $[DATE(MMM. DD, YYYY)]\nFull format with day of week: $[DATE(dddd, MMMM Do, YYYY)]\nShort American format: $[DATE(MM/DD/YYYY)]\n\nDefault time: $[TIME]\nNorth American time: $[TIME(h:mm A)]\n\nISO date: $[DATE(YYYY-MM-DD)]\nBritish date: $[DATE(D MMMM YYYY)]\n\nMilitary time with seconds: $[DATE(HH:mm:ss)]\n\nDate and time: $[DATE(ddd, MMM DD, YYYY hh:mm a)]\nDate and time as two separate placeholders: $[DATE(dddd, MMMM Do, YYYY)] @ $[TIME(h:mm A)]", - "shortcutKey": "", - "sourceURL": "", - "label": "", - "seq": 5 - }, - { - "name": "Multi-select placehldr w/ default value containing special chars #2", - "content": "$[NAME]\n\n$[PlaceholderWithSpecialChars{Whoa!|This is OK?|Foo/bar _baz_|La-a|Censored profanity: @#%&!|The \"Best\" thing 'ever'|Lotsa Ca$h|£22.00 ea., in GBP|¥99 Japanese Yen|süß (German for «sweet» as in taste)|EUR €99.88|*Hello, world!?*|¡Hola! 89¢ per item}]\n\nTested on $[HOSTAPP] $[beta{beta|}]\n$[UA]", - "shortcutKey": "", - "sourceURL": "", - "label": "", - "seq": 1 - }, - { - "name": "Placeholders used multiple times", - "content": "$[salutation] $[name],\n\nMy name is $[senderName{Adrianna|Casi|Holly|Jewelya|Poesia|Riley|Taya|Veronica}]. I think you are awesome! Keep doing what you're doing, $[name]!\n\nHave a nice day, $[name]! :)\n\n$[closing{Yours truly|Best regards|Love}],\n\n$[senderName]", - "shortcutKey": "", - "sourceURL": "", - "label": "", - "seq": 3 - }, - { - "name": "Single placeholder used multiple times", - "content": "Hello $[name], how are you? It was really nice meeting you, $[name]. Have a nice day, $[name]!", - "shortcutKey": "", - "sourceURL": "", - "label": "", - "seq": 4 - } - ], - "seq": 1 - }, - { - "name": "Placeholder values with Symbols & Emoji", - "children": [ - { - "name": "Symbols and emoji", - "content": "Arrows: $[arrows{←|→|⇑|⇓|⇿}]\nMathematical Operators: $[math{∀|∈|∞|⋿}]\nMiscellaneous Symbols: $[misc{☀|☘|♠|♫|⛩}]\nDingbats: $[dingbats{✅|❌|➿}]\nMiscellaneous Symbols and Pictographs: $[pictographs{🌀|🌎|🌞|🍅|🍕|🏀|👍|💖|💰|🔔|🔥|🗿}]\nEmoticons (Emoji): $[emoji{😀|😃|😄|😆|😎|🙁|🙂|🙏}]\nTransport and Map Symbols: $[transport{🚀|🚘|🚨}]\nSupplemental Symbols and Pictographs: $[supplmtSymbols{🤘|🤠|🤣|🥑|🦷|🧿}]", - "shortcutKey": "", - "sourceURL": "", - "label": "", - "seq": 0 - }, - { - "name": "macOS symbols and emoji", - "content": "Emoji - Smileys and People: $[smileys{😀|👍|🌂}]\nEmoji - Animals and Nature: $[nature{🍄|🌵|🐶|☔}]\nEmoji - Food and Drink: $[food{🍏|🍕|🧂}]\nEmoji - Activity: $[activity{⚽️|🏆|🧩}]\nEmoji - Travel and Places: $[travel{🚗|✈️|⛩|🌁}]\nEmoji - Objects: $[objects{📀|⚙|🔓}]\nEmoji - Symbols: $[symbols{❤️|☮️|❌|🔴|🟦|🔔|🕧}]\nEmoji - Flags: $[flags{🏁|🏴‍☠️|🚩|🏳️|🏴|🏳️‍🌈|🇨🇦|🇺🇸|🇿🇼}]\nBullets and Stars: $[bullets{●|★|☆|❖|※}]\nCurrency: $[currency{¢|€|£|₧}]\nLetter-like: $[letterLike{®|©|™|℅}]\nMath Symbols: $[math{±|×|℮|∅|∑|√}]\nParentheses: $[parens{【|】|‹|›|«|»}]\nPictographs: $[picto{☀|☯|☮|⚑}]\nPunctuation: $[punct{¡|§|¶}]\n**Unsupported Punctuation: $[unsupportedPunct{¡|~}]\nTechnical Symbols: $[tech{⌘|⇥|⌦|⌥|⏏|⎈}]", - "shortcutKey": "", - "sourceURL": "", - "label": "", - "seq": 1 - }, - { - "name": "Embedded symbols and emoji", - "content": "I feel $[emoji{happy 😃|sad 🙁|angry 😡|funny 🤣|cool 😎}]!\n\nMy favorite symbol is: $[symbol{← left arrow|⇒ double right arrow|★ star|® registered trademark}] ", - "shortcutKey": "", - "sourceURL": "", - "label": "", - "seq": 2 - }, - { - "name": "Test Clipping for issue #418", - "content": "Unicode symbols / emojis not supported in placeholders' default values (#418)\n\n$[foo{bar|baz}]\n$[quux{←|→}]\n$[qux{💪|🚀}]", - "shortcutKey": "", - "sourceURL": "", - "label": "", - "seq": 3 - } - ], - "seq": 2 - } - ] -} +{"version":"6.1","createdBy":"Clippings/mx","userClippingsRoot":[{"name":"New demo clippings","children":[{"name":"Multi-select placehldr w/ default value containing special chars","content":"$[NAME]\n\n$[PlaceholderWithSpecialChars{Whoa!|This is OK?|Foo/bar _baz_|La-a|Censored profanity: @#%&!|The \"Best\" thing 'ever'|Lotsa Ca$h|£22.00 ea., in GBP|¥99 Japanese Yen|süß (German for «sweet» as in taste)|EUR €99.88|*Hello, world!?*|¡Hola! 89¢ per item}]\n\nTested with love on $[HOSTAPP]","shortcutKey":"","sourceURL":"","label":"","seq":0},{"name":"Normal placeholder w/ default value containing special chars","content":"$[NAME]\nHello, cruel world!\n\n$[PlaceholderWithSpecialChars{Whoa! This is OK? Foo/bar _baz_ La-a is an actual name! Censored profanity: @#%&! The \"Best\" thing 'ever'. Lotsa Ca$h: £22.00 ea. in GBP, ¥99 Japanese Yen... süß (German for «sweet» as in taste) ... EUR €99.88 -- *Hello, world!?* ¡Hola! 89¢ per item}]\n\nGoodbye, cruel world!","shortcutKey":"","sourceURL":"","label":"","seq":2},{"name":"Moment.js test","content":"Today's date and time, expressed in various formats:\n\nDefault date: $[DATE]\nLong format: $[DATE(MMMM D, YYYY)]\nLong format with abbreviated month: $[DATE(MMM. DD, YYYY)]\nFull format with day of week: $[DATE(dddd, MMMM Do, YYYY)]\nShort American format: $[DATE(MM/DD/YYYY)]\n\nDefault time: $[TIME]\nNorth American time: $[TIME(h:mm A)]\n\nISO date: $[DATE(YYYY-MM-DD)]\nBritish date: $[DATE(D MMMM YYYY)]\n\nMilitary time with seconds: $[DATE(HH:mm:ss)]\n\nDate and time: $[DATE(ddd, MMM DD, YYYY hh:mm a)]\nDate and time as two separate placeholders: $[DATE(dddd, MMMM Do, YYYY)] @ $[TIME(h:mm A)]","shortcutKey":"","sourceURL":"","label":"","seq":5},{"name":"Multi-select placehldr w/ default value containing special chars #2","content":"$[NAME]\n\n$[PlaceholderWithSpecialChars{Whoa!|This is OK?|Foo/bar _baz_|La-a|Censored profanity: @#%&!|The \"Best\" thing 'ever'|Lotsa Ca$h|£22.00 ea., in GBP|¥99 Japanese Yen|süß (German for «sweet» as in taste)|EUR €99.88|*Hello, world!?*|¡Hola! 89¢ per item}]\n\nTested on $[HOSTAPP] $[beta{beta|}]\n$[UA]","shortcutKey":"","sourceURL":"","label":"","seq":1},{"name":"Placeholders used multiple times","content":"$[salutation] $[name],\n\nMy name is $[senderName{Adrianna|Casi|Holly|Jewelya|Poesia|Riley|Taya|Veronica}]. I think you are awesome! Keep doing what you're doing, $[name]!\n\nHave a nice day, $[name]! :)\n\n$[closing{Yours truly|Best regards|Love}],\n\n$[senderName]","shortcutKey":"","sourceURL":"","label":"","seq":3},{"name":"Single placeholder used multiple times","content":"Hello $[name], how are you? It was really nice meeting you, $[name]. Have a nice day, $[name]!","shortcutKey":"","sourceURL":"","label":"","seq":4}],"seq":1},{"name":"Placeholder values with Symbols & Emoji","children":[{"name":"Symbols & emoji","content":"Arrows: $[arrows{←|→|⇑|⇓|⇿}]\nMathematical Operators: $[math{∀|∈|∞|⋿}]\nMiscellaneous Symbols: $[misc{☀|☘|♠|♫|⛩}]\nDingbats: $[dingbats{✅|❌|➿}]\nMiscellaneous Symbols and Pictographs: $[pictographs{🌀|🌎|🌞|🍅|🍕|🏀|👍|💖|💰|🔔|🔥|🗿}]\nEmoticons (Emoji): $[emoji{😀|😃|😄|😆|😎|🙁|🙂|🙏}]\nTransport and Map Symbols: $[transport{🚀|🚘|🚨}]\nSupplemental Symbols and Pictographs: $[supplmtSymbols{🤘|🤠|🤣|🥑|🦷|🧿}]","shortcutKey":"","sourceURL":"","label":"","seq":0},{"name":"macOS symbols & emoji","content":"Emoji - Smileys and People: $[smileys{😀|👍|🌂}]\nEmoji - Animals and Nature: $[nature{🍄|🌵|🐶|☔}]\nEmoji - Food and Drink: $[food{🍏|🍕|🧂}]\nEmoji - Activity: $[activity{⚽️|🏆|🧩}]\nEmoji - Travel and Places: $[travel{🚗|✈️|⛩|🌁}]\nEmoji - Objects: $[objects{📀|⚙|🔓}]\nEmoji - Symbols: $[symbols{❤️|☮️|❌|🔴|🟦|🔔|🕧}]\nEmoji - Flags: $[flags{🏁|🏴‍☠️|🚩|🏳️|🏴|🏳️‍🌈|🇨🇦|🇺🇸|🇿🇼}]\nBullets and Stars: $[bullets{●|★|☆|❖|※}]\nCurrency: $[currency{¢|€|£|₧}]\nLetter-like: $[letterLike{®|©|™|℅}]\nMath Symbols: $[math{±|×|℮|∅|∑|√}]\nParentheses: $[parens{【|】|‹|›|«|»}]\nPictographs: $[picto{☀|☯|☮|⚑}]\nPunctuation: $[punct{¡|§|¶}]\n**Unsupported Punctuation: $[unsupportedPunct{¡|~}]\nTechnical Symbols: $[tech{⌘|⇥|⌦|⌥|⏏|⎈}]","shortcutKey":"","sourceURL":"","label":"","seq":1},{"name":"Embedded symbols & emoji","content":"I feel $[emoji{happy 😃|sad 🙁|angry 😡|funny 🤣|cool 😎}]!\n\nMy favorite symbol is: $[symbol{← left arrow|⇒ double right arrow|★ star|® registered trademark}] ","shortcutKey":"","sourceURL":"","label":"","seq":2},{"name":"Test Clipping for issue #418","content":"Unicode symbols / emojis not supported in placeholders' default values (#418)\n\n$[foo{bar|baz}]\n$[quux{←|→}]\n$[qux{💪|🚀}]","shortcutKey":"","sourceURL":"","label":"","seq":3}],"seq":2}]} \ No newline at end of file diff --git a/wx-src/background.js b/wx-src/background.js index f5448338..f92af2d4 100644 --- a/wx-src/background.js +++ b/wx-src/background.js @@ -6,20 +6,15 @@ const ROOT_FOLDER_NAME = "clippings-root"; -let gOS = null; -let gHostAppName = null; -let gHostAppVer; -let gAutoIncrPlchldrs = null; +// These globals are used only briefly or during initialization, so they don't +// need to be saved to extension storage. let gClippingMenuItemIDMap = {}; let gFolderMenuItemIDMap = {}; -let gSyncFldrID = null; -let gBackupRemIntervalID = null; -let gIsReloadingSyncFldr = false; -let gSyncClippingsHelperDwnldPgURL; -let gForceShowFirstTimeBkupNotif = false; let gPrefersColorSchemeMedQry; let gIsFirstRun = false; let gIsMajorVerUpdate = false; +let gSetDisplayOrderOnRootItems = false; +let gIsReloadingSyncFldr = false; let gClippingsListener = { _isImporting: false, @@ -139,15 +134,17 @@ let gSyncClippingsListener = { // turning on Sync Clippings from extension preferences. }, - onDeactivate(aOldSyncFolderID) + async onDeactivate(aOldSyncFolderID) { log("Clippings/wx: gSyncClippingsListener.onDeactivate()"); - if (gPrefs.cxtMenuSyncItemsOnly) { + let cxtMenuSyncItemsOnly = await aePrefs.getPref("cxtMenuSyncItemsOnly"); + if (cxtMenuSyncItemsOnly) { return; } - - let syncFldrMenuID = gFolderMenuItemIDMap[aOldSyncFolderID]; + + let fldrMenuItemIDMap = await aePrefs.getPref("_folderMenuItemIDMap"); + let syncFldrMenuID = fldrMenuItemIDMap[aOldSyncFolderID]; // Change the icon on the "Synced Clippings" folder to be a normal // folder icon. @@ -159,11 +156,13 @@ let gSyncClippingsListener = { browser.menus.update(syncFldrMenuID, mnuIco); }, - onAfterDeactivate(aRemoveSyncFolder, aOldSyncFolderID) + async onAfterDeactivate(aRemoveSyncFolder, aOldSyncFolderID) { + let prefs = await aePrefs.getAllPrefs(); + function resetCxtMenuSyncItemsOnlyOpt(aRebuildCxtMenu) { - if (gPrefs.cxtMenuSyncItemsOnly) { - aePrefs.setPrefs({ cxtMenuSyncItemsOnly: false }); + if (prefs.cxtMenuSyncItemsOnly) { + aePrefs.setPrefs({cxtMenuSyncItemsOnly: false}); } if (aRebuildCxtMenu) { rebuildContextMenu(); @@ -196,7 +195,8 @@ let gSyncClippingsListener = { rebuildContextMenu(); log("Clippings/wx: gSyncClippingsListener.onReloadFinish(): Setting static IDs on synced items that don't already have them."); - let isStaticIDsAdded = await addStaticIDs(gSyncFldrID); + let syncFldrID = await aePrefs.getPref("syncFolderID"); + let isStaticIDsAdded = await addStaticIDs(syncFldrID); if (isStaticIDsAdded) { log("Clippings/wx: gSyncClippingsListener.onReloadFinish(): Static IDs added to synced items. Saving sync file."); @@ -273,16 +273,6 @@ let gPlaceholders = { } }; -let gWndIDs = { - newClipping: null, - keyboardPaste: null, - placeholderPrmt: null, - clippingsMgr: null -}; - -let gPrefs = null; -let gSetDisplayOrderOnRootItems = false; - // // Post-installation event handler @@ -301,16 +291,21 @@ browser.runtime.onInstalled.addListener(async (aInstall) => { } else { log(`Clippings/wx: Updating from version ${oldVer} to ${currVer}`); - - // Detect upgrade to version 6.5, which doesn't have any new prefs. - if (aeVersionCmp(oldVer, "6.5") < 0) { - gIsMajorVerUpdate = true; - } } } }); +// +// Browser startup +// + +browser.runtime.onStartup.addListener(async () => { + log("Clippings/wx: Resetting persistent background script data during browser startup"); + await aePrefs.setDefaultBkgdState(); +}); + + // // Browser window and Clippings menu initialization // @@ -319,67 +314,72 @@ void async function () { log("Clippings/wx: WebExtension startup initiated."); - gPrefs = await aePrefs.getAllPrefs(); + let prefs = await aePrefs.getAllPrefs(); log("Clippings/wx: Successfully retrieved user preferences:"); - log(gPrefs); + log(prefs); // Check for and set user prefs (if not already set) from all previous // versions of Clippings. This needs to be performed at every startup since // it is not possible to determine if this function is called after the // WebExtension is installed, updated, reloaded, or loaded during host app // startup. - if (! aePrefs.hasUserPrefs(gPrefs)) { + if (! aePrefs.hasUserPrefs(prefs)) { log("Initializing Clippings user preferences."); gIsFirstRun = true; - await aePrefs.setUserPrefs(gPrefs); + await aePrefs.setUserPrefs(prefs); } - if (! aePrefs.hasSanDiegoPrefs(gPrefs)) { + if (! aePrefs.hasSanDiegoPrefs(prefs)) { gSetDisplayOrderOnRootItems = true; log("Initializing 6.1 user preferences."); - await aePrefs.setSanDiegoPrefs(gPrefs); + await aePrefs.setSanDiegoPrefs(prefs); } - if (! aePrefs.hasBalboaParkPrefs(gPrefs)) { - gForceShowFirstTimeBkupNotif = true; + if (! aePrefs.hasBalboaParkPrefs(prefs)) { log("Initializing 6.1.2 user preferences."); - await aePrefs.setBalboaParkPrefs(gPrefs); + await aePrefs.setBalboaParkPrefs(prefs); } - if (! aePrefs.hasMalibuPrefs(gPrefs)) { + if (! aePrefs.hasMalibuPrefs(prefs)) { log("Initializing 6.2 user preferences."); - await aePrefs.setMalibuPrefs(gPrefs); + await aePrefs.setMalibuPrefs(prefs); } - if (! aePrefs.hasTopangaPrefs(gPrefs)) { + if (! aePrefs.hasTopangaPrefs(prefs)) { log("Initializing 6.2.1 user preferences."); - await aePrefs.setTopangaPrefs(gPrefs); + await aePrefs.setTopangaPrefs(prefs); } - if (! aePrefs.hasHuntingdonPrefs(gPrefs)) { + if (! aePrefs.hasHuntingdonPrefs(prefs)) { log("Initializing 6.3 user preferences."); - await aePrefs.setHuntingdonPrefs(gPrefs); + await aePrefs.setHuntingdonPrefs(prefs); } - if (! aePrefs.hasSanClementePrefs(gPrefs)) { + if (! aePrefs.hasSanClementePrefs(prefs)) { log("Initializing 6.4 user preferences."); - await aePrefs.setSanClementePrefs(gPrefs); + await aePrefs.setSanClementePrefs(prefs); } - if (! aePrefs.hasModestoPrefs(gPrefs)) { + if (! aePrefs.hasModestoPrefs(prefs)) { log("Initializing 6.5.2 user preferences."); - await aePrefs.setModestoPrefs(gPrefs); + await aePrefs.setModestoPrefs(prefs); + } + + if (! aePrefs.hasSanFranciscoPrefs(prefs)) { + log("Initializing 7.0 user preferences and MV3 background script state persistence."); + await aePrefs.setSanFranciscoPrefs(prefs); + gIsMajorVerUpdate = true; } - if (gPrefs.clippingsMgrDetailsPane) { - gPrefs.clippingsMgrAutoShowDetailsPane = false; + if (prefs.clippingsMgrDetailsPane) { + aePrefs.setPrefs({clippingsMgrAutoShowDetailsPane: false}); } - init(); + init(prefs); }(); -async function init() +async function init(aPrefs) { log("Clippings/wx: Initializing integration with host app..."); @@ -390,19 +390,15 @@ async function init() browser.runtime.getPlatformInfo(), ]); - gHostAppName = brws.name; - gHostAppVer = brws.version; - log(`Clippings/wx: Host app: ${gHostAppName} (version ${gHostAppVer})`); - - gOS = platform.os; - log("Clippings/wx: OS: " + gOS); + log(`Clippings/wx: Host app: ${brws.name} (version ${brws.version})`); + log("Clippings/wx: OS: " + platform.os); - if (gOS == "linux" && gPrefs.clippingsMgrMinzWhenInactv === null) { - await aePrefs.setPrefs({ clippingsMgrMinzWhenInactv: true }); + if (platform.os == "linux" && aPrefs.clippingsMgrMinzWhenInactv === null) { + await aePrefs.setPrefs({clippingsMgrMinzWhenInactv: true}); } - if (gPrefs.autoAdjustWndPos === null) { - let autoAdjustWndPos = gOS == "win"; + if (aPrefs.autoAdjustWndPos === null) { + let autoAdjustWndPos = platform.os == "win"; let clippingsMgrSaveWndGeom = autoAdjustWndPos; await aePrefs.setPrefs({autoAdjustWndPos, clippingsMgrSaveWndGeom}); } @@ -412,20 +408,19 @@ async function init() handlePrefersColorSchemeChange(gPrefersColorSchemeMedQry); gPrefersColorSchemeMedQry.addEventListener("change", handlePrefersColorSchemeChange); - if (gPrefs.syncClippings) { - gSyncFldrID = gPrefs.syncFolderID; - + if (aPrefs.syncClippings) { // The context menu will be built when refreshing the sync data. + // Do this even when the background script is restarted in order to pick up + // any changes to sync data. refreshSyncedClippings(true); } else { - buildContextMenu(); + rebuildContextMenu(); } - aeClippingSubst.init(navigator.userAgent, gPrefs.autoIncrPlcHldrStartVal); - gAutoIncrPlchldrs = new Set(); + aeClippingSubst.init(navigator.userAgent, aPrefs.autoIncrPlcHldrStartVal); - if (gPrefs.backupRemFirstRun && !gPrefs.lastBackupRemDate) { + if (aPrefs.backupRemFirstRun && !aPrefs.lastBackupRemDate) { aePrefs.setPrefs({ lastBackupRemDate: new Date().toString(), }); @@ -435,7 +430,7 @@ async function init() setWhatsNewNotificationDelay(); } else { - if (gPrefs.upgradeNotifCount > 0) { + if (aPrefs.upgradeNotifCount > 0) { // Show post-update notification in 1 minute. browser.alarms.create("show-upgrade-notifcn", { delayInMinutes: aeConst.POST_UPGRADE_NOTIFCN_DELAY_MS / 60000 @@ -448,19 +443,19 @@ async function init() delayInMinutes: aeConst.BACKUP_REMINDER_DELAY_MS / 60000 }); - if (gPrefs.syncClippings && gPrefs.syncHelperCheckUpdates) { + if (aPrefs.syncClippings && aPrefs.syncHelperCheckUpdates) { // Check for updates to Sync Clippings Helper native app in 10 minutes. browser.alarms.create("show-sync-helper-upd-notifcn", { delayInMinutes: aeConst.SYNC_HELPER_CHECK_UPDATE_DELAY_MS / 60000 }); } - if (gPrefs.tabModalMsgBox) { + if (aPrefs.tabModalMsgBox) { let tabs = await browser.tabs.query({}); tabs.forEach(aTab => {initContentCSS(aTab.id)}); } - if (gPrefs.showWelcome) { + if (aPrefs.showWelcome) { openWelcomePage(); aePrefs.setPrefs({showWelcome: false}); } @@ -540,11 +535,12 @@ function addStaticIDs(aFolderID) async function enableSyncClippings(aIsEnabled) { let clippingsDB = aeClippings.getDB(); + let syncFolderID = await aePrefs.getPref("syncFolderID"); if (aIsEnabled) { log("Clippings/wx: enableSyncClippings(): Turning ON"); - if (gSyncFldrID === null) { + if (syncFolderID === null) { log("Clippings/wx: enableSyncClippings(): Creating the Synced Clippings folder."); let syncFldr = { name: browser.i18n.getMessage("syncFldrName"), @@ -553,24 +549,23 @@ async function enableSyncClippings(aIsEnabled) isSync: true, }; try { - gSyncFldrID = await clippingsDB.folders.add(syncFldr); + syncFolderID = await clippingsDB.folders.add(syncFldr); } catch (e) { console.error("Clippings/wx: enableSyncClippings(): Failed to create the Synced Clipping folder: " + e); } - await aePrefs.setPrefs({ syncFolderID: gSyncFldrID }); - log("Clippings/wx: enableSyncClippings(): Synced Clippings folder ID: " + gSyncFldrID); - return gSyncFldrID; + await aePrefs.setPrefs({syncFolderID}); + log("Clippings/wx: enableSyncClippings(): Synced Clippings folder ID: " + syncFolderID); + return syncFolderID; } } else { log("Clippings/wx: enableSyncClippings(): Turning OFF"); - let oldSyncFldrID = gSyncFldrID; + let oldSyncFldrID = syncFolderID; - let numUpd = await clippingsDB.folders.update(gSyncFldrID, { isSync: undefined }); - await aePrefs.setPrefs({ syncFolderID: null }); - gSyncFldrID = null; + let numUpd = await clippingsDB.folders.update(syncFolderID, { isSync: undefined }); + await aePrefs.setPrefs({syncFolderID: null}); return oldSyncFldrID; } } @@ -580,6 +575,8 @@ async function refreshSyncedClippings(aRebuildClippingsMenu) { log("Clippings/wx: refreshSyncedClippings(): Retrieving synced clippings from the Sync Clippings helper app..."); + let platform = await browser.runtime.getPlatformInfo(); + let prefs = await aePrefs.getAllPrefs(); let clippingsDB = aeClippings.getDB(); let natMsg = {msgID: "get-synced-clippings"}; let resp; @@ -598,7 +595,7 @@ async function refreshSyncedClippings(aRebuildClippingsMenu) } if (aRebuildClippingsMenu) { - buildContextMenu(); + buildContextMenu(platform.os, prefs); } return; } @@ -611,7 +608,9 @@ async function refreshSyncedClippings(aRebuildClippingsMenu) throw new Error("Clippings/wx: refreshSyncedClippings(): Response data from native app is invalid"); } - if (gSyncFldrID === null) { + let syncFolderID = await aePrefs.getPref("syncFolderID"); + + if (syncFolderID === null) { log("Clippings/wx: The Synced Clippings folder is missing. Creating it..."); let syncFldr = { name: browser.i18n.getMessage("syncFldrName"), @@ -619,36 +618,38 @@ async function refreshSyncedClippings(aRebuildClippingsMenu) displayOrder: 0, }; - gSyncFldrID = await clippingsDB.folders.add(syncFldr); + syncFolderID = await clippingsDB.folders.add(syncFldr); } - log("Clippings/wx: refreshSyncedClippings(): Synced Clippings folder ID: " + gSyncFldrID); + log("Clippings/wx: refreshSyncedClippings(): Synced Clippings folder ID: " + syncFolderID); - await aePrefs.setPrefs({syncFolderID: gSyncFldrID}); + await aePrefs.setPrefs({syncFolderID}); gSyncClippingsListener.onReloadStart(); log("Clippings/wx: Purging existing items in the Synced Clippings folder..."); - await purgeFolderItems(gSyncFldrID, true); + await purgeFolderItems(syncFolderID, true); log("Clippings/wx: Importing clippings data from sync file..."); // Method aeImportExport.importFromJSON() is asynchronous, so the import // may not yet be finished when this function has finished executing! aeImportExport.setDatabase(clippingsDB); - aeImportExport.importFromJSON(syncJSONData, false, false, gSyncFldrID); + aeImportExport.importFromJSON(syncJSONData, false, false, syncFolderID); + let afterSyncFldrReloadDelay = await aePrefs.getPref("afterSyncFldrReloadDelay"); setTimeout(function () { gSyncClippingsListener.onReloadFinish(); - }, gPrefs.afterSyncFldrReloadDelay); + }, afterSyncFldrReloadDelay); } async function pushSyncFolderUpdates() { - if (!gPrefs.syncClippings || gSyncFldrID === null) { + let prefs = await aePrefs.getAllPrefs(); + if (!prefs.syncClippings || prefs.syncFolderID === null) { throw new Error("Sync Clippings is not turned on!"); } - let syncData = await aeImportExport.exportToJSON(true, true, gSyncFldrID, false, true); + let syncData = await aeImportExport.exportToJSON(true, true, prefs.syncFolderID, false, true); let natMsg = { msgID: "set-synced-clippings", syncData: syncData.userClippingsRoot, @@ -705,7 +706,8 @@ function purgeFolderItems(aFolderID, aKeepFolder) async function getShortcutKeyPrefixStr() { let rv = ""; - let isMacOS = getOS() == "mac"; + let platform = await browser.runtime.getPlatformInfo(); + let isMacOS = platform.os == "mac"; let [cmd] = await browser.commands.getAll(); let shct = cmd.shortcut; let keybPasteKey = shct.substring(shct.lastIndexOf("+") + 1); @@ -777,7 +779,7 @@ async function getShortcutKeyPrefixStr() } -function getContextMenuData(aFolderID) +function getContextMenuData(aFolderID, aPrefs) { function fnSortMenuItems(aItem1, aItem2) { @@ -787,15 +789,6 @@ function getContextMenuData(aFolderID) } return rv; } - - function sanitizeMenuTitle(aTitle) - { - // Escape the ampersand character, which would normally be used to denote - // the access key for the menu item. - let rv = aTitle.replace(/&/g, "&&"); - - return rv; - } // END nested functions let rv = []; @@ -814,7 +807,7 @@ function getContextMenuData(aFolderID) // Submenu icon let iconPath = "img/folder.svg"; - if (aItem.id == gSyncFldrID) { + if (aItem.id == aPrefs.syncFolderID) { // Firefox bug on macOS: // Dark Mode setting isn't applied to the browser context menu when // a Firefox dark color theme is used. @@ -825,7 +818,7 @@ function getContextMenuData(aFolderID) iconPath = "img/synced-clippings.svg"; } } - submenuItemData.icons = { 16: iconPath }; + submenuItemData.icons = {16: iconPath}; if (! ("displayOrder" in aItem)) { submenuItemData.displayOrder = 0; @@ -838,8 +831,7 @@ function getContextMenuData(aFolderID) let parentFldrMenuItemID = gFolderMenuItemIDMap[aFolderID]; submenuItemData.parentId = parentFldrMenuItemID; } - - getContextMenuData(aItem.id).then(aSubmenuData => { + getContextMenuData(aItem.id, aPrefs).then(aSubmenuData => { aSubmenuData.sort(fnSortMenuItems); submenuItemData.submenuItems = aSubmenuData; rv.push(submenuItemData); @@ -864,7 +856,7 @@ function getContextMenuData(aFolderID) else { menuItemData.displayOrder = aItem.displayOrder; } - + if (aFolderID != aeConst.ROOT_FOLDER_ID) { let fldrMenuItemID = gFolderMenuItemIDMap[aFolderID]; menuItemData.parentId = fldrMenuItemID; @@ -884,7 +876,7 @@ function getContextMenuData(aFolderID) getContextMenuData.isDarkMode = null; -function buildContextMenu() +function buildContextMenu(aPlatformOS, aPrefs) { log("Clippings/wx: buildContextMenu()"); @@ -893,18 +885,18 @@ function buildContextMenu() id: "ae-clippings-reset-autoincr-plchldrs", title: browser.i18n.getMessage("baMenuResetAutoIncrPlaceholders"), enabled: false, - contexts: ["browser_action"], + contexts: ["action"], documentUrlPatterns: [""] }); let prefsMnuStrKey = "mnuPrefs"; - if (gOS == "win") { + if (aPlatformOS == "win") { prefsMnuStrKey = "mnuPrefsWin"; } browser.menus.create({ id: "ae-clippings-prefs", title: browser.i18n.getMessage(prefsMnuStrKey), - contexts: ["browser_action"], + contexts: ["action"], }); // Context menu for web page textbox or HTML editor. @@ -914,7 +906,6 @@ function buildContextMenu() contexts: ["editable", "selection"], documentUrlPatterns: [""] }); - browser.menus.create({ id: "ae-clippings-manager", title: browser.i18n.getMessage("cxtMenuOpenClippingsMgr"), @@ -923,18 +914,27 @@ function buildContextMenu() }); let rootFldrID = aeConst.ROOT_FOLDER_ID; - if (gPrefs.syncClippings && gPrefs.cxtMenuSyncItemsOnly) { - rootFldrID = gSyncFldrID; + if (aPrefs.syncClippings && aPrefs.cxtMenuSyncItemsOnly) { + rootFldrID = aPrefs.syncFolderID; } - getContextMenuData(rootFldrID).then(aMenuData => { + gFolderMenuItemIDMap = aPrefs._clippingMenuItemIDMap; + gClippingMenuItemIDMap = aPrefs._folderMenuItemIDMap; + + getContextMenuData(rootFldrID, aPrefs).then(aMenuData => { if (aeConst.DEBUG) { console.log("buildContextMenu(): Menu data: "); console.log(aMenuData); } + + aePrefs.setPrefs({ + _clippingMenuItemIDMap: gClippingMenuItemIDMap, + _folderMenuItemIDMap: gFolderMenuItemIDMap, + }); if (aMenuData.length > 0) { browser.menus.create({ + id: "ae-clippings-submenu-separator", type: "separator", contexts: ["editable"], documentUrlPatterns: [""] @@ -971,17 +971,17 @@ function buildContextMenuHelper(aMenuData) } -function updateContextMenuForFolder(aUpdatedFolderID) +async function updateContextMenuForFolder(aUpdatedFolderID) { let id = Number(aUpdatedFolderID); let clippingsDB = aeClippings.getDB(); - clippingsDB.folders.get(id).then(aResult => { - let menuItemID = gFolderMenuItemIDMap[id]; - if (menuItemID) { - browser.menus.update(menuItemID, {title: aResult.name}); - } - }); + let folder = await clippingsDB.folders.get(id); + let fldrMenuItemIDMap = await aePrefs.getPref("_folderMenuItemIDMap"); + let menuItemID = fldrMenuItemIDMap[id]; + if (menuItemID) { + browser.menus.update(menuItemID, {title: sanitizeMenuTitle(folder.name)}); + } } @@ -992,35 +992,47 @@ async function rebuildContextMenu() gClippingMenuItemIDMap = {}; gFolderMenuItemIDMap = {}; - buildContextMenu(); + await aePrefs.setPrefs({ + _clippingMenuItemIDMap: {}, + _folderMenuItemIDMap: {}, + }); + + let platform = await browser.runtime.getPlatformInfo(); + let prefs = await aePrefs.getAllPrefs(); + buildContextMenu(platform.os, prefs); } -function handlePrefersColorSchemeChange(aMediaQuery) +async function handlePrefersColorSchemeChange(aMediaQuery) { getContextMenuData.isDarkMode = aMediaQuery.matches; + let syncClippings = await aePrefs.getPref("syncClippings"); + // Changes to the Dark Mode setting only affects the Synced Clippings folder // menu icon. - if (gPrefs.syncClippings) { + if (syncClippings) { rebuildContextMenu(); } } -function buildAutoIncrementPlchldrResetMenu(aAutoIncrPlchldrs) +async function buildAutoIncrementPlchldrResetMenu(aAutoIncrPlchldrs) { + let autoIncrPlchldrs = await aePrefs.getPref("_autoIncrPlchldrs"); + let autoIncrPlchldrsSet = new Set(autoIncrPlchldrs); let enabledResetMenu = false; aAutoIncrPlchldrs.forEach(async (aItem, aIndex, aArray) => { - if (! gAutoIncrPlchldrs.has(aItem)) { - gAutoIncrPlchldrs.add(aItem); + if (! autoIncrPlchldrsSet.has(aItem)) { + autoIncrPlchldrsSet.add(aItem); + await aePrefs.setPrefs({_autoIncrPlchldrs: [...autoIncrPlchldrsSet]}); let menuItem = { id: `ae-clippings-reset-autoincr-${aItem}`, title: `#[${aItem}]`, parentId: "ae-clippings-reset-autoincr-plchldrs", - contexts: ["browser_action"], + contexts: ["action"], documentUrlPatterns: [""] }; @@ -1040,11 +1052,14 @@ async function resetAutoIncrPlaceholder(aPlaceholder) { log(`Clippings/wx: resetAutoIncrPlaceholder(): Resetting placeholder: #[${aPlaceholder}]`); - aeClippingSubst.resetAutoIncrementVar(aPlaceholder); - gAutoIncrPlchldrs.delete(aPlaceholder); + await aeClippingSubst.resetAutoIncrementVar(aPlaceholder); + let autoIncrPlchldrs = await aePrefs.getPref("_autoIncrPlchldrs"); + let autoIncrPlchldrsSet = new Set(autoIncrPlchldrs); + autoIncrPlchldrsSet.delete(aPlaceholder); await browser.menus.remove(`ae-clippings-reset-autoincr-${aPlaceholder}`); + aePrefs.setPrefs({_autoIncrPlchldrs: [...autoIncrPlchldrsSet]}); - if (gAutoIncrPlchldrs.size == 0) { + if (autoIncrPlchldrsSet.size == 0) { browser.menus.update("ae-clippings-reset-autoincr-plchldrs", {enabled: false}); } } @@ -1052,16 +1067,17 @@ async function resetAutoIncrPlaceholder(aPlaceholder) async function showBackupNotification() { - if (gPrefs.backupRemFrequency == aeConst.BACKUP_REMIND_NEVER) { + let prefs = await aePrefs.getAllPrefs(); + if (prefs.backupRemFrequency == aeConst.BACKUP_REMIND_NEVER) { return; } let today = new Date(); - let lastBackupRemDate = new Date(gPrefs.lastBackupRemDate); + let lastBackupRemDate = new Date(prefs.lastBackupRemDate); let diff = new aeDateDiff(today, lastBackupRemDate); let numDays = 0; - switch (gPrefs.backupRemFrequency) { + switch (prefs.backupRemFrequency) { case aeConst.BACKUP_REMIND_DAILY: numDays = 1; break; @@ -1092,8 +1108,8 @@ async function showBackupNotification() break; } - if (diff.days >= numDays || gForceShowFirstTimeBkupNotif) { - if (gPrefs.backupRemFirstRun) { + if (diff.days >= numDays || prefs._forceShowFirstTimeBkupNotif) { + if (prefs.backupRemFirstRun) { info("Clippings/wx: showBackupNotification(): Showing first-time backup reminder."); await browser.notifications.create("backup-reminder-firstrun", { @@ -1103,21 +1119,21 @@ async function showBackupNotification() iconUrl: "img/notifIcon.svg", }); - aePrefs.setPrefs({ + await aePrefs.setPrefs({ backupRemFirstRun: false, backupRemFrequency: aeConst.BACKUP_REMIND_WEEKLY, lastBackupRemDate: new Date().toString(), }); - if (gForceShowFirstTimeBkupNotif) { + if (prefs._forceShowFirstTimeBkupNotif) { setBackupNotificationInterval(); - gForceShowFirstTimeBkupNotif = false; + aePrefs.setPrefs({_forceShowFirstTimeBkupNotif: false}); } } else { - info("Clippings/wx: showBackupNotification(): Last backup reminder: " + gPrefs.lastBackupRemDate); + info("Clippings/wx: showBackupNotification(): Last backup reminder: " + prefs.lastBackupRemDate); - if (gPrefs.skipBackupRemIfUnchg && gPrefs.clippingsUnchanged) { + if (prefs.skipBackupRemIfUnchg && prefs.clippingsUnchanged) { log("Clippings/wx: No changes to clippings since last backup; skipping backup notification."); } else { @@ -1129,7 +1145,7 @@ async function showBackupNotification() }); setBackupNotificationInterval(); - aePrefs.setPrefs({ lastBackupRemDate: new Date().toString() }); + aePrefs.setPrefs({lastBackupRemDate: new Date().toString()}); } } } @@ -1182,25 +1198,27 @@ async function showWhatsNewNotification() iconUrl: "img/notifIcon.svg", }); - let upgradeNotifCount = gPrefs.upgradeNotifCount - 1; + let upgradeNotifCount = await aePrefs.getPref("upgradeNotifCount"); + upgradeNotifCount -= 1; aePrefs.setPrefs({upgradeNotifCount}); } async function showSyncHelperUpdateNotification() { - if (!gPrefs.syncClippings || !gPrefs.syncHelperCheckUpdates) { + let prefs = await aePrefs.getAllPrefs(); + if (!prefs.syncClippings || !prefs.syncHelperCheckUpdates) { return; } let today, lastUpdateCheck, diff; - if (gPrefs.lastSyncHelperUpdChkDate) { + if (prefs.lastSyncHelperUpdChkDate) { today = new Date(); - lastUpdateCheck = new Date(gPrefs.lastSyncHelperUpdChkDate); + lastUpdateCheck = new Date(prefs.lastSyncHelperUpdChkDate); diff = new aeDateDiff(today, lastUpdateCheck); } - if (!gPrefs.lastSyncHelperUpdChkDate || diff.days >= aeConst.SYNC_HELPER_CHECK_UPDATE_FREQ_DAYS) { + if (!prefs.lastSyncHelperUpdChkDate || diff.days >= aeConst.SYNC_HELPER_CHECK_UPDATE_FREQ_DAYS) { let currVer = ""; let natMsg = {msgID: "get-app-version"}; let resp; @@ -1234,17 +1252,17 @@ async function showSyncHelperUpdateNotification() if (aeVersionCmp(currVer, updateInfo.latestVersion) < 0) { info(`Clippings/wx: showSyncHelperUpdateNotification(): Found a newer version of Sync Clippings Helper! Current version: ${currVer}; new version found: ${updateInfo.latestVersion}`); - gSyncClippingsHelperDwnldPgURL = updateInfo.downloadPageURL; + await aePrefs.setPrefs({ + _syncClippingsHelperDwnldPgURL: updateInfo.downloadPageURL, + lastSyncHelperUpdChkDate: new Date().toString(), + }); + browser.notifications.create("sync-helper-update", { type: "basic", title: browser.i18n.getMessage("syncUpdateTitle"), message: browser.i18n.getMessage("syncUpdateMsg"), iconUrl: "img/syncClippingsApp.svg", }); - - aePrefs.setPrefs({ - lastSyncHelperUpdChkDate: new Date().toString() - }); } } } @@ -1260,6 +1278,7 @@ async function openWelcomePage() async function openClippingsManager(aBackupMode) { + let prefs = await aePrefs.getAllPrefs(); let clippingsMgrURL = browser.runtime.getURL("pages/clippingsMgr.html"); let wnd = await browser.windows.getCurrent(); @@ -1275,16 +1294,16 @@ async function openClippingsManager(aBackupMode) let height = 410; let topOffset = 200; let left, top; - let wndGeom = gPrefs.clippingsMgrWndGeom; + let wndGeom = prefs.clippingsMgrWndGeom; - if (gPrefs.clippingsMgrSaveWndGeom && wndGeom) { + if (prefs.clippingsMgrSaveWndGeom && wndGeom) { width = wndGeom.w - 1; // Compensate for workaround to popup window bug. height = wndGeom.h; left = wndGeom.x; top = wndGeom.y; } else { - if (gPrefs.autoAdjustWndPos) { + if (prefs.autoAdjustWndPos) { wndGeom = await getWndGeometryFromBrwsTab(); log("Clippings/wx: openClippingsManager() > openClippingsMgrHelper(): Calculating initial geometry of Clippings Manager. Retrieved window geometry of browser window:"); log(wndGeom); @@ -1323,35 +1342,33 @@ async function openClippingsManager(aBackupMode) }; let wnd = await browser.windows.create(wndInfo); - gWndIDs.clippingsMgr = wnd.id; + let wndIDs = await aePrefs.getPref("_wndIDs"); + wndIDs.clippingsMgr = wnd.id; + aePrefs.setPrefs({_wndIDs: wndIDs}); + + // TO DO: This might not be needed anymore. browser.history.deleteUrl({ url: clippingsMgrURL }); // Workaround to bug where window position isn't set when calling // `browser.windows.create()`. If unable to get window geometry, then // default to centering on screen. if (wndGeom) { - browser.windows.update(wnd.id, { left, top }); + browser.windows.update(wnd.id, {left, top}); } } // END nested function - // The `gPrefs` object is null if the "Run in Private Windows" setting was - // turned on or off. This renders Clippings unusable until Firefox is - // restarted. - if (! (gPrefs instanceof Object)) { - alertEx("msgRunInPrivateChg", true); - return; - } - - if (gWndIDs.clippingsMgr) { + let wndIDs = await aePrefs.getPref("_wndIDs"); + if (wndIDs.clippingsMgr) { try { - let wnd = await browser.windows.get(gWndIDs.clippingsMgr); - browser.windows.update(gWndIDs.clippingsMgr, {focused: true}); + let wnd = await browser.windows.get(wndIDs.clippingsMgr); + browser.windows.update(wndIDs.clippingsMgr, {focused: true}); } catch { // Handle dangling ref to previously-closed Clippings Manager window // because it was closed before it finished initializing. - gWndIDs.clippingsMgr = null; + wndIDs.clippingsMgr = null; + aePrefs.setPrefs({_wndIDs: wndIDs}); openClippingsMgrHelper(); } } @@ -1396,23 +1413,36 @@ async function newClipping(aActiveTab) let name = aeClippings.createClippingNameFromText(content); let url = aActiveTab.url; - gNewClipping.set({name, content, url}); - openNewClippingDlg(); + + let platform = await browser.runtime.getPlatformInfo(); + openNewClippingDlg(platform.os); } -function openNewClippingDlg() +function openNewClippingDlg(aPlatformOS) { let url = browser.runtime.getURL("pages/new.html"); let height = 416; - if (gOS == "win") { + if (aPlatformOS == "win") { height = 448; } openDlgWnd(url, "newClipping", {type: "popup", width: 432, height}); } +function getNewClippingData() +{ + let rv = null; + let newClipping = gNewClipping.get(); + if (newClipping !== null) { + rv = newClipping; + } + + return rv; +} + + function openKeyboardPasteDlg(aTabID) { // TO DO: Check first if the cursor is in a web page textbox or HTML editor. @@ -1442,17 +1472,18 @@ function openPlaceholderPromptDlg(aTabID) } -function openBackupDlg() +async function openBackupDlg() { let url = browser.runtime.getURL("pages/backup.html"); let lang = browser.i18n.getUILanguage(); let height = 412; + let platform = await browser.runtime.getPlatformInfo(); - if (lang == "uk" || (lang == "fr" && gOS == "mac")) { + if (lang == "uk" || (lang == "fr" && platform.os == "mac")) { height = 450; } - openDlgWnd(url, "backupFirstRun", { type: "popup", width: 590, height }); + openDlgWnd(url, "backupFirstRun", {type: "popup", width: 590, height}); } @@ -1460,11 +1491,12 @@ async function openDlgWnd(aURL, aWndKey, aWndPpty) { async function openDlgWndHelper() { + let autoAdjustWndPos = await aePrefs.getPref("autoAdjustWndPos"); let width = aWndPpty.width; let height = aWndPpty.height; let left, top, wndGeom; - if (gPrefs.autoAdjustWndPos) { + if (autoAdjustWndPos) { wndGeom = await getWndGeometryFromBrwsTab(); log("Clippings/wx: openDlgWnd() > openDlgWndHelper(): Window geometry of browser window:"); log(wndGeom); @@ -1505,7 +1537,11 @@ async function openDlgWnd(aURL, aWndKey, aWndPpty) left, top, }); - gWndIDs[aWndKey] = wnd.id; + let wndIDs = await aePrefs.getPref("_wndIDs"); + wndIDs[aWndKey] = wnd.id; + aePrefs.setPrefs({_wndIDs: wndIDs}); + + // TO DO: This might not be needed anymore. browser.history.deleteUrl({ url: aURL }); // Workaround to bug where window position isn't set when calling @@ -1517,13 +1553,15 @@ async function openDlgWnd(aURL, aWndKey, aWndPpty) } // END nested function - if (gWndIDs[aWndKey]) { + let wndIDs = await aePrefs.getPref("_wndIDs"); + if (wndIDs[aWndKey]) { try { - await browser.windows.get(gWndIDs[aWndKey]); - browser.windows.update(gWndIDs[aWndKey], { focused: true }); + await browser.windows.get(wndIDs[aWndKey]); + browser.windows.update(wndIDs[aWndKey], {focused: true}); } catch (e) { - gWndIDs[aWndKey] = null; + wndIDs[aWndKey] = null; + aePrefs.setPrefs({_wndIDs: wndIDs}); openDlgWndHelper(); }; } @@ -1680,7 +1718,8 @@ async function pasteClipping(aClippingInfo, aIsExternalRequest, aTabID) let autoIncrPlchldrs = aeClippingSubst.getAutoIncrPlaceholders(processedCtnt); if (autoIncrPlchldrs.length > 0) { buildAutoIncrementPlchldrResetMenu(autoIncrPlchldrs); - processedCtnt = aeClippingSubst.processAutoIncrPlaceholders(processedCtnt); + processedCtnt = await aeClippingSubst.processAutoIncrPlaceholders(processedCtnt); + await aeClippingSubst.saveAutoIncrementVars(); } let plchldrs = aeClippingSubst.getCustomPlaceholders(processedCtnt); @@ -1699,30 +1738,40 @@ async function pasteClipping(aClippingInfo, aIsExternalRequest, aTabID) async function pasteProcessedClipping(aClippingContent, aTabID) { + // Focus the target window and tab to ensure that the clipping is + // successfully pasted into the web page. + let tab; + try { + tab = await browser.tabs.get(aTabID); + } + catch (e) { + // Browser tab was closed. + warn("Clippings/wx: pasteProcessedClipping(): Can't find browser tab " + aTabID); + return; + } + await browser.windows.update(tab.windowId, {focused: true}); + + let prefs = await aePrefs.getAllPrefs(); let msg = { msgID: "paste-clipping", content: aClippingContent, - htmlPaste: gPrefs.htmlPaste, - autoLineBreak: gPrefs.autoLineBreak, - dispatchInputEvent: gPrefs.dispatchInputEvent, - useInsertHTMLCmd: gPrefs.useInsertHTMLCmd, + htmlPaste: prefs.htmlPaste, + autoLineBreak: prefs.autoLineBreak, + dispatchInputEvent: prefs.dispatchInputEvent, + useInsertHTMLCmd: prefs.useInsertHTMLCmd, }; log(`Clippings/wx: Extension sending message "paste-clipping" to content script (active tab ID = ${aTabID})`); log(msg); - // The placeholder prompt or keyboard paste dialog may not be fully closed - // when the message is sent to the content script, which can't insert the - // clipping if the web page doesn't have focus. - // Work around by sending message after a short delay. - setTimeout(async () => { - try { - await browser.tabs.sendMessage(aTabID, msg); - } - catch (e) { - console.error("Clippings/wx: pasteProcessedClipping(): Failed to paste clipping: " + e); - } - }, 150); + await browser.tabs.sendMessage(aTabID, msg); +} + + +async function openSyncClippingsDownloadPage() +{ + let syncClippingsHelperDwnldPgURL = await aePrefs.getPref("_syncClippingsHelperDwnldPgURL"); + browser.tabs.create({url: syncClippingsHelperDwnldPgURL}); } @@ -1741,12 +1790,6 @@ function showSyncErrorNotification() // Utility functions // -function getOS() -{ - return gOS; -} - - async function initContentCSS(aTabID) { try { @@ -1758,13 +1801,24 @@ async function initContentCSS(aTabID) } +function sanitizeMenuTitle(aTitle) +{ + // Escape the ampersand character, which would normally be used to denote + // the access key for the menu item. + let rv = aTitle.replace(/&/g, "&&"); + + return rv; +} + + async function alertEx(aMessageID, aUsePopupWnd=false) { let message = browser.i18n.getMessage(aMessageID); info("Clippings/wx: " + message); + let prefs = await aePrefs.getAllPrefs(); let [tab] = await browser.tabs.query({active: true, currentWindow: true}); - if (gPrefs && gPrefs.tabModalMsgBox && tab && !aUsePopupWnd) { + if (prefs.tabModalMsgBox && tab && !aUsePopupWnd) { let activeTabID = tab.id; let tabInfo = await browser.tabs.get(activeTabID); @@ -1810,7 +1864,7 @@ async function alertEx(aMessageID, aUsePopupWnd=false) let left = 256; let top = 64; - if (gPrefs && gPrefs.autoAdjustWndPos) { + if (prefs.autoAdjustWndPos) { wndGeom = await getWndGeometryFromBrwsTab(); if (wndGeom) { @@ -1838,23 +1892,36 @@ async function alertEx(aMessageID, aUsePopupWnd=false) left, top, }); - gWndIDs[wndKey] = wnd.id; + let wndIDs = prefs._wndIDs; + wndIDs[wndKey] = wnd.id; + aePrefs.setPrefs({_wndIDs: wndIDs}); + + // TO DO: This might not be needed anymore browser.history.deleteUrl({ url }); // Workaround to bug where window position isn't correctly set when calling // `browser.windows.create()`. If unable to get window geometry, then default // to centering on screen. if (wndGeom) { - browser.windows.update(wnd.id, { left, top }); + browser.windows.update(wnd.id, {left, top}); } } +async function resetWndID(aWndID) +{ + let wndIDs = await aePrefs.getPref("_wndIDs"); + wndIDs[aWndID] = null; + await aePrefs.setPrefs({_wndIDs: wndIDs}); +} + + + // // Event handlers // -browser.browserAction.onClicked.addListener(aTab => { +browser.action.onClicked.addListener(aTab => { openClippingsManager(); }); @@ -1862,12 +1929,14 @@ browser.browserAction.onClicked.addListener(aTab => { browser.commands.onCommand.addListener(async (aCmdName, aTab) => { info(`Clippings/wx: Command "${aCmdName}" invoked!`); + let keyboardPaste = await aePrefs.getPref("keyboardPaste"); + // The aTab parameter is undefined - see Bugzilla bug: // https://bugzilla.mozilla.org/show_bug.cgi?id=1843866 // Expected to be fixed in Firefox 126. let [tab] = await browser.tabs.query({active: true, currentWindow: true}); - if (aCmdName == "ae-clippings-paste-clipping" && gPrefs.keyboardPaste) { + if (aCmdName == "ae-clippings-paste-clipping" && keyboardPaste) { log(`Clippings/wx: Active tab ID: ${tab.id} - opening keyboard paste dialog.`); openKeyboardPasteDlg(tab.id); } @@ -1936,7 +2005,7 @@ browser.notifications.onClicked.addListener(aNotifID => { break; case "sync-helper-update": - browser.tabs.create({url: gSyncClippingsHelperDwnldPgURL}); + openSyncClippingsDownloadPage(); break; case "whats-new": @@ -1954,8 +2023,6 @@ browser.storage.onChanged.addListener((aChanges, aAreaName) => { let changedPrefs = Object.keys(aChanges); for (let pref of changedPrefs) { - gPrefs[pref] = aChanges[pref].newValue; - if (pref == "autoIncrPlcHldrStartVal") { aeClippingSubst.setAutoIncrementStartValue(aChanges[pref].newValue); } @@ -1967,35 +2034,23 @@ browser.runtime.onMessage.addListener(aRequest => { log(`Clippings/wx: Received message "${aRequest.msgID}"`); switch (aRequest.msgID) { - case "get-env-info": - return Promise.resolve({ - os: gOS, - hostAppName: gHostAppName, - hostAppVer: gHostAppVer, - }); - case "init-new-clipping-dlg": - let newClipping = gNewClipping.get(); - if (newClipping !== null) { - newClipping.saveSrcURL = gPrefs.alwaysSaveSrcURL; - newClipping.checkSpelling = gPrefs.checkSpelling; - return Promise.resolve(newClipping); - } + return Promise.resolve(getNewClippingData()); break; case "init-placeholder-prmt-dlg": return Promise.resolve(gPlaceholders.get()); case "close-new-clipping-dlg": - gWndIDs.newClipping = null; + resetWndID("newClipping"); break; case "close-clippings-mgr-wnd": - gWndIDs.clippingsMgr = null; + resetWndID("clippingsMgr"); break; case "close-keybd-paste-dlg": - gWndIDs.keyboardPaste = null; + resetWndID("keyboardPaste"); break; case "paste-shortcut-key": @@ -2014,7 +2069,7 @@ browser.runtime.onMessage.addListener(aRequest => { return Promise.resolve(pasteProcessedClipping(aRequest.processedContent, aRequest.browserTabID)); case "close-placeholder-prmt-dlg": - gWndIDs.placeholderPrmt = null; + resetWndID("placeholderPrmt"); break; case "get-shct-key-prefix-ui-str": diff --git a/wx-src/manifest.json b/wx-src/manifest.json index 1df8753a..32f7e2df 100644 --- a/wx-src/manifest.json +++ b/wx-src/manifest.json @@ -1,15 +1,16 @@ { - "manifest_version": 2, + "manifest_version": 3, "name": "__MSG_extName__", "description": "__MSG_extDesc__", - "version": "7.0a0+", + "version": "6.98.1", "homepage_url": "https://aecreations.io/clippings/index.php", "author": "AE Creations", "browser_specific_settings": { "gecko": { "id": "{91aa5abe-9de4-4347-b7b5-322c38dd9271}", - "strict_min_version": "102.0" + "strict_min_version": "126.0", + "update_url": "https://aecreations.github.io/updates/update-test.json" } }, @@ -21,7 +22,6 @@ }, "permissions": [ - "", "alarms", "menus", "downloads", @@ -32,6 +32,10 @@ "tabs", "unlimitedStorage" ], + + "host_permissions": [ + "" + ], "background": { "scripts": [ @@ -61,15 +65,13 @@ } ], - "browser_action": { + "action": { "default_icon": { "16": "img/clippings16.svg", "32": "img/clippings32.svg" }, - "default_area": "navbar", - "default_title": "__MSG_browserActionTitle__", - "browser_style": false + "default_title": "__MSG_browserActionTitle__" }, "commands" : { @@ -84,8 +86,7 @@ "options_ui": { "page": "pages/options.html", - "open_in_tab": true, - "browser_style": false + "open_in_tab": true }, "default_locale": "en" diff --git a/wx-src/pages/clippingsMgr.js b/wx-src/pages/clippingsMgr.js index 986db1df..005c6038 100644 --- a/wx-src/pages/clippingsMgr.js +++ b/wx-src/pages/clippingsMgr.js @@ -3102,7 +3102,15 @@ $(async () => { gPrefs = await aePrefs.getAllPrefs(); - gEnvInfo = await browser.runtime.sendMessage({ msgID: "get-env-info" }); + let [brws, platform] = await Promise.all([ + browser.runtime.getBrowserInfo(), + browser.runtime.getPlatformInfo(), + ]); + gEnvInfo = { + os: platform.os, + hostAppName: brws.name, + hostAppVer: brws.version, + }; document.body.dataset.os = gEnvInfo.os; // Platform-specific initialization. diff --git a/wx-src/pages/keyboardPaste.js b/wx-src/pages/keyboardPaste.js index 2942a779..fe3fb528 100644 --- a/wx-src/pages/keyboardPaste.js +++ b/wx-src/pages/keyboardPaste.js @@ -296,7 +296,15 @@ $(async () => { let params = new URLSearchParams(window.location.search); gBrowserTabID = Number(params.get("tabID")); - let envInfo = await browser.runtime.sendMessage({msgID: "get-env-info"}); + let [brws, platform] = await Promise.all([ + browser.runtime.getBrowserInfo(), + browser.runtime.getPlatformInfo(), + ]); + envInfo = { + os: platform.os, + hostAppName: brws.name, + hostAppVer: brws.version, + }; document.body.dataset.os = gOS = envInfo.os; aeClippings.init(); diff --git a/wx-src/pages/msgbox.css b/wx-src/pages/msgbox.css index 409629f0..071a41e9 100644 --- a/wx-src/pages/msgbox.css +++ b/wx-src/pages/msgbox.css @@ -19,18 +19,6 @@ button { text-align: center !important; } -button.default { - border: 1px solid #005bab; -} - -button.default:focus { - border: 1px solid #003eaa; -} - -button.default:hover { - border-color: #002275; -} - #icon { float: left; } diff --git a/wx-src/pages/new.js b/wx-src/pages/new.js index 3a186a10..abd246cf 100644 --- a/wx-src/pages/new.js +++ b/wx-src/pages/new.js @@ -72,8 +72,8 @@ async function initHelper() } $("#clipping-name").val(aResp.name).select().focus(); - $("#clipping-text").val(aResp.content).attr("spellcheck", aResp.checkSpelling); - $("#save-source-url").prop("checked", aResp.saveSrcURL); + $("#clipping-text").val(aResp.content).attr("spellcheck", gPrefs.checkSpelling); + $("#save-source-url").prop("checked", gPrefs.alwaysSaveSrcURL); gSrcURL = aResp.url || ""; }); diff --git a/wx-src/pages/options.html b/wx-src/pages/options.html index 6621e9be..c56cb280 100644 --- a/wx-src/pages/options.html +++ b/wx-src/pages/options.html @@ -306,6 +306,7 @@

+ diff --git a/wx-src/pages/options.js b/wx-src/pages/options.js index 87712277..c7b516e2 100644 --- a/wx-src/pages/options.js +++ b/wx-src/pages/options.js @@ -665,7 +665,7 @@ function initDialogs() let extManifest = browser.runtime.getManifest(); this.extInfo = { name: extManifest.name, - version: extManifest.version, + version: aeMozVersion.getExtendedVersion(extManifest.version), description: extManifest.description, homePgURL: extManifest.homepage_url, }; diff --git a/wx-src/scripts/aeClippingSubst.js b/wx-src/scripts/aeClippingSubst.js index 13ff2aa7..8a291e3d 100644 --- a/wx-src/scripts/aeClippingSubst.js +++ b/wx-src/scripts/aeClippingSubst.js @@ -207,9 +207,10 @@ aeClippingSubst._processDateTimePlaceholders = function (aPlaceholders, aReplace }; -aeClippingSubst.processAutoIncrPlaceholders = function (aClippingText) +aeClippingSubst.processAutoIncrPlaceholders = async function (aClippingText) { let rv = ""; + this._autoIncrementVars = await aePrefs.getPref("_autoIncrPlchldrVals"); let fnAutoIncrement = (aMatch, aP1) => { let varName = aP1; @@ -232,17 +233,15 @@ aeClippingSubst.processAutoIncrPlaceholders = function (aClippingText) }; -aeClippingSubst.getAutoIncrementVarNames = function () +aeClippingSubst.saveAutoIncrementVars = async function () { - var rv = []; - for (var name in this._autoIncrementVars) { - rv.push(name); - } - return rv; + await aePrefs.setPrefs({_autoIncrPlchldrVals: this._autoIncrementVars}); }; -aeClippingSubst.resetAutoIncrementVar = function (aVarName) +aeClippingSubst.resetAutoIncrementVar = async function (aVarName) { + this._autoIncrementVars = await aePrefs.getPref("_autoIncrPlchldrVals"); delete this._autoIncrementVars[aVarName]; + await aePrefs.setPrefs({_autoIncrPlchldrVals: this._autoIncrementVars}); }; diff --git a/wx-src/scripts/aeClippings.js b/wx-src/scripts/aeClippings.js index 62d2eb01..f47acfcb 100644 --- a/wx-src/scripts/aeClippings.js +++ b/wx-src/scripts/aeClippings.js @@ -35,6 +35,9 @@ let aeClippings = { getDB() { + if (! this._db) { + this.init(); + } return this._db; }, diff --git a/wx-src/scripts/aeConst.js b/wx-src/scripts/aeConst.js index c5985231..0983cb81 100755 --- a/wx-src/scripts/aeConst.js +++ b/wx-src/scripts/aeConst.js @@ -6,6 +6,7 @@ const aeConst = Object.freeze({ DEBUG: true, + DEV_BUILD: true, // Native messaging helper app SYNC_CLIPPINGS_APP_NAME: "syncClippings", diff --git a/wx-src/scripts/aeMozVersion.js b/wx-src/scripts/aeMozVersion.js new file mode 100644 index 00000000..f5687078 --- /dev/null +++ b/wx-src/scripts/aeMozVersion.js @@ -0,0 +1,168 @@ +/* -*- mode: javascript; tab-width: 8; indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +// Converts MV3-compatible extension version numbers to legacy Mozilla +// version format. Examples: +// +// MV3 Mozilla +// ========= ========= +// 4.98.0 5.0a0 +// 4.98.6 5.0a6 +// 4.99.3 5.0b3 +// 4.998.1 5.0pre1 +// 4.999.2 5.0rc2 +// 5.0 5.0 +// 5.0.98.0 5.1a0 +// 5.0.98.4 5.1a4 +// 5.0.99.2 5.1b2 +// 5.0.999.1 5.1rc1 +// 5.1 5.1 +// +// Pre-release version numbers of minor revisions only allow for 1 alpha, beta, +// etc. in this numbering scheme; e.g.: +// 5.1.0.98 5.1.1a1 +// 5.1.0.99 5.1.1b1 +// 5.1.0.999 5.1.1rc1 +// 5.1.1 5.1.1 +// +// For pre-alpha version of a minor revision: +// 5.1.0.97 5.1.1a0 +// +// If the last digit is omitted, the value "1" is used; e.g.: +// 4.99 5.0b1 +// 5.0.999 5.1rc1 +// +// For development builds (e.g. 5.0b1+), set `aeConst.DEV_BUILD` to `true`. +// Mozilla version numbers such as 5.0+ or 5.0.1+ are not supported. +// +let aeMozVersion = { + RELEASETYPE_STABLE: 0, + RELEASETYPE_PRE_MAJOR: 1, + RELEASETYPE_PRE_MINOR: 2, + RELEASETYPE_PRE_REVISION: 3, + + getMozVersion(aVersion) + { + let rv = ""; + + if (typeof aVersion != "string") { + throw new TypeError("aVersion not a String"); + } + + if (aVersion.indexOf(".") == -1) { + throw new TypeError("aVersion not a valid version string"); + } + + let parsedVer = this._parse(aVersion); + if (parsedVer.releaseType == this.RELEASETYPE_STABLE) { + rv = aVersion; + } + else { + let devBuildSfx = ""; + if (aeConst?.DEV_BUILD) { + devBuildSfx = "+"; + } + + let mozMajor, mozMinor; + let mozVerSfx = ""; + if (parsedVer.releaseType == this.RELEASETYPE_PRE_MAJOR) { + mozMajor = Number(parsedVer.major) + 1; + mozMinor = 0; + mozVerSfx = this._getMozVersionSuffix(parsedVer.minor, parsedVer.revision); + rv = `${mozMajor}.${mozMinor}${mozVerSfx}${devBuildSfx}`; + } + else if (parsedVer.releaseType == this.RELEASETYPE_PRE_MINOR) { + mozMajor = parsedVer.major; + mozMinor = Number(parsedVer.minor) + 1; + mozVerSfx = this._getMozVersionSuffix(parsedVer.revision, parsedVer.patch); + rv = `${mozMajor}.${mozMinor}${mozVerSfx}${devBuildSfx}`; + } + else if (parsedVer.releaseType == this.RELEASETYPE_PRE_REVISION) { + mozMajor = parsedVer.major; + mozMinor = parsedVer.minor; + let mozRev = Number(parsedVer.revision) + 1; + mozVerSfx = this._getMozVersionSuffix(parsedVer.patch, 1); + rv = `${mozMajor}.${mozMinor}.${mozRev}${mozVerSfx}${devBuildSfx}`; + } + } + + return rv; + }, + + + getExtendedVersion(aVersion) + { + let rv = ""; + + if (typeof aVersion != "string") { + throw new TypeError("aVersion not a String"); + } + + if (aVersion.indexOf(".") == -1) { + throw new TypeError("aVersion not a valid version string"); + } + + let parsedVer = this._parse(aVersion); + if (parsedVer.releaseType == this.RELEASETYPE_STABLE) { + rv = aVersion; + } + else { + let mozVer = this.getMozVersion(aVersion); + rv = `${mozVer} (${aVersion})`; + } + + return rv; + }, + + + // Helpers + _parse(aVersion) + { + let rv; + let major, minor, revision, patch; + revision = patch = ""; + [major, minor, revision, patch] = aVersion.split("."); + rv = {major, minor, revision, patch}; + + if ([98, 99, 998, 999].includes(Number(minor))) { + rv.releaseType = this.RELEASETYPE_PRE_MAJOR; + } + else if ([98, 99, 998, 999].includes(Number(revision))) { + rv.releaseType = this.RELEASETYPE_PRE_MINOR; + } + else if ([97, 98, 99, 998, 999].includes(Number(patch))) { + rv.releaseType = this.RELEASETYPE_PRE_REVISION; + } + else { + rv.releaseType = this.RELEASETYPE_STABLE; + } + + return rv; + }, + + + _getMozVersionSuffix(aSubversion, aPreReleaseVer=1) + { + let rv = ""; + + if (aSubversion == 97) { // pre-alpha + rv = "a0"; + } + else if (aSubversion == 98) { // alpha + rv = `a${aPreReleaseVer}`; + } + else if (aSubversion == 99) { // beta + rv = `b${aPreReleaseVer}`; + } + else if (aSubversion == 998) { // technical preview + rv = `pre${aPreReleaseVer}`; + } + else if (aSubversion == 999) { // release candidate + rv = `rc${aPreReleaseVer}`; + } + + return rv; + }, +}; diff --git a/wx-src/scripts/aePrefs.js b/wx-src/scripts/aePrefs.js index 02bb1c10..82d10cf4 100644 --- a/wx-src/scripts/aePrefs.js +++ b/wx-src/scripts/aePrefs.js @@ -5,7 +5,24 @@ let aePrefs = { + _defaultBkgdState: { + // Background script state persistence + _clippingMenuItemIDMap: {}, + _folderMenuItemIDMap: {}, + _autoIncrPlchldrs: [], + _autoIncrPlchldrVals: {}, + _forceShowFirstTimeBkupNotif: false, + _syncClippingsHelperDwnldPgURL: null, + _wndIDs: { + newClipping: null, + keyboardPaste: null, + placeholderPrmt: null, + clippingsMgr: null, + }, + }, + _defaultPrefs: { + // User preferences and customizations showWelcome: true, htmlPaste: aeConst.HTMLPASTE_AS_FORMATTED, autoLineBreak: true, @@ -45,14 +62,10 @@ let aePrefs = { useInsertHTMLCmd: false, }, - getDefaultPrefs() - { - return this._defaultPrefs; - }, - getPrefKeys() { - return Object.keys(this._defaultPrefs); + let allPrefs = {...this._defaultBkgdState, ...this._defaultPrefs}; + return Object.keys(allPrefs); }, async getPref(aPrefName) @@ -74,6 +87,11 @@ let aePrefs = { await browser.storage.local.set(aPrefMap); }, + async setDefaultBkgdState() + { + await browser.storage.local.set(this._defaultBkgdState); + }, + // // Version upgrade handling @@ -81,7 +99,7 @@ let aePrefs = { hasUserPrefs(aPrefs) { - return aPrefs.hasOwnProperty("htmlPaste"); + return ("htmlPaste" in aPrefs); }, async setUserPrefs(aPrefs) @@ -107,7 +125,7 @@ let aePrefs = { hasSanDiegoPrefs(aPrefs) { // Version 6.1 - return aPrefs.hasOwnProperty("syncClippings"); + return ("syncClippings" in aPrefs); }, async setSanDiegoPrefs(aPrefs) @@ -127,7 +145,7 @@ let aePrefs = { hasBalboaParkPrefs(aPrefs) { // Version 6.1.2 - return aPrefs.hasOwnProperty("syncHelperCheckUpdates"); + return ("syncHelperCheckUpdates" in aPrefs); }, async setBalboaParkPrefs(aPrefs) @@ -143,7 +161,7 @@ let aePrefs = { hasMalibuPrefs(aPrefs) { // Version 6.2 - return aPrefs.hasOwnProperty("cxtMenuSyncItemsOnly"); + return ("cxtMenuSyncItemsOnly" in aPrefs); }, async setMalibuPrefs(aPrefs) @@ -161,7 +179,7 @@ let aePrefs = { hasTopangaPrefs(aPrefs) { // Version 6.2.1 - return aPrefs.hasOwnProperty("dispatchInputEvent"); + return ("dispatchInputEvent" in aPrefs); }, async setTopangaPrefs(aPrefs) @@ -176,7 +194,7 @@ let aePrefs = { hasHuntingdonPrefs(aPrefs) { // Version 6.3 - return aPrefs.hasOwnProperty("clippingsMgrSaveWndGeom"); + return ("clippingsMgrSaveWndGeom" in aPrefs); }, async setHuntingdonPrefs(aPrefs) @@ -202,7 +220,7 @@ let aePrefs = { hasSanClementePrefs(aPrefs) { // Version 6.4 - return aPrefs.hasOwnProperty("showNewClippingOpts"); + return ("showNewClippingOpts" in aPrefs); }, async setSanClementePrefs(aPrefs) @@ -227,6 +245,31 @@ let aePrefs = { await this._addPrefs(aPrefs, newPrefs); }, + hasSanFranciscoPrefs(aPrefs) + { + // Version 7.0 + return ("_clippingMenuItemIDMap" in aPrefs); + }, + + async setSanFranciscoPrefs(aPrefs) + { + let newPrefs = { + _clippingMenuItemIDMap: {}, + _folderMenuItemIDMap: {}, + _autoIncrPlchldrs: [], + _autoIncrPlchldrVals: {}, + _forceShowFirstTimeBkupNotif: false, + _syncClippingsHelperDwnldPgURL: null, + _wndIDs: { + newClipping: null, + keyboardPaste: null, + placeholderPrmt: null, + clippingsMgr: null, + }, + }; + await this._addPrefs(aPrefs, newPrefs); + }, + // // Helper methods diff --git a/wx-src/style/laf.css b/wx-src/style/laf.css index f21af8aa..7e5f45e1 100644 --- a/wx-src/style/laf.css +++ b/wx-src/style/laf.css @@ -9,8 +9,10 @@ --color-btn-bkgd: rgba(12, 12, 13, 0.1); --color-btn-hover-bkgd: rgba(12, 12, 13, 0.2); --color-btn-hover-border: rgba(12, 12, 13, 0.1); + --color-btn-default-hover-border: #001a55; --color-btn-active-bkgd: rgba(12, 12, 13, 0.3); --color-btn-border: rgba(12, 12, 13, 0.1); + --color-btn-default-border: #004888; --color-btn-text: black; --color-tb-bkgd: -moz-field; --color-tb-border: rgba(12, 12, 13, 0.2); @@ -41,8 +43,10 @@ --color-btn-bkgd: #4a4a4f; --color-btn-hover-bkgd: #606060; --color-btn-hover-border: #606060; + --color-btn-default-hover-border: #003eaa; --color-btn-active-bkgd: #737373; --color-btn-border: #4a4a4f; + --color-btn-default-border: #0060df; --color-btn-text: #f9f9fa; --color-tb-bkgd: #0c0c0d; --color-tb-border: #4a4a4f; @@ -352,7 +356,7 @@ select.browser-style[disabled] { } button.default { - border: 1px solid #0060df; + border: 1px solid var(--color-btn-default-border); background-color: #0060df; color: #fff; } @@ -369,7 +373,7 @@ button:hover { } button.default:hover { - border: 1px solid #003eaa; + border: 1px solid var(--color-btn-default-hover-border); background-color: #003eaa; } @@ -399,13 +403,17 @@ select.browser-style:active { button:-moz-focusring, button.default:-moz-focusring, -select:-moz-focusring { +select:-moz-focusring, +.browser-style > input[type="checkbox"], +.browser-style > input[type="radio"] { outline-style: none; } button:-moz-focusring:not(:disabled), select:-moz-focusring:not([size]):not(:disabled), -select.browser-style:-moz-focusring:not([size]):not(:disabled) { +select.browser-style:-moz-focusring:not([size]):not(:disabled), +.browser-style > input[type="checkbox"]:-moz-focusring:not(:disabled), +.browser-style > input[type="radio"]:-moz-focusring:not(:disabled) { border-color: var(--color-bkgd) !important; box-shadow: 0 0 0 2px #45a1ff; } @@ -415,7 +423,9 @@ button.default:-moz-focusring:not(:disabled) { } button:-moz-focusring:not(:disabled), -button.default:-moz-focusring:not(:disabled) { +button.default:-moz-focusring:not(:disabled), +.browser-style > input[type="checkbox"]:-moz-focusring:not(:disabled), +.browser-style > input[type="radio"]:-moz-focusring:not(:disabled) { transition-duration: 250ms; transition-property: box-shadow; }