stylus/edit/global-search.js
eight e3d3604afc
Refactor the entire storage system and the section editor (#518)
* Squashed commit of the following:

commit d84c4dc3fe
Author: eight <eight04@gmail.com>
Date:   Sun Oct 14 19:13:29 2018 +0800

    Fix: remove unused comment

commit 46027120ec
Author: eight <eight04@gmail.com>
Date:   Sun Oct 14 19:09:06 2018 +0800

    Add: handle styleUpdated message

commit f85d4de39b
Author: eight <eight04@gmail.com>
Date:   Sun Oct 14 18:59:29 2018 +0800

    Fix: handle styleAdded message in popup

commit 81f3e69574
Author: eight <eight04@gmail.com>
Date:   Sun Oct 14 18:50:54 2018 +0800

    Change: getStylesInfoByUrl -> getStylesByUrl

commit f9dc04558f
Author: eight <eight04@gmail.com>
Date:   Sun Oct 14 18:48:20 2018 +0800

    Fix: drop getStylesInfo

commit fea04d591f
Author: eight <eight04@gmail.com>
Date:   Sun Oct 14 18:39:28 2018 +0800

    Fix: remove unused ignoreChromeError

commit 2aff14e213
Author: eight <eight04@gmail.com>
Date:   Sun Oct 14 18:09:53 2018 +0800

    Fix: don't dup promisify in prefs

commit d4ddfcc713
Author: eight <eight04@gmail.com>
Date:   Sun Oct 14 17:56:16 2018 +0800

    Change: drop .last and .rotate

commit 85e70491e4
Author: eight <eight04@gmail.com>
Date:   Sun Oct 14 17:36:00 2018 +0800

    Fix: unused renderIndex

commit 7acb131642
Author: eight <eight04@gmail.com>
Date:   Sun Oct 14 17:32:49 2018 +0800

    Fix: update title on input

commit a39405ac4c
Author: eight <eight04@gmail.com>
Date:   Sun Oct 14 17:17:20 2018 +0800

    Fix: remove unused messages

commit 14c2fdbb58
Author: eight <eight04@gmail.com>
Date:   Sun Oct 14 16:36:12 2018 +0800

    Fix: dirty state for new added applies

commit fb1b49b8bb
Author: eight <eight04@gmail.com>
Date:   Sun Oct 14 16:27:17 2018 +0800

    Fix: minor

commit 2c2d849fa4
Author: eight <eight04@gmail.com>
Date:   Sun Oct 14 16:20:14 2018 +0800

    Fix: drop unused getCode

commit f133c3e67a
Author: eight <eight04@gmail.com>
Date:   Sun Oct 14 16:18:14 2018 +0800

    Fix: drop unused lastActive

commit 05a6208f5c
Author: eight <eight04@gmail.com>
Date:   Sun Oct 14 16:17:45 2018 +0800

    Fix: minor

commit 05a87ed00f
Author: eight <eight04@gmail.com>
Date:   Sun Oct 14 15:58:33 2018 +0800

    Fix: minor

commit 576f73f333
Author: eight <eight04@gmail.com>
Date:   Sun Oct 14 03:03:35 2018 +0800

    Fix: always register listeners

commit e93819deb4
Author: eight <eight04@gmail.com>
Date:   Sun Oct 14 02:58:49 2018 +0800

    Fix: unused statement

commit 39b11685b4
Author: eight <eight04@gmail.com>
Date:   Sun Oct 14 02:54:29 2018 +0800

    Fix: minor

commit 9dd3cd43c1
Author: eight <eight04@gmail.com>
Date:   Sun Oct 14 02:49:22 2018 +0800

    Fix: don't reorder options

commit 90aadfd728
Author: eight <eight04@gmail.com>
Date:   Sun Oct 14 02:43:52 2018 +0800

    Fix: drop __ERROR__

commit 838c21e3b3
Author: eight <eight04@gmail.com>
Date:   Sun Oct 14 02:36:20 2018 +0800

    Fix: use findStyle API

commit 93a4cdf595
Author: eight <eight04@gmail.com>
Date:   Sun Oct 14 02:34:05 2018 +0800

    Add: findStyle API

commit 8e75871b9b
Author: eight <eight04@gmail.com>
Date:   Sun Oct 14 02:19:01 2018 +0800

    Breaking: drop getStylesFallback

commit ad06551440
Author: eight <eight04@gmail.com>
Date:   Sun Oct 14 02:16:48 2018 +0800

    Fix: use dataurl to inject page script

commit cb5cbb4d10
Author: eight <eight04@gmail.com>
Date:   Sun Oct 14 01:39:50 2018 +0800

    Fix: various

commit 53efd78b89
Author: eight <eight04@gmail.com>
Date:   Sun Oct 14 01:12:57 2018 +0800

    Update doc

commit 7d005f3eaa
Author: eight <eight04@gmail.com>
Date:   Sun Oct 14 01:09:22 2018 +0800

    Change: kill style.reason

commit fc53bed3de
Author: eight <eight04@gmail.com>
Date:   Sun Oct 14 00:56:04 2018 +0800

    Fix: doo many indents

commit 14e321d258
Author: eight <eight04@gmail.com>
Date:   Sun Oct 14 00:40:23 2018 +0800

    Fix: don't update icon for popup and options

commit 01bdd529bc
Author: eight <eight04@gmail.com>
Date:   Sun Oct 14 00:39:17 2018 +0800

    Fix: updateCount

commit b9968830d3
Author: eight <eight04@gmail.com>
Date:   Sun Oct 14 00:38:49 2018 +0800

    Fix: don't send null value

commit ff3bf6f52d
Author: eight <eight04@gmail.com>
Date:   Sun Oct 14 00:03:34 2018 +0800

    Add: styleViaAPI updateCount

commit 39d21c3d29
Author: eight <eight04@gmail.com>
Date:   Sat Oct 13 23:57:45 2018 +0800

    Fix: broadcastError -> ignoreError

commit ecb622c93c
Author: eight <eight04@gmail.com>
Date:   Sat Oct 13 21:29:06 2018 +0800

    Fix: implement styleViaAPI

commit 7c3d49c005
Author: eight <eight04@gmail.com>
Date:   Sat Oct 13 17:50:28 2018 +0800

    Fix: ROOT may change in XML pages

commit 3fd8d937f3
Author: eight <eight04@gmail.com>
Date:   Sat Oct 13 16:49:43 2018 +0800

    Fix: various

commit 859afc8ee9
Author: eight <eight04@gmail.com>
Date:   Sat Oct 13 16:39:54 2018 +0800

    Enhance: don't cache enabled state

commit fbe77a8d15
Author: eight <eight04@gmail.com>
Date:   Sat Oct 13 16:15:07 2018 +0800

    Fix: various

commit a4fc3e9162
Author: eight <eight04@gmail.com>
Date:   Sat Oct 13 16:11:38 2018 +0800

    Fix: various

commit 7e0eddeb8f
Author: eight <eight04@gmail.com>
Date:   Sat Oct 13 15:58:31 2018 +0800

    Fix: various

commit 8b4ab47d89
Author: eight <eight04@gmail.com>
Date:   Sat Oct 13 15:20:10 2018 +0800

    Add: some type hint

commit 7d340d62dc
Author: eight <eight04@gmail.com>
Date:   Sat Oct 13 15:13:11 2018 +0800

    Change: drop storage.js, some functions are moved to sections-util

commit d286997d6a
Author: eight <eight04@gmail.com>
Date:   Sat Oct 13 15:12:00 2018 +0800

    Fix: minor

commit d60db9dbef
Author: eight <eight04@gmail.com>
Date:   Sat Oct 13 15:03:10 2018 +0800

    Fix: minor

commit 43afa31fa0
Author: eight <eight04@gmail.com>
Date:   Sat Oct 13 14:50:31 2018 +0800

    Fix: update tab icon on forward/backward

commit f08faea149
Author: eight <eight04@gmail.com>
Date:   Sat Oct 13 13:50:03 2018 +0800

    Fix: parallel import

commit 4d06435486
Author: eight <eight04@gmail.com>
Date:   Fri Oct 12 23:32:03 2018 +0800

    Add: importStyle API

commit c55675912e
Author: eight <eight04@gmail.com>
Date:   Fri Oct 12 23:14:46 2018 +0800

    Fix: refactor import-export

commit 86ea846a89
Author: eight <eight04@gmail.com>
Date:   Fri Oct 12 17:34:36 2018 +0800

    Fix: search db is broken

commit 831ca07c2d
Author: eight <eight04@gmail.com>
Date:   Fri Oct 12 17:29:35 2018 +0800

    fixup! Add: implement sloppy regexp indicator

commit e67b7f4f36
Author: eight <eight04@gmail.com>
Date:   Fri Oct 12 17:27:19 2018 +0800

    Add: implement sloppy regexp indicator

commit 36e13f88f0
Author: eight <eight04@gmail.com>
Date:   Fri Oct 12 16:59:23 2018 +0800

    Add: return excluded/sloppy state in getStylesInfoByUrl

commit f6ce78f55b
Author: eight <eight04@gmail.com>
Date:   Fri Oct 12 16:39:47 2018 +0800

    Fix: dead object

commit 5ae95a1ad9
Author: eight <eight04@gmail.com>
Date:   Fri Oct 12 16:27:54 2018 +0800

    Fix: don't reinit all editors on save

commit 1a5a206fe6
Author: eight <eight04@gmail.com>
Date:   Fri Oct 12 16:18:40 2018 +0800

    Refactor: pull out sections editor section

commit 8016346035
Author: eight <eight04@gmail.com>
Date:   Fri Oct 12 15:30:35 2018 +0800

    Fix: replaceStyle make style name undefined

commit fa080d1913
Author: eight <eight04@gmail.com>
Date:   Fri Oct 12 15:21:36 2018 +0800

    Fix: catch csp error

commit e0b064115d
Author: eight <eight04@gmail.com>
Date:   Fri Oct 12 15:03:00 2018 +0800

    Fix: use a simple eval to execute page scripts

commit 405b7f8f06
Author: eight <eight04@gmail.com>
Date:   Fri Oct 12 03:48:13 2018 +0800

    Fix: removed unused API

commit 1b2c88f926
Author: eight <eight04@gmail.com>
Date:   Fri Oct 12 03:46:51 2018 +0800

    Fix: no need to access db

commit a8131fc9c5
Author: eight <eight04@gmail.com>
Date:   Fri Oct 12 03:43:31 2018 +0800

    Fix: remove unused methods

commit 3ae0c4dd13
Author: eight <eight04@gmail.com>
Date:   Fri Oct 12 03:10:26 2018 +0800

    Enhance: allow matcher to return verbose info

commit 0ea7ada48f
Author: eight <eight04@gmail.com>
Date:   Fri Oct 12 02:02:14 2018 +0800

    Fix: content script may load before the background is ready

commit 04c2d6bbf6
Author: eight <eight04@gmail.com>
Date:   Fri Oct 12 01:49:52 2018 +0800

    Fix: throw receiving end doesn't exist message

commit f0c0bc4d6a
Author: eight <eight04@gmail.com>
Date:   Fri Oct 12 01:11:17 2018 +0800

    Fix: unwrap error

commit 4d42765d6c
Author: eight <eight04@gmail.com>
Date:   Thu Oct 11 23:55:16 2018 +0800

    fixup! Fix: match subdomain

commit 99626e4a48
Author: eight <eight04@gmail.com>
Date:   Thu Oct 11 23:54:58 2018 +0800

    Fix: match subdomain

commit a57b3b2716
Author: eight <eight04@gmail.com>
Date:   Thu Oct 11 23:39:11 2018 +0800

    Fix: firefox

commit 5cfea3933f
Author: eight <eight04@gmail.com>
Date:   Thu Oct 11 22:46:34 2018 +0800

    Add some comment to db.js

commit 25fd3a1c2b
Author: eight <eight04@gmail.com>
Date:   Thu Oct 11 22:14:56 2018 +0800

    Fix: remove unused prop

commit bdae1c3697
Author: eight <eight04@gmail.com>
Date:   Thu Oct 11 20:00:25 2018 +0800

    Change: simpler styleCodeEmpty

commit bd4a453f45
Merge: c1bf9f5 9058c06
Author: eight <eight04@gmail.com>
Date:   Thu Oct 11 19:49:37 2018 +0800

    Merge branch 'dev-usercss-meta' into dev-exclusions

commit c1bf9f57e9
Author: eight <eight04@gmail.com>
Date:   Thu Oct 11 19:29:17 2018 +0800

    Fix: minor

commit fd5eeb4b81
Author: eight <eight04@gmail.com>
Date:   Thu Oct 11 19:00:05 2018 +0800

    Add: refresh on view

commit 3e38810a49
Author: eight <eight04@gmail.com>
Date:   Thu Oct 11 18:13:24 2018 +0800

    Fix: make sure icons are refreshed at startup

commit c657d7e55c
Author: eight <eight04@gmail.com>
Date:   Thu Oct 11 17:32:27 2018 +0800

    Add: implement bug 461

commit 7ed39ab6ef
Author: eight <eight04@gmail.com>
Date:   Thu Oct 11 15:42:44 2018 +0800

    fixup! Add: icon-util

commit 30e494eda9
Author: eight <eight04@gmail.com>
Date:   Thu Oct 11 15:42:23 2018 +0800

    Add: icon-util

commit 510a886e14
Author: eight <eight04@gmail.com>
Date:   Thu Oct 11 03:21:38 2018 +0800

    Fix: exposeIframes

commit c7f81662c4
Author: eight <eight04@gmail.com>
Date:   Thu Oct 11 02:19:14 2018 +0800

    Fix: autoCloseBrackets is true by default

commit f3a103645d
Author: eight <eight04@gmail.com>
Date:   Thu Oct 11 02:11:14 2018 +0800

    Fix: various

commit d4436cde20
Author: eight <eight04@gmail.com>
Date:   Thu Oct 11 01:39:10 2018 +0800

    Add: implement exposeIframe

commit 43db875fd8
Author: eight <eight04@gmail.com>
Date:   Thu Oct 11 01:26:24 2018 +0800

    Kill more globals

commit dc491e9be3
Author: eight <eight04@gmail.com>
Date:   Thu Oct 11 01:22:13 2018 +0800

    Kill old storage, storage-dummy

commit ba64b95575
Author: eight <eight04@gmail.com>
Date:   Thu Oct 11 00:54:38 2018 +0800

    WIP: kill cachedStyles

commit 7eba890a21
Merge: d2b36a1 81e4823
Author: eight <eight04@gmail.com>
Date:   Wed Oct 10 23:15:14 2018 +0800

    Merge branch 'dev-private-prefs' into dev-exclusions

commit d2b36a168e
Author: eight <eight04@gmail.com>
Date:   Wed Oct 10 23:05:20 2018 +0800

    Kill hidden globals

commit 22d4767511
Author: eight <eight04@gmail.com>
Date:   Wed Oct 10 19:23:34 2018 +0800

    Fix: margin for deleted sections

commit 00687983f0
Author: eight <eight04@gmail.com>
Date:   Wed Oct 10 18:21:07 2018 +0800

    Fix: default value

commit ff6fd8cad3
Author: eight <eight04@gmail.com>
Date:   Wed Oct 10 18:02:51 2018 +0800

    Fix: default options

commit c23f315c52
Author: eight <eight04@gmail.com>
Date:   Wed Oct 10 17:40:07 2018 +0800

    Refactor: use CodeMirror.defineOption

commit 4419c5dc1e
Author: eight <eight04@gmail.com>
Date:   Wed Oct 10 16:32:39 2018 +0800

    Change: kill editors, styleId

commit 6494985b50
Author: eight <eight04@gmail.com>
Date:   Wed Oct 10 16:14:51 2018 +0800

    Fix: various

commit 37e1f43f75
Author: eight <eight04@gmail.com>
Date:   Wed Oct 10 15:04:03 2018 +0800

    Fix: minor

commit d26ce3238e
Author: eight <eight04@gmail.com>
Date:   Wed Oct 10 14:49:37 2018 +0800

    Add: codemirror-factory

commit 15a1f552f6
Author: eight <eight04@gmail.com>
Date:   Wed Oct 10 12:08:35 2018 +0800

    WIP: kill getSection

commit ba6159e067
Author: eight <eight04@gmail.com>
Date:   Wed Oct 10 02:43:09 2018 +0800

    WIP: edit page

commit fd9ab5d6e5
Author: eight <eight04@gmail.com>
Date:   Wed Oct 10 00:41:07 2018 +0800

    Fix: switch to editor

commit 06e22d0d18
Author: eight <eight04@gmail.com>
Date:   Tue Oct 9 23:38:29 2018 +0800

    Change: add sections-editor

commit 30e8662946
Author: eight <eight04@gmail.com>
Date:   Mon Oct 8 20:12:39 2018 +0800

    Add: preview error

commit 47b2b4fc49
Author: eight <eight04@gmail.com>
Date:   Mon Oct 8 18:38:01 2018 +0800

    Add: livePreview.show

commit 7b5e7c96d5
Author: eight <eight04@gmail.com>
Date:   Mon Oct 8 18:16:45 2018 +0800

    Hook up live preview

commit 15efafff3c
Author: eight <eight04@gmail.com>
Date:   Mon Oct 8 17:49:57 2018 +0800

    Add: live preview

commit a38558ef78
Author: eight <eight04@gmail.com>
Date:   Mon Oct 8 15:30:39 2018 +0800

    WIP: make notifyAllTabs a noop

commit 582e9078af
Author: eight <eight04@gmail.com>
Date:   Mon Oct 8 14:39:08 2018 +0800

    Fix: inject all scripts

commit f4651da8d8
Author: eight <eight04@gmail.com>
Date:   Sun Oct 7 23:41:46 2018 +0800

    Drop deleteStyle

commit 0489fb3b2f
Author: eight <eight04@gmail.com>
Date:   Sun Oct 7 23:33:51 2018 +0800

    Drop saveStyle

commit 02f471f077
Author: eight <eight04@gmail.com>
Date:   Sun Oct 7 23:28:41 2018 +0800

    Fix: usercss API

commit 057111b171
Author: eight <eight04@gmail.com>
Date:   Sun Oct 7 22:59:31 2018 +0800

    Update usercss API

commit 69cae02381
Author: eight <eight04@gmail.com>
Date:   Sun Oct 7 21:40:29 2018 +0800

    Drop getStyles

commit c5d41529d9
Author: eight <eight04@gmail.com>
Date:   Sun Oct 7 21:28:51 2018 +0800

    Minor fixes

commit 5b3b4e680f
Author: eight <eight04@gmail.com>
Date:   Sun Oct 7 21:20:39 2018 +0800

    Add: navigator-util

commit b5107b78a5
Author: eight <eight04@gmail.com>
Date:   Sun Oct 7 01:42:43 2018 +0800

    Add: broadcast messages with reasons

commit e7ef4948cd
Author: eight <eight04@gmail.com>
Date:   Sat Oct 6 18:10:47 2018 +0800

    Fix: observer is unavailable?

commit 1c635b5bc1
Author: eight <eight04@gmail.com>
Date:   Sat Oct 6 17:47:43 2018 +0800

    Drop requestStyles

commit 75f2561154
Author: eight <eight04@gmail.com>
Date:   Sat Oct 6 16:38:04 2018 +0800

    Fix: don't recreate element when style update in popup

commit 583ca31d97
Author: eight <eight04@gmail.com>
Date:   Sat Oct 6 15:40:07 2018 +0800

    fixup! Add: isCodeEmpty

commit 1cf6008514
Author: eight <eight04@gmail.com>
Date:   Sat Oct 6 15:33:18 2018 +0800

    Add: isCodeEmpty

commit 450cd60aeb
Author: eight <eight04@gmail.com>
Date:   Sat Oct 6 15:22:04 2018 +0800

    Fix: ignore comment block

commit 196b6aac63
Author: eight <eight04@gmail.com>
Date:   Sat Oct 6 15:16:00 2018 +0800

    Fix: the return value of getSectionsByUrl is changed

commit 3122d28c1a
Author: eight <eight04@gmail.com>
Date:   Sat Oct 6 15:14:05 2018 +0800

    Fix: always use promise in API call

commit e594b8ccb1
Author: eight <eight04@gmail.com>
Date:   Sat Oct 6 15:11:01 2018 +0800

    Cache enabled state

commit 1f18b13a92
Author: eight <eight04@gmail.com>
Date:   Sat Oct 6 13:48:46 2018 +0800

    Add: match global sections

commit fedf844ddd
Author: eight <eight04@gmail.com>
Date:   Sat Oct 6 13:45:37 2018 +0800

    Add: getStylesInfoByUrl

commit 095998f07c
Author: eight <eight04@gmail.com>
Date:   Sat Oct 6 13:27:58 2018 +0800

    Change: switch to msg.js

commit fa3127d988
Author: eight <eight04@gmail.com>
Date:   Sat Oct 6 13:02:45 2018 +0800

    Change: switch to msg.js

commit 05d582c726
Author: eight <eight04@gmail.com>
Date:   Sat Oct 6 11:43:42 2018 +0800

    Add: msg.sendBg

commit 171339f710
Author: eight <eight04@gmail.com>
Date:   Sat Oct 6 04:39:48 2018 +0800

    WIP: drop api.js

commit 3a618aca2a
Author: eight <eight04@gmail.com>
Date:   Sat Oct 6 03:19:51 2018 +0800

    WIP: use deepCopy

commit bb1cb58024
Author: eight <eight04@gmail.com>
Date:   Sat Oct 6 03:10:04 2018 +0800

    WIP: msg.js

commit 2472e91f57
Author: eight <eight04@gmail.com>
Date:   Fri Oct 5 21:28:19 2018 +0800

    WIP: emitChangesToTabs

commit 34497ebe16
Author: eight <eight04@gmail.com>
Date:   Fri Oct 5 18:47:52 2018 +0800

    WIP: switch to API

commit f1639cc33e
Author: eight <eight04@gmail.com>
Date:   Fri Oct 5 01:03:40 2018 +0800

    WIP: broadcastMessage

commit 81e4823f46
Author: eight <eight04@gmail.com>
Date:   Thu Oct 4 19:39:59 2018 +0800

    Debounce updateAllTabsIcon

commit dc5f3e209f
Author: eight <eight04@gmail.com>
Date:   Thu Oct 4 19:34:36 2018 +0800

    Fix: settings could be empty on the first install

commit 2328cf623a
Author: eight <eight04@gmail.com>
Date:   Thu Oct 4 19:34:22 2018 +0800

    Change: start-firefox -> start

commit 7be6a1cba9
Author: eight <eight04@gmail.com>
Date:   Thu Oct 4 19:24:35 2018 +0800

    Add: applications

commit 630725196f
Author: eight <eight04@gmail.com>
Date:   Thu Oct 4 19:22:44 2018 +0800

    fixup! Fix: update all icons when some prefs changed

commit 0d0e1b4dc0
Author: eight <eight04@gmail.com>
Date:   Thu Oct 4 19:20:36 2018 +0800

    Fix: update all icons when some prefs changed

commit 5c0288e9ba
Author: eight <eight04@gmail.com>
Date:   Thu Oct 4 19:20:11 2018 +0800

    fixup! Remove unused FIREFOX_NO_DOM_STORAGE

commit 56b737b65a
Author: eight <eight04@gmail.com>
Date:   Thu Oct 4 18:14:57 2018 +0800

    Remove unused FIREFOX_NO_DOM_STORAGE

commit 829a134ed1
Author: eight <eight04@gmail.com>
Date:   Thu Oct 4 18:10:53 2018 +0800

    Fix: this -> prefs

commit d35f92250e
Author: eight <eight04@gmail.com>
Date:   Thu Oct 4 18:08:19 2018 +0800

    Fixme: styleViaAPI

commit 8a6e8ac03a
Author: eight <eight04@gmail.com>
Date:   Thu Oct 4 18:05:41 2018 +0800

    Change: drop prefChanged, use prefs service

commit 10f9449144
Author: eight <eight04@gmail.com>
Date:   Thu Oct 4 17:46:45 2018 +0800

    Change: move setupLivePrefs to dom.js. Remove prefs.js dependencies

commit dd2b8ed091
Author: eight <eight04@gmail.com>
Date:   Thu Oct 4 17:18:38 2018 +0800

    Fix: type error

commit 3af310c341
Author: eight <eight04@gmail.com>
Date:   Thu Oct 4 17:09:26 2018 +0800

    Fix: open-manager has no default value

commit 874a2da33e
Author: eight <eight04@gmail.com>
Date:   Thu Oct 4 17:04:23 2018 +0800

    Enhance: make prefs use storage.sync

commit c01f93f62c
Author: eight <eight04@gmail.com>
Date:   Thu Oct 4 15:57:02 2018 +0800

    WIP

commit 6d32ffb76b
Author: eight <eight04@gmail.com>
Date:   Thu Oct 4 12:46:19 2018 +0800

    WIP

commit 0f148eac32
Author: eight <eight04@gmail.com>
Date:   Thu Oct 4 03:35:07 2018 +0800

    WIP

commit 282bdf7706
Author: eight <eight04@gmail.com>
Date:   Wed Oct 3 20:24:06 2018 +0800

    Fix: numbers are not compared correctly

commit 24b1eea8a4
Merge: 8a6011d 5cbe8a8
Author: eight <eight04@gmail.com>
Date:   Wed Oct 3 15:00:07 2018 +0800

    Merge branch 'master' of https://github.com/openstyles/stylus into dev-exclusions

commit 5cbe8a8d78
Author: eight <eight04@gmail.com>
Date:   Tue Oct 2 20:22:18 2018 +0800

    Add: fetch style object from DB directly in the editor (#507)

commit 9058c06c54
Author: eight <eight04@gmail.com>
Date:   Mon Oct 1 23:24:29 2018 +0800

    Fix: bad API

commit 1f2d116aae
Author: eight <eight04@gmail.com>
Date:   Mon Oct 1 23:14:56 2018 +0800

    Fix: use meta parser

commit 918e47b1ed
Author: eight <eight04@gmail.com>
Date:   Mon Oct 1 23:01:21 2018 +0800

    Fix: emit update event if no fatal errors

commit 81a7bb9ac9
Author: eight <eight04@gmail.com>
Date:   Mon Oct 1 22:56:25 2018 +0800

    Add: editorWorker.metalint

commit f47d57aea8
Author: eight <eight04@gmail.com>
Date:   Mon Oct 1 22:49:16 2018 +0800

    Change: use editorWorker.metalint

commit 5778d5c858
Author: eight <eight04@gmail.com>
Date:   Mon Oct 1 22:39:01 2018 +0800

    Change: editor-worker-body -> editor-worker

commit 268e1716b4
Author: eight <eight04@gmail.com>
Date:   Mon Oct 1 22:38:06 2018 +0800

    Change: switch to worker-util

commit cc2980b647
Author: eight <eight04@gmail.com>
Date:   Mon Oct 1 22:30:16 2018 +0800

    Drop: parserlib-loader

commit 08adcb60f2
Merge: 6909c73 2fd531e
Author: eight <eight04@gmail.com>
Date:   Mon Oct 1 22:29:39 2018 +0800

    Merge branch 'master' into dev-usercss-meta

commit e4135ce35d
Author: eight <eight04@gmail.com>
Date:   Fri Sep 28 11:57:34 2018 +0800

    Fix: remove unused function

commit 39a6d1909f
Author: eight <eight04@gmail.com>
Date:   Fri Sep 28 00:26:29 2018 +0800

    Fix: prefs doesn't work in FF's private windows. Add web-ext. Drop prefs.readOnlyValues

commit 6909c73c69
Author: eight <eight04@gmail.com>
Date:   Wed Sep 26 12:16:33 2018 +0800

    Fix: minor

commit 79833d8bba
Author: eight <eight04@gmail.com>
Date:   Wed Sep 26 11:40:04 2018 +0800

    Fix: a better way to draw list?

commit a849fd6dda
Author: eight <eight04@gmail.com>
Date:   Wed Sep 26 11:39:53 2018 +0800

    Fix: missing placeholders

commit d5ee31a080
Author: eight <eight04@gmail.com>
Date:   Wed Sep 26 11:37:50 2018 +0800

    Fix: a better way to draw character list?

commit 7b959af3e3
Author: eight <eight04@gmail.com>
Date:   Wed Sep 26 11:30:10 2018 +0800

    Update usercss-meta

commit fefa987c4d
Author: eight <eight04@gmail.com>
Date:   Wed Sep 26 10:37:28 2018 +0800

    Change: sections-equal -> sections-util

commit 2abbf670d8
Author: eight <eight04@gmail.com>
Date:   Wed Sep 26 10:37:14 2018 +0800

    Fix: check err.code

commit 1fe0586b29
Author: eight <eight04@gmail.com>
Date:   Wed Sep 26 10:33:02 2018 +0800

    Add: i18n error message

commit ab0ef239cf
Author: eight <eight04@gmail.com>
Date:   Wed Sep 26 09:34:57 2018 +0800

    Change: move styleCodeEmpty to sections-util, load colorConverter in background worker

commit d5ade807f0
Author: eight <eight04@gmail.com>
Date:   Wed Sep 26 09:27:30 2018 +0800

    Fix: display error message

commit 4f5337e51d
Author: eight <eight04@gmail.com>
Date:   Wed Sep 26 09:26:55 2018 +0800

    Fix: remove unused colorconverter

commit 29b8f51292
Author: eight <eight04@gmail.com>
Date:   Tue Sep 25 23:21:44 2018 +0800

    Fix: vars could be undefined

commit a7cfeb22e4
Author: eight <eight04@gmail.com>
Date:   Tue Sep 25 22:54:40 2018 +0800

    Fix: window is undefined

commit 9713c6a3be
Author: eight <eight04@gmail.com>
Date:   Tue Sep 25 21:56:38 2018 +0800

    Fix: throw an error for unparsable color

commit 3c30bc3eb0
Author: eight <eight04@gmail.com>
Date:   Tue Sep 25 21:55:55 2018 +0800

    Fix: try to get error message

commit 3d32b0428b
Author: eight <eight04@gmail.com>
Date:   Tue Sep 25 21:38:40 2018 +0800

    Fix: vars might be empty

commit 7d75dd8754
Author: eight <eight04@gmail.com>
Date:   Tue Sep 25 21:18:39 2018 +0800

    Add: meta-parser

commit a4df641b96
Author: eight <eight04@gmail.com>
Date:   Tue Sep 25 21:18:18 2018 +0800

    Enhance: set flag in parserlib so we don't need another loader

commit 8028a3529f
Author: eight <eight04@gmail.com>
Date:   Tue Sep 25 21:17:40 2018 +0800

    Include util, worker-util in background

commit ba5d6cc31a
Author: eight <eight04@gmail.com>
Date:   Tue Sep 25 21:16:59 2018 +0800

    Fix: use spread syntax in loadScript

commit b853be13f8
Author: eight <eight04@gmail.com>
Date:   Tue Sep 25 21:14:46 2018 +0800

    Enhance: swith to usercss-meta (in worker)

commit a3e7915199
Author: eight <eight04@gmail.com>
Date:   Tue Sep 25 21:11:54 2018 +0800

    Fix: use promise API

commit 5d07a8cd4e
Author: eight <eight04@gmail.com>
Date:   Tue Sep 25 21:11:09 2018 +0800

    Fix: buildMeta now returns a promise

commit a004bc3c7d
Author: eight <eight04@gmail.com>
Date:   Tue Sep 25 21:10:35 2018 +0800

    Move styleCodeEmpty to util

commit 41ac66a137
Author: eight <eight04@gmail.com>
Date:   Tue Sep 25 21:09:40 2018 +0800

    Add: background worker

commit ffb13bf1db
Author: eight <eight04@gmail.com>
Date:   Tue Sep 25 21:09:04 2018 +0800

    Enhance: move moz-parser/meta-parser/usercss compiler to worker

commit 42e97ef153
Author: eight <eight04@gmail.com>
Date:   Tue Sep 25 20:45:07 2018 +0800

    Fix: display error on install page

commit 64aa9fcf53
Author: eight <eight04@gmail.com>
Date:   Tue Sep 25 17:34:54 2018 +0800

    Add: background worker

commit b0e407e98f
Author: eight <eight04@gmail.com>
Date:   Tue Sep 25 14:52:35 2018 +0800

    Add: worker util

commit 7a24547e09
Author: eight <eight04@gmail.com>
Date:   Tue Sep 25 00:01:18 2018 +0800

    Add: usercss-meta

commit 8a6011de8c
Author: Rob Garrison <wowmotty@gmail.com>
Date:   Sun Jul 22 09:15:09 2018 -0500

    Attempt to update icon count

commit 4fcb1a88d7
Author: Rob Garrison <wowmotty@gmail.com>
Date:   Sun Jul 15 13:44:29 2018 -0500

    Fix empty exclusion storage error

commit bfe54ab4c4
Author: Rob Garrison <wowmotty@gmail.com>
Date:   Sun Jul 15 12:59:51 2018 -0500

    Add tab communication

commit 983a7bc219
Author: Rob Garrison <wowmotty@gmail.com>
Date:   Sun Jul 15 10:51:11 2018 -0500

    Fix escaped regex example

commit 3950482f34
Author: Rob Garrison <wowmotty@gmail.com>
Date:   Wed Apr 25 18:11:37 2018 -0500

    Fix undefined error

commit e94c7edb38
Author: Rob Garrison <wowmotty@gmail.com>
Date:   Wed Apr 25 17:09:45 2018 -0500

    Attempt to fix popup exclusion issues

commit 2b4a1a5635
Author: Rob Garrison <wowmotty@gmail.com>
Date:   Thu Apr 19 13:00:27 2018 -0500

    Modify input method

commit 9f75b69cd8
Author: Rob Garrison <wowmotty@gmail.com>
Date:   Wed Mar 7 11:54:05 2018 -0600

    Include iframe urls in exclusion popup

commit 68dfa0153c
Author: Rob Garrison <wowmotty@gmail.com>
Date:   Wed Jan 24 19:42:02 2018 -0600

    Add style exclusions. Closes #113

* Revert: exclusions

* Fix: pass eslint

* Fix: the style is injected twice

* Fix: don't load script async

* Fix: styleCodeEmpty returns true for empty string

* Fix: drop array selection

* Fix: the config dialog is broken

* Fix: popup doesn't use getStyle/getStylesByUrl correctly

* Fix: keep disabled state in setStyleContent

* Fix: allow live-preview to assign newest vars

* Fix: transition fix is broken because setStyleContent becomes async

* Fix: typo, TypeError in styleExists

* Fix: use new API

* Fix: pass linter

* Fix: LICENCE -> LICENSE

* Fix: remove unused distroy function
2018-11-07 14:09:29 +08:00

944 lines
27 KiB
JavaScript

/* global CodeMirror focusAccessibility colorMimicry editor
onDOMready $ $$ $create t debounce tryRegExp stringAsRegExp template */
'use strict';
onDOMready().then(() => {
//region Constants and state
const INCREMENTAL_SEARCH_DELAY = 0;
const ANNOTATE_SCROLLBAR_DELAY = 350;
const ANNOTATE_SCROLLBAR_OPTIONS = {maxMatches: 10e3};
const STORAGE_UPDATE_DELAY = 500;
const SCROLL_REVEAL_MIN_PX = 50;
const DIALOG_SELECTOR = '#search-replace-dialog';
const DIALOG_STYLE_SELECTOR = '#search-replace-dialog-style';
const TARGET_CLASS = 'search-target-editor';
const MATCH_CLASS = 'search-target-match';
const MATCH_TOKEN_NAME = 'searching';
const APPLIES_VALUE_CLASS = 'applies-value';
const RX_MAYBE_REGEXP = /^\s*\/(.+?)\/([simguy]*)\s*$/;
const state = {
// used for case-sensitive matching directly
find: '',
// used when /re/ is detected or for case-insensitive matching
rx: null,
// used by overlay and doSearchInApplies, equals to rx || stringAsRegExp(find)
rx2: null,
icase: true,
reverse: false,
lastFind: '',
numFound: 0,
numApplies: -1,
replace: '',
lastReplace: null,
cm: null,
input: null,
input2: null,
dialog: null,
tally: null,
originalFocus: null,
undoHistory: [],
searchInApplies: !document.documentElement.classList.contains('usercss'),
};
//endregion
//region Events
const ACTIONS = {
key: {
'Enter': event => {
switch (document.activeElement) {
case state.input:
if (state.dialog.dataset.type === 'find') {
const found = doSearch({canAdvance: false});
if (found) {
const target = $('.' + TARGET_CLASS);
const cm = target.CodeMirror;
(cm || target).focus();
if (cm) {
const pos = cm.state.search.searchPos;
cm.setSelection(pos.from, pos.to);
}
}
destroyDialog({restoreFocus: !found});
return;
}
// fallthrough
case state.input2:
doReplace();
return;
}
return !event.target.closest(focusAccessibility.ELEMENTS.join(','));
},
'Esc': () => {
destroyDialog({restoreFocus: true});
},
},
click: {
next: () => doSearch({reverse: false}),
prev: () => doSearch({reverse: true}),
close: () => destroyDialog({restoreFocus: true}),
replace: () => doReplace(),
replaceAll: () => doReplaceAll(),
undo: () => doUndo(),
clear() {
setInputValue(this._input, '');
},
case() {
state.icase = !state.icase;
state.lastFind = '';
toggleDataset(this, 'enabled', !state.icase);
doSearch({canAdvance: false});
}
},
};
const EVENTS = {
oninput() {
state.find = state.input.value;
debounce(doSearch, INCREMENTAL_SEARCH_DELAY, {canAdvance: false});
adjustTextareaSize(this);
if (!state.find) enableReplaceButtons(false);
},
onkeydown(event) {
const action = ACTIONS.key[CodeMirror.keyName(event)];
if (action && action(event) !== false) {
event.preventDefault();
}
},
onclick(event) {
const el = event.target.closest('[data-action]');
const action = el && ACTIONS.click[el.dataset.action];
if (action && action.call(el, event) !== false) {
event.preventDefault();
}
},
onfocusout() {
if (!state.dialog.contains(document.activeElement)) {
state.dialog.addEventListener('focusin', EVENTS.onfocusin);
state.dialog.removeEventListener('focusout', EVENTS.onfocusout);
}
},
onfocusin() {
state.dialog.addEventListener('focusout', EVENTS.onfocusout);
state.dialog.removeEventListener('focusin', EVENTS.onfocusin);
trimUndoHistory();
enableUndoButton(state.undoHistory.length);
if (state.find) doSearch({canAdvance: false});
}
};
const DIALOG_PROPS = {
dialog: {
onclick: EVENTS.onclick,
onkeydown: EVENTS.onkeydown,
},
input: {
oninput: EVENTS.oninput,
},
input2: {
oninput() {
state.replace = this.value;
adjustTextareaSize(this);
debounce(writeStorage, STORAGE_UPDATE_DELAY);
}
},
};
//endregion
//region Commands
const COMMANDS = {
find(cm, {reverse = false} = {}) {
state.reverse = reverse;
focusDialog('find', cm);
},
findNext: cm => doSearch({reverse: false, cm}),
findPrev: cm => doSearch({reverse: true, cm}),
replace(cm) {
state.reverse = false;
focusDialog('replace', cm);
}
};
COMMANDS.replaceAll = COMMANDS.replace;
//endregion
Object.assign(CodeMirror.commands, COMMANDS);
readStorage();
return;
//region Find
function initState({initReplace} = {}) {
const text = state.find;
const textChanged = text !== state.lastFind;
if (textChanged) {
state.numFound = 0;
state.numApplies = -1;
state.lastFind = text;
const match = text && text.match(RX_MAYBE_REGEXP);
const unicodeFlag = 'unicode' in RegExp.prototype ? 'u' : '';
const string2regexpFlags = (state.icase ? 'gi' : 'g') + unicodeFlag;
state.rx = match && tryRegExp(match[1], 'g' + match[2].replace(/[guy]/g, '') + unicodeFlag) ||
text && (state.icase || text.includes('\n')) && stringAsRegExp(text, string2regexpFlags);
state.rx2 = state.rx || text && stringAsRegExp(text, string2regexpFlags);
state.cursorOptions = {
caseFold: !state.rx && state.icase,
multiline: true,
};
debounce(writeStorage, STORAGE_UPDATE_DELAY);
}
if (initReplace && state.replace !== state.lastReplace) {
state.lastReplace = state.replace;
state.replaceValue = state.replace.replace(/(\\r)?\\n/g, '\n').replace(/\\t/g, '\t');
state.replaceHasRefs = /\$[$&`'\d]/.test(state.replaceValue);
}
const cmFocused = document.activeElement && document.activeElement.closest('.CodeMirror');
state.activeAppliesTo = $(`.${APPLIES_VALUE_CLASS}:focus, .${APPLIES_VALUE_CLASS}.${TARGET_CLASS}`);
state.cmStart = editor.closestVisible(
cmFocused && document.activeElement ||
state.activeAppliesTo ||
state.cm);
const cmExtra = $('body > :not(#sections) .CodeMirror');
state.editors = cmExtra ? [cmExtra.CodeMirror] : editor.getEditors();
}
function doSearch({
reverse = state.reverse,
canAdvance = true,
inApplies = true,
cm,
} = {}) {
if (cm) setActiveEditor(cm);
state.reverse = reverse;
if (!state.find && !dialogShown()) {
focusDialog('find', state.cm);
return;
}
initState();
const {cmStart} = state;
const {index, found, foundInCode} = state.find && doSearchInEditors({cmStart, canAdvance, inApplies}) || {};
if (!foundInCode) clearMarker();
if (!found) makeTargetVisible(null);
const radiateFrom = foundInCode ? index : state.editors.indexOf(cmStart);
setupOverlay(radiateArray(state.editors, radiateFrom));
enableReplaceButtons(foundInCode);
if (state.find) {
const firstSuccessfulSearch = foundInCode && !state.numFound;
debounce(showTally, 0, firstSuccessfulSearch ? 1 : undefined);
} else {
showTally(0, 0);
}
return found;
}
function doSearchInEditors({cmStart, canAdvance, inApplies}) {
const query = state.rx || state.find;
const reverse = state.reverse;
const BOF = {line: 0, ch: 0};
const EOF = getEOF(cmStart);
const start = state.editors.indexOf(cmStart);
const total = state.editors.length;
let i = 0;
let wrapAround = 0;
let pos, index, cm;
if (inApplies && state.activeAppliesTo) {
if (doSearchInApplies(state.cmStart, canAdvance)) {
return {found: true};
}
if (reverse) pos = EOF; else i++;
} else {
pos = getContinuationPos({cm: cmStart, reverse: !canAdvance || reverse});
wrapAround = !reverse ?
CodeMirror.cmpPos(pos, BOF) > 0 :
CodeMirror.cmpPos(pos, EOF) < 0;
}
for (; i < total + wrapAround; i++) {
index = (start + i * (reverse ? -1 : 1) + total) % total;
cm = state.editors[index];
if (i) {
pos = !reverse ? BOF : {line: cm.doc.size, ch: 0};
}
const cursor = cm.getSearchCursor(query, pos, state.cursorOptions);
if (cursor.find(reverse)) {
makeMatchVisible(cm, cursor);
return {found: true, foundInCode: true, index};
}
const cmForNextApplies = !reverse ? cm : state.editors[index ? index - 1 : total - 1];
if (inApplies && doSearchInApplies(cmForNextApplies)) {
return {found: true};
}
}
}
function doSearchInApplies(cm, canAdvance) {
if (!state.searchInApplies) return;
const inputs = editor.getSearchableInputs(cm);
if (state.reverse) inputs.reverse();
inputs.splice(0, inputs.indexOf(state.activeAppliesTo));
for (const input of inputs) {
const value = input.value;
if (input === state.activeAppliesTo) {
state.rx2.lastIndex = input.selectionStart + canAdvance;
} else {
state.rx2.lastIndex = 0;
}
const match = state.rx2.exec(value);
if (!match) {
continue;
}
const end = match.index + match[0].length;
// scroll selected part into view in long inputs,
// works only outside of current event handlers chain, hence timeout=0
setTimeout(() => {
input.setSelectionRange(end, end);
input.setSelectionRange(match.index, end);
});
const canFocus = !state.dialog || !state.dialog.contains(document.activeElement);
makeTargetVisible(!canFocus && input);
editor.scrollToEditor(cm);
if (canFocus) input.focus();
state.cm = cm;
clearMarker();
return true;
}
}
//endregion
//region Replace
function doReplace() {
initState({initReplace: true});
const cm = state.cmStart;
const generation = cm.changeGeneration();
const pos = getContinuationPos({cm, reverse: true});
const cursor = doReplaceInEditor({cm, pos});
if (!cursor) {
return;
}
if (cursor.findNext()) {
clearMarker();
makeMatchVisible(cm, cursor);
} else {
doSearchInEditors({cmStart: getNextEditor(cm)});
}
getStateSafe(cm).unclosedOp = false;
if (cm.curOp) cm.endOperation();
if (cursor) {
state.undoHistory.push([[cm, generation]]);
enableUndoButton(true);
}
}
function doReplaceAll() {
initState({initReplace: true});
clearMarker();
const generations = new Map(state.editors.map(cm => [cm, cm.changeGeneration()]));
const found = state.editors.filter(cm => doReplaceInEditor({cm, all: true}));
if (found.length) {
state.lastFind = null;
state.undoHistory.push(found.map(cm => [cm, generations.get(cm)]));
enableUndoButton(true);
doSearch({canAdvance: false});
}
}
function doReplaceInEditor({cm, pos, all = false}) {
const cursor = cm.getSearchCursor(state.rx || state.find, pos, state.cursorOptions);
const replace = state.replaceValue;
let found;
cursor.find();
while (cursor.atOccurrence) {
found = true;
if (!cm.curOp) {
cm.startOperation();
getStateSafe(cm).unclosedOp = true;
}
if (state.rx) {
const text = cm.getRange(cursor.pos.from, cursor.pos.to);
cursor.replace(state.replaceHasRefs ? text.replace(state.rx, replace) : replace);
} else {
cursor.replace(replace);
}
if (!all) {
makeMatchVisible(cm, cursor);
return cursor;
}
cursor.findNext();
}
if (all) {
getStateSafe(cm).searchPos = null;
}
return found;
}
function doUndo() {
let undoneSome;
saveWindowScrollPos();
for (const [cm, generation] of state.undoHistory.pop() || []) {
if (document.body.contains(cm.display.wrapper) && !cm.isClean(generation)) {
cm.undo();
cm.getAllMarks().forEach(marker =>
marker !== state.marker &&
marker.className === MATCH_CLASS &&
marker.clear());
undoneSome = true;
}
}
enableUndoButton(state.undoHistory.length);
if (state.undoHistory.length) {
focusUndoButton();
} else {
state.input.focus();
}
if (undoneSome) {
state.lastFind = null;
restoreWindowScrollPos();
doSearch({
reverse: false,
canAdvance: false,
inApplies: false,
});
}
}
//endregion
//region Overlay
function setupOverlay(queue, debounced) {
if (!queue.length) {
return;
}
if (queue.length > 1 || !debounced) {
debounce(setupOverlay, 0, queue, true);
if (!debounced) {
return;
}
}
let canContinue = true;
while (queue.length && canContinue) {
const cm = queue.shift();
if (!document.body.contains(cm.display.wrapper)) {
continue;
}
const cmState = getStateSafe(cm);
const query = state.rx2;
if ((cmState.overlay || {}).query === query) {
if (cmState.unclosedOp && cm.curOp) cm.endOperation();
cmState.unclosedOp = false;
continue;
}
if (cmState.overlay) {
if (!cm.curOp) cm.startOperation();
cm.removeOverlay(cmState.overlay);
cmState.overlay = null;
canContinue = false;
}
const hasMatches = query && cm.getSearchCursor(query, null, state.cursorOptions).find();
if (hasMatches) {
if (!cm.curOp) cm.startOperation();
cmState.overlay = {
query,
token: tokenize,
numFound: 0,
tallyShownTime: 0,
};
cm.addOverlay(cmState.overlay);
canContinue = false;
}
if (cmState.annotate) {
if (!cm.curOp) cm.startOperation();
cmState.annotate.clear();
cmState.annotate = null;
canContinue = false;
}
if (cmState.annotateTimer) {
clearTimeout(cmState.annotateTimer);
cmState.annotateTimer = 0;
}
if (hasMatches) {
cmState.annotateTimer = setTimeout(annotateScrollbar, ANNOTATE_SCROLLBAR_DELAY,
cm, query, state.icase);
}
cmState.unclosedOp = false;
if (cm.curOp) cm.endOperation();
}
if (!queue.length) debounce.unregister(setupOverlay);
}
function tokenize(stream) {
this.query.lastIndex = stream.pos;
const match = this.query.exec(stream.string);
if (match && match.index === stream.pos) {
this.numFound++;
const t = performance.now();
if (t - this.tallyShownTime > 10) {
this.tallyShownTime = t;
debounce(showTally);
}
stream.pos += match[0].length || 1;
return MATCH_TOKEN_NAME;
} else if (match) {
stream.pos = match.index;
} else {
stream.skipToEnd();
}
}
function annotateScrollbar(cm, query, icase) {
getStateSafe(cm).annotate = cm.showMatchesOnScrollbar(query, icase, ANNOTATE_SCROLLBAR_OPTIONS);
debounce(showTally);
}
//endregion
//region Dialog
function focusDialog(type, cm) {
setActiveEditor(cm);
const dialogFocused = state.dialog && state.dialog.contains(document.activeElement);
let sel = dialogFocused ? '' : getSelection().toString() || cm && cm.getSelection();
sel = !sel.includes('\n') && !sel.includes('\r') && sel;
if (sel) state.find = sel;
if (!dialogShown(type)) {
destroyDialog();
createDialog(type);
} else if (sel) {
setInputValue(state.input, sel);
}
state.input.focus();
state.input.select();
if (state.find) {
doSearch({canAdvance: false});
}
}
function dialogShown(type) {
return document.body.contains(state.input) &&
(!type || state.dialog.dataset.type === type);
}
function createDialog(type) {
state.originalFocus = document.activeElement;
const dialog = state.dialog = template.searchReplaceDialog.cloneNode(true);
Object.assign(dialog, DIALOG_PROPS.dialog);
dialog.addEventListener('focusout', EVENTS.onfocusout);
dialog.dataset.type = type;
dialog.style.pointerEvents = 'auto';
const content = $('[data-type="content"]', dialog);
content.parentNode.replaceChild(template[type].cloneNode(true), content);
createInput(0, 'input', state.find);
createInput(1, 'input2', state.replace);
toggleDataset($('[data-action="case"]', dialog), 'enabled', !state.icase);
state.tally = $('[data-type="tally"]', dialog);
const colors = {
body: colorMimicry.get(document.body, {bg: 'backgroundColor'}),
input: colorMimicry.get($('input:not(:disabled)'), {bg: 'backgroundColor'}),
icon: colorMimicry.get($$('svg.info')[1], {fill: 'fill'}),
};
document.documentElement.appendChild(
$(DIALOG_STYLE_SELECTOR) ||
$create('style' + DIALOG_STYLE_SELECTOR)
).textContent = `
#search-replace-dialog {
background-color: ${colors.body.bg};
}
#search-replace-dialog textarea {
color: ${colors.body.fore};
background-color: ${colors.input.bg};
}
#search-replace-dialog svg {
fill: ${colors.icon.fill};
}
#search-replace-dialog [data-action="case"] {
color: ${colors.icon.fill};
}
#search-replace-dialog[data-type="replace"] button:hover svg,
#search-replace-dialog svg:hover {
fill: inherit;
}
#search-replace-dialog [data-action="case"]:hover {
color: inherit;
}
#search-replace-dialog [data-action="clear"] {
background-color: ${colors.input.bg.replace(/[^,]+$/, '') + '.75)'};
}
`;
document.body.appendChild(dialog);
dispatchEvent(new Event('showHotkeyInTooltip'));
adjustTextareaSize(state.input);
if (type === 'replace') {
adjustTextareaSize(state.input2);
enableReplaceButtons(state.find !== '');
enableUndoButton(state.undoHistory.length);
}
return dialog;
}
function createInput(index, name, value) {
const input = state[name] = $$('textarea', state.dialog)[index];
if (!input) {
return;
}
input.value = value;
Object.assign(input, DIALOG_PROPS[name]);
input.parentElement.appendChild(template.clearSearch.cloneNode(true));
$('[data-action]', input.parentElement)._input = input;
}
function destroyDialog({restoreFocus = false} = {}) {
state.input = null;
$.remove(DIALOG_SELECTOR);
debounce.unregister(doSearch);
makeTargetVisible(null);
if (restoreFocus) {
setTimeout(focusNoScroll, 0, state.originalFocus);
} else {
saveWindowScrollPos();
restoreWindowScrollPos({immediately: false});
}
}
function adjustTextareaSize(el) {
const oldWidth = parseFloat(el.style.width) || el.clientWidth;
const widthHistory = el._widthHistory = el._widthHistory || new Map();
const knownWidth = widthHistory.get(el.value);
let newWidth;
if (knownWidth) {
newWidth = knownWidth;
} else {
const hasVerticalScrollbar = el.scrollHeight > el.clientHeight;
newWidth = el.scrollWidth + (hasVerticalScrollbar ? el.scrollWidth - el.clientWidth : 0);
newWidth += newWidth > oldWidth ? 50 : 0;
widthHistory.set(el.value, newWidth);
}
if (newWidth !== oldWidth) {
const dialogRightOffset = parseFloat(getComputedStyle(state.dialog).right);
const dialogRight = state.dialog.getBoundingClientRect().right;
const textRight = (state.input2 || state.input).getBoundingClientRect().right;
newWidth = Math.min(newWidth,
(window.innerWidth - dialogRightOffset - (dialogRight - textRight)) / (state.input2 ? 2 : 1) - 20);
el.style.width = newWidth + 'px';
}
const numLines = el.value.split('\n').length;
if (numLines !== parseInt(el.rows)) {
el.rows = numLines;
}
el.style.overflowX = el.scrollWidth > el.clientWidth ? '' : 'hidden';
}
function enableReplaceButtons(enabled) {
if (state.dialog && state.dialog.dataset.type === 'replace') {
for (const el of $$('[data-action^="replace"]', state.dialog)) {
el.disabled = !enabled;
}
}
}
function enableUndoButton(enabled) {
if (state.dialog && state.dialog.dataset.type === 'replace') {
for (const el of $$('[data-action="undo"]', state.dialog)) {
el.disabled = !enabled;
}
}
}
function focusUndoButton() {
for (const btn of $$('[data-action="undo"]', state.dialog)) {
if (getComputedStyle(btn).display !== 'none') {
btn.focus();
break;
}
}
}
//endregion
//region Utility
function getStateSafe(cm) {
return cm.state.search || (cm.state.search = {});
}
// determines search start position:
// the cursor if it was moved or the last match
function getContinuationPos({cm, reverse}) {
const cmSearchState = getStateSafe(cm);
const posType = reverse ? 'from' : 'to';
const searchPos = (cmSearchState.searchPos || {})[posType];
const cursorPos = cm.getCursor(posType);
const preferCursor = !searchPos || CodeMirror.cmpPos(cursorPos, cmSearchState.cursorPos[posType]);
return preferCursor ? cursorPos : searchPos;
}
function getEOF(cm) {
const line = cm.doc.size - 1;
return {line, ch: cm.getLine(line).length};
}
function getNextEditor(cm, step = 1) {
const editors = state.editors;
return editors[(editors.indexOf(cm) + step + editors.length) % editors.length];
}
// sets the editor to start the search in
// e.g. when the user switched to another editor and invoked a search command
function setActiveEditor(cm) {
if (cm.display.wrapper.contains(document.activeElement)) {
state.cm = cm;
state.originalFocus = cm;
}
}
// adds a class on the editor that contains the active match
// instead of focusing it (in order to keep the minidialog focused)
function makeTargetVisible(element) {
const old = $('.' + TARGET_CLASS);
if (old !== element) {
if (old) old.classList.remove(TARGET_CLASS);
if (element) element.classList.add(TARGET_CLASS);
}
}
// scrolls the editor to reveal the match
function makeMatchVisible(cm, searchCursor) {
const canFocus = !state.dialog || !state.dialog.contains(document.activeElement);
state.cm = cm;
// scroll within the editor
Object.assign(getStateSafe(cm), {
cursorPos: {
from: cm.getCursor('from'),
to: cm.getCursor('to'),
},
searchPos: searchCursor.pos,
unclosedOp: !cm.curOp,
});
if (!cm.curOp) cm.startOperation();
if (canFocus) cm.setSelection(searchCursor.pos.from, searchCursor.pos.to);
cm.scrollIntoView(searchCursor.pos, SCROLL_REVEAL_MIN_PX);
// scroll to the editor itself
editor.scrollToEditor(cm);
// focus or expose as the current search target
clearMarker();
if (canFocus) {
cm.focus();
makeTargetVisible(null);
} else {
makeTargetVisible(cm.display.wrapper);
// mark the match
const pos = searchCursor.pos;
state.marker = cm.state.search.marker = cm.markText(pos.from, pos.to, {
className: MATCH_CLASS,
clearOnEnter: true,
});
}
}
function clearMarker() {
if (state.marker) state.marker.clear();
}
function showTally(num, numApplies) {
if (!state.tally) return;
if (num === undefined) {
num = 0;
for (const cm of state.editors) {
const {annotate, overlay} = getStateSafe(cm);
num +=
((annotate || {}).matches || []).length ||
(overlay || {}).numFound ||
0;
}
state.numFound = num;
}
if (numApplies === undefined && state.searchInApplies && state.numApplies < 0) {
numApplies = 0;
const elements = state.find ? document.getElementsByClassName(APPLIES_VALUE_CLASS) : [];
for (const el of elements) {
const value = el.value;
if (state.rx) {
state.rx.lastIndex = 0;
while (state.rx.exec(value)) numApplies++;
} else {
let i = -1;
while ((i = value.indexOf(state.find, i + 1)) >= 0) numApplies++;
}
}
state.numApplies = numApplies;
} else {
numApplies = state.numApplies;
}
const newText = num + (numApplies > 0 ? '+' + numApplies : '');
if (state.tally.textContent !== newText) {
state.tally.textContent = newText;
const newTitle = t('searchNumberOfResults' + (numApplies ? '2' : ''));
if (state.tally.title !== newTitle) state.tally.title = newTitle;
}
}
function trimUndoHistory() {
const history = state.undoHistory;
for (let last; (last = history[history.length - 1]);) {
const undoables = last.filter(([cm, generation]) =>
document.body.contains(cm.display.wrapper) && !cm.isClean(generation));
if (undoables.length) {
history[history.length - 1] = undoables;
break;
}
history.length--;
}
}
function focusNoScroll(el) {
if (el) {
saveWindowScrollPos();
el.focus({preventScroll: true});
restoreWindowScrollPos({immediately: false});
}
}
function toggleDataset(el, prop, state) {
if (state) {
el.dataset[prop] = '';
} else {
delete el.dataset[prop];
}
}
function saveWindowScrollPos() {
state.scrollX = window.scrollX;
state.scrollY = window.scrollY;
}
function restoreWindowScrollPos({immediately = true} = {}) {
if (!immediately) {
// run in the next microtask cycle
Promise.resolve().then(restoreWindowScrollPos);
return;
}
if (window.scrollX !== state.scrollX || window.scrollY !== state.scrollY) {
window.scrollTo(state.scrollX, state.scrollY);
}
}
// produces [i, i+1, i-1, i+2, i-2, i+3, i-3, ...]
function radiateArray(arr, focalIndex) {
const result = [arr[focalIndex]];
const len = arr.length;
for (let i = 1; i < len; i++) {
if (focalIndex + i < len) {
result.push(arr[focalIndex + i]);
}
if (focalIndex - i >= 0) {
result.push(arr[focalIndex - i]);
}
}
return result;
}
function readStorage() {
chrome.storage.local.get('editor', ({editor = {}}) => {
state.find = editor.find || '';
state.replace = editor.replace || '';
state.icase = editor.icase || state.icase;
});
}
function writeStorage() {
chrome.storage.local.get('editor', ({editor}) =>
chrome.storage.local.set({
editor: Object.assign(editor || {}, {
find: state.find,
replace: state.replace,
icase: state.icase,
})
}));
}
function setInputValue(input, value) {
input.focus();
input.select();
// using execCommand to add to the input's undo history
document.execCommand(value ? 'insertText' : 'delete', false, value);
// some versions of Firefox ignore execCommand
if (input.value !== value) {
input.value = value;
input.dispatchEvent(new Event('input', {bubbles: true}));
}
}
//endregion
});